系统调用 accept

在完成三次握手后,该连接被放到全连接队列(也称 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);
}

// 至此,成功建立连接,可以使用 clientfd 与客户端通信

// 关闭套接字
close(clientfd);
close(listenfd);

return 0;
}

在这个示例中,accept 函数被用于等待并接受客户端的连接。它会阻塞程序的执行,直到有客户端连接请求到达,然后创建一个新的套接字 clientfd 用于与客户端通信。在实际使用中,你可以在 clientfd 上进行读写操作,与客户端进行数据交换。连接结束后,应该关闭 clientfdlistenfd

Reference


系统调用 accept
https://flepeng.github.io/002-Linux-41-系统调用-系统调用-accept-源码分析/
作者
Lepeng
发布于
2024年9月23日
许可协议