1. bind
函数与描述符
bind
函数用于将一个套接字(socket)与特定的地址和端口绑定,这样客户端就可以通过该地址和端口找到并连接到服务器。其函数原型如下:
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 参数说明:
sockfd
:是一个套接字描述符,它是通过socket
函数创建的。socket
函数会返回一个非负整数,作为这个新创建套接字的标识。addr
:指向一个struct sockaddr
类型的结构体,该结构体包含了要绑定的地址和端口信息。addrlen
:表示addr
所指向结构体的长度。
2. accept
函数与描述符
accept
函数用于从监听队列中取出一个客户端的连接请求,并创建一个新的套接字来与该客户端进行通信。其函数原型如下:
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- 参数说明:
sockfd
:同样是一个套接字描述符,它是用于监听客户端连接请求的套接字描述符。addr
:指向一个struct sockaddr
类型的结构体,用于存储客户端的地址信息。addrlen
:指向一个socklen_t
类型的变量,传入时表示addr
所指向结构体的长度,返回时表示实际存储的客户端地址信息的长度。
3. 二者描述符相同的原因
在服务器端的网络编程流程中,一般会按以下步骤进行操作:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 8888
#define BACKLOG 5
int main() {
int listen_fd, conn_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len;
// 创建套接字
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd == -1) {
perror("socket creation failed");
return -1;
}
// 准备服务器地址信息
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
// 绑定套接字到指定地址和端口
if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("bind failed");
close(listen_fd);
return -1;
}
// 开始监听客户端连接请求
if (listen(listen_fd, BACKLOG) == -1) {
perror("listen failed");
close(listen_fd);
return -1;
}
// 接受客户端连接
client_addr_len = sizeof(client_addr);
conn_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &client_addr_len);
if (conn_fd == -1) {
perror("accept failed");
close(listen_fd);
return -1;
}
// 后续可以使用 conn_fd 与客户端进行通信
// ...
// 关闭套接字
close(conn_fd);
close(listen_fd);
return 0;
}
- 首先,使用
socket
函数创建一个套接字,返回一个套接字描述符listen_fd
。 - 然后,使用
bind
函数将这个套接字描述符listen_fd
与服务器的地址和端口绑定,这样服务器就可以在该地址和端口上监听客户端的连接请求。 - 接着,使用
listen
函数将该套接字设置为监听状态,开始监听客户端的连接请求。 - 最后,使用
accept
函数从监听队列中取出一个客户端的连接请求。这里accept
函数的第一个参数仍然是listen_fd
,因为它需要知道从哪个监听套接字的队列中取出连接请求。当accept
函数成功返回时,会创建一个新的套接字描述符conn_fd
,用于与该客户端进行实际的数据通信。
bind
函数的第一个参数描述符和 accept
函数的第一个参数描述符通常是一样的,它们都指向用于监听客户端连接请求的套接字。而 accept
函数返回的新套接字描述符则用于与具体的客户端进行数据交互。
这俩个函数返回值的区别
bind
函数和accept
函数返回值的区别主要体现在含义、取值及错误处理等方面,以下是具体介绍:
返回值含义
bind
函数:用于将套接字与特定的网络地址和端口号绑定。其返回值表示绑定操作是否成功。accept
函数:用于从服务器的连接请求队列中接受一个客户端连接,并创建一个新的套接字来与客户端进行通信。其返回值表示接受连接操作的结果,若成功则返回一个新的套接字描述符,用于与该客户端进行后续的数据交互。
取值情况
bind
函数:成功时返回 0,表示套接字与指定地址和端口绑定成功,服务器可以开始在该地址和端口上监听连接请求。失败时返回 - 1,同时会设置errno
变量来表示具体的错误原因,如EACCES
(权限不足,无法绑定到指定端口)、EADDRINUSE
(地址已被使用)等。accept
函数:成功时返回一个非负整数,即新的套接字描述符,这个新的描述符用于与接受的客户端进行通信,后续可以使用这个描述符进行read
、write
等操作来与客户端交换数据。失败时返回 - 1,同时errno
会被设置为相应的错误码,如EAGAIN
或EWOULDBLOCK
(在非阻塞模式下,没有可用的连接且没有设置超时)、ECONNABORTED
(连接被对方中止)等。
错误处理影响
bind
函数:如果bind
失败,通常意味着服务器无法在指定的地址和端口上进行监听,后续的listen
和accept
操作也就无法正常进行,服务器需要根据错误原因进行相应的处理,如更换端口、检查权限等,然后重新尝试bind
操作。accept
函数:accept
失败时,可能只是当前没有可用的连接请求,或者连接出现了一些问题。服务器可以根据错误类型决定是继续等待连接(例如在非阻塞模式下EAGAIN
错误时),还是进行一些错误处理和清理操作,而不会影响服务器继续监听新的连接请求。