在完成三次握手后,该连接被放到全连接队列(也称 Accept Queue)里面,server 端 TCP 会创建一个 sock 结构来与 client 端的 scoket 进行一对一的数据传递。
但这个 sock 存在于内核中,server 端用户进程还无法使用。进程要想使用这个新的连接,必须调用 accept 系统调用生成一个与sock 关联的 socket,然后进程通过对这个 socket 进行 recv、send 等操作来实现与 client 端的数据传递。
accept 返回一个新的套接字描述符,用于与客户端进行通信。通过 accept,服务器可以处理多个客户端的连接请求,实现多用户并发访问。
以下是 accept
系统调用的原型:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <sys/types.h> #include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数: sockfd:套接字的文件描述符,socket()系统调用返回的文件描述符fd(通常是服务器的监听套接字)。 addr:指向 struct sockaddr 类型的指针,用于存储连接的对端地址信息。可以为 NULL,表示不关心对端地址。 addrlen:是一个指向 socklen_t 类型的指针,用于指定 addr 缓冲区的大小。在调用 accept 之前,addrlen 应该被初始化为 struct sockaddr 缓冲区的大小,函数返回时,addrlen 会被设置为实际地址结构的长度。
返回值: 如果连接成功,返回一个新的文件描述符,这个文件描述符用于与客户端通信。 如果失败,返回 -1,并设置 `errno` 表示错误原因。
|
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h>
int main() { int listenfd = socket(AF_INET, SOCK_STREAM, 0); if (listenfd == -1) { perror("socket"); exit(EXIT_FAILURE); }
struct sockaddr_in server_addr; 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(8080);
if (bind(listenfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { perror("bind"); close(listenfd); exit(EXIT_FAILURE); }
if (listen(listenfd, 10) == -1) { perror("listen"); close(listenfd); exit(EXIT_FAILURE); }
struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr);
int clientfd = accept(listenfd, (struct sockaddr*)&client_addr, &client_len); if (clientfd == -1) { perror("accept"); close(listenfd); exit(EXIT_FAILURE); }
close(clientfd); close(listenfd);
return 0; }
|
在这个示例中,accept
函数被用于等待并接受客户端的连接。它会阻塞程序的执行,直到有客户端连接请求到达,然后创建一个新的套接字 clientfd
用于与客户端通信。在实际使用中,你可以在 clientfd
上进行读写操作,与客户端进行数据交换。连接结束后,应该关闭 clientfd
和 listenfd
。
Reference