当前位置: 首页 > news >正文

从餐馆迎客看 accept4:更灵活的“接客“高手

一、从餐馆迎客看 accept4:更灵活的"接客"高手

想象你经营着一家网红餐厅,门口总有排队的客人。一开始你雇了个服务员负责迎接客人,流程很固定:客人来了,引到座位,递上菜单——这就像标准的 accept 函数,功能单一,只能按默认方式处理新客人。

但渐渐地,你发现有些客人很特别:有的赶时间,希望服务员动作麻利点(非阻塞处理);有的自带餐具,希望用完就把餐厅的餐具收走(exec 时关闭资源)。这时你需要一个更灵活的服务员,能根据客人的需求调整服务方式——这就是 accept4 函数的作用。

简单说,accept4 是 Linux 系统中用于接受 TCP 连接的系统调用,它是 accept 函数的增强版。和 accept 一样,它能从监听套接字(相当于餐厅门口)接收新的客户端连接,但多了一个"秘密武器"——flags 参数,通过这个参数可以在接受连接的同时,直接设置新连接的属性(比如非阻塞模式、exec 时自动关闭),省去了后续调用 fcntl 等函数的麻烦,效率更高。

这种特性让 accept4 在高性能网络服务器中特别受欢迎,比如 Nginx、Redis 等需要处理大量并发连接的程序,用 accept4 能减少系统调用次数,提升处理效率。

二、函数的"身份卡":声明与来源

要使用 accept4,得先知道它的"出身"。这个函数定义在 <sys/socket.h> 头文件中,所以代码里必须加上 #include <sys/socket.h> 才能调用。

从"家族背景"来看,accept4 是 Linux 特有的扩展,不属于 POSIX 标准(虽然很多类 Unix 系统也支持,但最开始是 Linux 引入的),由 GNU C 库(glibc)提供支持,最终通过系统调用与内核交互。

函数的声明长这样:

int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);

看起来比 accept 多了最后一个 flags 参数,但正是这个参数让它变得灵活。

三、返回值:连接成功与否的"判决书"

accept4 的返回值就像服务员的"工作汇报",告诉你是否成功接到客人:

  • 成功时:返回一个非负整数,这是新创建的套接字描述符(可以叫它"客户端 fd"),用来与对应的客户端通信,相当于给新客人安排的专属座位号。
  • 失败时:返回 -1,同时设置全局变量 errno 说明失败原因。常见的错误有:
    • EAGAINEWOULDBLOCK:如果监听套接字是非阻塞的,且当前没有新连接,就会返回这个错误(相当于"现在没客人,稍等")。
    • ECONNABORTED:客户端在连接建立前主动断开了连接(客人排到一半走了)。
    • EMFILE:进程打开的文件描述符达到上限(座位都满了)。
    • ENFILE:系统打开的文件描述符总数达到上限(整个商场的座位都满了)。
    • EINVAL:sockfd 不是监听套接字,或 addrlen 无效(比如服务员认错了门口,或记错了客人信息长度)。
    • EFAULT:addr 或 addrlen 指向的内存区域不可访问(客人信息单是坏的)。

比如,如果你的程序在非阻塞模式下调用 accept4,而此时没有新连接,它就会返回 -1,errno 设为 EAGAIN,告诉你"暂时没客人,下次再来问"。

四、参数详解:四个参数的"分工合作"

accept4 有四个参数,各司其职,共同完成接受连接的任务:

  1. sockfd:类型是 int,代表监听套接字的描述符。这个套接字必须已经通过 bind 和 listen 准备好(相当于餐厅门口,已经摆好了"营业中"的牌子)。如果这个 fd 不是监听套接字,accept4 会返回 EINVAL 错误。

  2. addr:类型是 struct sockaddr *,用于存放客户端的地址信息(比如 IP 地址和端口)。这就像服务员记录客人的姓名和联系方式,方便后续服务。如果不需要客户端地址,可以传 NULL(不想记客人信息)。

  3. addrlen:类型是 socklen_t *,传入时表示 addr 缓冲区的大小,传出时表示实际存放的客户端地址长度(相当于告诉服务员信息单能写多少字,写完后再告诉你实际写了多少)。如果 addr 是 NULL,这个参数也得是 NULL。

  4. flags:类型是 int,这是 accept4 区别于 accept 的关键,用于设置新连接的属性。目前支持两个常用标志,可单独使用或组合(用 | 连接):

    • SOCK_NONBLOCK:新创建的客户端套接字会被设置为非阻塞模式。非阻塞模式下,调用 read、write 等 I/O 函数时,如果暂时无法完成操作(比如没数据可读),不会阻塞等待,而是返回 EAGAIN 错误(相当于告诉服务员"现在没菜,别等着,先去忙别的")。
    • SOCK_CLOEXEC:为新客户端套接字设置 FD_CLOEXEC 标志,意味着当进程调用 exec 系列函数启动新程序时,这个 fd 会被自动关闭(相当于客人用完餐具后,服务员主动把餐具收走,不留给下一桌客人)。

比如,accept4(sockfd, &addr, &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC) 表示接受一个连接,同时让新套接字是非阻塞的,且 exec 时自动关闭。

五、使用示例:动手实践见真章

光说不练假把式,咱们通过三个示例,从简单到复杂,看看 accept4 怎么用。

示例 1:基础用法——接受连接并打印客户端信息

这个示例创建一个监听套接字,用 accept4 接受新连接,打印客户端的 IP 和端口,然后关闭连接。

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>#define PORT 8888
#define BACKLOG 10  // 监听队列长度int main() {// 1. 创建监听套接字(IPv4,TCP)int listen_fd = socket(AF_INET, SOCK_STREAM, 0);if (listen_fd == -1) {perror("socket 创建失败");exit(EXIT_FAILURE);}// 设置端口复用(避免服务器重启时端口被占用)int opt = 1;if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {perror("setsockopt 失败");close(listen_fd);exit(EXIT_FAILURE);}// 2. 绑定地址和端口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(PORT);        // 端口转换为网络字节序if (bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {perror("bind 失败");close(listen_fd);exit(EXIT_FAILURE);}// 3. 开始监听if (listen(listen_fd, BACKLOG) == -1) {perror("listen 失败");close(listen_fd);exit(EXIT_FAILURE);}printf("服务器启动,监听端口 %d...\n", PORT);printf("等待客户端连接...\n");// 4. 接受连接struct sockaddr_in client_addr;socklen_t client_addr_len = sizeof(client_addr);// 用 accept4,flags 设为 0(默认行为,和 accept 一样)int client_fd = accept4(listen_fd, (struct sockaddr*)&client_addr, &client_addr_len, 0);if (client_fd == -1) {perror("accept4 失败");close(listen_fd);exit(EXIT_FAILURE);}// 5. 打印客户端信息char client_ip[INET_ADDRSTRLEN];inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);printf("新客户端连接:IP = %s,端口 = %d,客户端 fd = %d\n",client_ip, ntohs(client_addr.sin_port), client_fd);// 6. 关闭连接close(client_fd);close(listen_fd);printf("连接已关闭\n");return 0;
}

代码说明

  1. 先创建监听套接字,设置端口复用,绑定到 8888 端口并开始监听。
  2. 调用 accept4(listen_fd, &client_addr, &client_addr_len, 0) 接受连接,flags 为 0 表示和 accept 行为一致。
  3. inet_ntop 把客户端的网络字节序 IP 转换为字符串,打印客户端信息。
  4. 最后关闭客户端套接字和监听套接字。
示例 2:SOCK_NONBLOCK 标志——非阻塞接受连接

这个示例展示如何用 SOCK_NONBLOCK 标志创建非阻塞的客户端套接字,即使没有数据也不会阻塞程序。

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>#define PORT 8889
#define BACKLOG 10// 检查套接字是否为非阻塞模式
int is_nonblocking(int fd) {int flags = fcntl(fd, F_GETFL, 0);if (flags == -1) {perror("fcntl F_GETFL 失败");return -1;}return (flags & O_NONBLOCK) ? 1 : 0;
}int main() {// 创建并设置监听套接字(步骤同上)int listen_fd = socket(AF_INET, SOCK_STREAM, 0);if (listen_fd == -1) { perror("socket 失败"); exit(1); }int opt = 1;setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));struct sockaddr_in server_addr = {0};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 失败"); exit(1);}if (listen(listen_fd, BACKLOG) == -1) { perror("listen 失败"); exit(1); }printf("服务器启动,监听端口 %d(非阻塞示例)...\n", PORT);// 把监听套接字设为非阻塞(这样 accept4 没连接时会立即返回)if (fcntl(listen_fd, F_SETFL, O_NONBLOCK) == -1) {perror("fcntl 设置非阻塞失败");close(listen_fd);exit(1);}struct sockaddr_in client_addr;socklen_t client_addr_len = sizeof(client_addr);int client_fd;// 第一次调用 accept4:此时可能还没有客户端连接printf("\n第一次调用 accept4(带 SOCK_NONBLOCK)...\n");client_fd = accept4(listen_fd, (struct sockaddr*)&client_addr, &client_addr_len, SOCK_NONBLOCK);if (client_fd == -1) {if (errno == EAGAIN || errno == EWOULDBLOCK) {printf("当前没有新连接,accept4 返回 EAGAIN(正常现象)\n");} else {perror("accept4 失败");close(listen_fd);exit(1);}}// 等待用户输入,给用户时间启动客户端printf("请启动客户端连接(如 telnet localhost %d),然后按回车继续...\n", PORT);getchar();// 第二次调用 accept4:此时应该有客户端连接了printf("\n第二次调用 accept4(带 SOCK_NONBLOCK)...\n");client_fd = accept4(listen_fd, (struct sockaddr*)&client_addr, &client_addr_len, SOCK_NONBLOCK);if (client_fd == -1) {perror("accept4 失败");close(listen_fd);exit(1);}// 验证新客户端套接字是否为非阻塞int nonblock = is_nonblocking(client_fd);if (nonblock == 1) {printf("客户端 fd = %d 是非阻塞模式(正确)\n", client_fd);} else {printf("客户端 fd = %d 不是非阻塞模式(错误)\n", client_fd);}// 打印客户端信息char client_ip[INET_ADDRSTRLEN];inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip));printf("客户端信息:IP = %s,端口 = %d\n", client_ip, ntohs(client_addr.sin_port));// 关闭连接close(client_fd);close(listen_fd);return 0;
}

代码说明

  1. 前面步骤和示例 1 类似,但把监听套接字设为非阻塞(用 fcntl),这样 accept4 在没连接时不会阻塞。
  2. 第一次调用 accept4 时,因为还没有客户端连接,会返回 -1 且 errno 为 EAGAIN,这是正常现象。
  3. 等待用户启动客户端后,第二次调用 accept4 成功接受连接,并用 is_nonblocking 函数验证新客户端套接字确实是非阻塞的(因为用了 SOCK_NONBLOCK 标志)。
  4. 非阻塞套接字在后续的 read/write 操作中,若无法立即完成会返回 EAGAIN,适合高并发场景中避免程序卡住。
示例 3:SOCK_CLOEXEC 标志——exec 时自动关闭连接

这个示例验证 SOCK_CLOEXEC 标志的作用:用 accept4 创建带该标志的客户端套接字,然后调用 exec 启动新程序,检查新程序中该套接字是否已关闭。

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <errno.h>#define PORT 8890
#define BACKLOG 10// 子进程执行的函数:检查指定fd是否打开
void check_fd_in_child(int target_fd) {printf("子进程中,检查 fd = %d 是否打开...\n", target_fd);// 用 fcntl 获取 fd 标志,若成功则说明 fd 打开if (fcntl(target_fd, F_GETFD) != -1) {printf("fd = %d 是打开的(未设置 CLOEXEC 或标志无效)\n", target_fd);} else {if (errno == EBADF) {printf("fd = %d 已关闭(CLOEXEC 生效)\n", target_fd);} else {perror("fcntl 检查失败");}}exit(EXIT_SUCCESS);
}int main() {// 创建并设置监听套接字int listen_fd = socket(AF_INET, SOCK_STREAM, 0);if (listen_fd == -1) { perror("socket 失败"); exit(1); }int opt = 1;setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));struct sockaddr_in server_addr = {0};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 失败"); exit(1);}if (listen(listen_fd, BACKLOG) == -1) { perror("listen 失败"); exit(1); }printf("服务器启动,监听端口 %d(CLOEXEC 示例)...\n", PORT);printf("请启动客户端连接(如 telnet localhost %d),然后按回车继续...\n", PORT);getchar();// 接受连接,分别创建带和不带 SOCK_CLOEXEC 的客户端 fdstruct sockaddr_in client_addr;socklen_t client_addr_len = sizeof(client_addr);// 1. 创建不带 SOCK_CLOEXEC 的客户端 fdint client_fd_no_cloexec = accept4(listen_fd, (struct sockaddr*)&client_addr, &client_addr_len, 0);if (client_fd_no_cloexec == -1) { perror("accept4 失败(无 CLOEXEC)"); exit(1); }printf("创建不带 SOCK_CLOEXEC 的客户端 fd = %d\n", client_fd_no_cloexec);// 2. 创建带 SOCK_CLOEXEC 的客户端 fd(需要新客户端连接,所以让用户再启动一个)printf("请再启动一个客户端连接,然后按回车继续...\n");getchar();int client_fd_with_cloexec = accept4(listen_fd, (struct sockaddr*)&client_addr, &client_addr_len, SOCK_CLOEXEC);if (client_fd_with_cloexec == -1) { perror("accept4 失败(带 CLOEXEC)"); exit(1); }printf("创建带 SOCK_CLOEXEC 的客户端 fd = %d\n", client_fd_with_cloexec);// 创建子进程,在子进程中调用 exec 验证 fd 状态pid_t pid = fork();if (pid == -1) { perror("fork 失败"); exit(1); }if (pid == 0) {  // 子进程// 调用 execvp 执行新程序(这里用我们自己的函数模拟)// 实际中 exec 会替换进程镜像,这里为了演示直接调用检查函数printf("\n子进程启动(模拟 exec 后):\n");check_fd_in_child(client_fd_no_cloexec);  // 检查不带 CLOEXEC 的 fdcheck_fd_in_child(client_fd_with_cloexec); // 检查带 CLOEXEC 的 fd} else {  // 父进程// 等待子进程结束waitpid(pid, NULL, 0);printf("\n父进程:子进程已退出\n");// 关闭所有 fdclose(client_fd_no_cloexec);close(client_fd_with_cloexec);close(listen_fd);}return 0;
}

代码说明

  1. 创建监听套接字,接受两个客户端连接:一个用 flags=0(不带 SOCK_CLOEXEC),一个用 flags=SOCK_CLOEXEC。
  2. fork 出子进程,在子进程中模拟 exec 操作(实际中 exec 会替换程序,这里直接调用检查函数)。
  3. 子进程中用 fcntl(target_fd, F_GETFD) 检查两个客户端 fd 的状态:不带 SOCK_CLOEXEC 的 fd 应该是打开的,带 SOCK_CLOEXEC 的 fd 应该已关闭(因为 exec 时自动关闭)。
  4. 这个示例展示了 SOCK_CLOEXEC 的核心作用:防止文件描述符泄露到 exec 启动的新程序中,避免资源浪费。

六、编译与运行:让代码跑起来

这三个示例都是 C 代码,用 gcc 编译即可,不需要链接额外的库(accept4 是系统调用,glibc 已包含相关封装)。

编译命令
  • 示例 1:
    gcc -o accept4_basic accept4_basic.c -Wall
    
  • 示例 2:
    gcc -o accept4_nonblock accept4_nonblock.c -Wall
    
  • 示例 3:
    gcc -o accept4_cloexec accept4_cloexec.c -Wall
    

-Wall 选项会显示编译警告,帮助发现代码中的潜在问题,建议加上。

运行方法
  • 示例 1:

    ./accept4_basic
    

    然后在另一个终端用 telnet 或 nc 连接:

    telnet localhost 8888
    # 或
    nc localhost 8888
    

    服务器会打印客户端信息,然后关闭连接。

  • 示例 2:

    ./accept4_nonblock
    

    程序第一次调用 accept4 会提示"当前没有新连接",此时在另一个终端启动客户端:

    telnet localhost 8889
    

    回到服务器终端按回车,程序会接受连接并验证客户端套接字是非阻塞的。

  • 示例 3:

    ./accept4_cloexec
    

    按提示第一次启动客户端(连接 8890 端口),按回车;再启动第二个客户端,再按回车。服务器会创建两个客户端 fd,子进程会显示带 SOCK_CLOEXEC 的 fd 已关闭,不带的仍打开。

注意事项
  1. 端口权限:如果使用 1-1024 之间的端口(如 80、443),需要 root 权限运行(sudo ./程序名),否则 bind 会失败(errno=EACCES)。
  2. 客户端连接:示例 2 和 3 需要手动启动客户端,可使用 telnet、nc(netcat)或自己写的客户端程序。如果没有客户端连接,示例 2 中第二次调用 accept4 仍会返回 EAGAIN。
  3. 文件描述符限制:如果同时打开多个连接,可能需要调整进程的文件描述符限制(默认可能为 1024),可用 ulimit -n 65535 临时增大上限。
  4. 非阻塞模式的正确使用:非阻塞套接字的 read/write 操作需要循环处理 EAGAIN 错误,否则可能漏掉数据(示例 2 仅展示创建,实际使用中需注意)。

七、执行结果分析:透过现象看本质

示例 1 结果分析

服务器启动后输出:

服务器启动,监听端口 8888...
等待客户端连接...

当客户端用 telnet localhost 8888 连接后,服务器会打印:

新客户端连接:IP = 127.0.0.1,端口 = 54321,客户端 fd = 4
连接已关闭

(54321 是客户端的随机端口,fd=4 是新创建的客户端套接字描述符)

这说明 accept4 成功接受了连接,并正确获取了客户端的地址信息。flags=0 时,accept4 的行为和 accept 完全一致,适合不需要特殊属性的场景。

示例 2 结果分析

服务器启动后输出:

服务器启动,监听端口 8889(非阻塞示例)...第一次调用 accept4(带 SOCK_NONBLOCK)...
当前没有新连接,accept4 返回 EAGAIN(正常现象)
请启动客户端连接(如 telnet localhost 8889),然后按回车继续...

启动客户端并按回车后:

第二次调用 accept4(带 SOCK_NONBLOCK)...
客户端 fd = 4 是非阻塞模式(正确)
客户端信息:IP = 127.0.0.1,端口 = 54322

第一次调用时因为没有客户端连接,非阻塞模式下 accept4 立即返回 EAGAIN;第二次有连接后成功返回,且新 fd 确实是非阻塞的(通过 is_nonblocking 验证)。这说明 SOCK_NONBLOCK 标志能有效设置新连接的非阻塞属性,省去了后续调用 fcntl 的步骤。

示例 3 结果分析

服务器启动后,按提示启动两个客户端,最终输出:

创建不带 SOCK_CLOEXEC 的客户端 fd = 4
创建带 SOCK_CLOEXEC 的客户端 fd = 5子进程启动(模拟 exec 后):
子进程中,检查 fd = 4 是否打开...
fd = 4 是打开的(未设置 CLOEXEC 或标志无效)
子进程中,检查 fd = 5 是否打开...
fd = 5 已关闭(CLOEXEC 生效)父进程:子进程已退出

结果显示,不带 SOCK_CLOEXEC 的 fd=4 在子进程(模拟 exec 后)中仍打开,而带 SOCK_CLOEXEC 的 fd=5 已关闭。这验证了 SOCK_CLOEXEC 的作用——防止文件描述符泄露到 exec 启动的新程序中,保持资源清洁。

八、深入理解:accept4 背后的工作机制

accept4 看似简单,但其内部涉及内核的复杂操作。我们来拆解一下它的工作流程:

  1. 等待连接:当调用 accept4 时,内核会检查监听套接字的连接队列(存放已完成三次握手的客户端)。如果队列不为空,就取出第一个连接;如果队列为空且监听套接字是阻塞的,进程会进入睡眠状态,直到有新连接到来。

  2. 创建新套接字:内核为新连接创建一个新的套接字(客户端 fd),这个套接字继承监听套接字的一些属性(如协议族),但状态变为已连接。

  3. 设置标志位:根据 flags 参数,内核为新套接字设置相应的标志:

    • 若指定 SOCK_NONBLOCK,就为新 fd 设置 O_NONBLOCK 标志(非阻塞)。
    • 若指定 SOCK_CLOEXEC,就为新 fd 设置 FD_CLOEXEC 标志(exec 时关闭)。
  4. 返回结果:内核把客户端的地址信息填入 addr 缓冲区,更新 addrlen,然后返回新套接字的描述符;如果出错(如无连接且非阻塞),返回 -1 并设置 errno。

用 Mermaid 图可视化这个过程:

graph TDA[调用 accept4(sockfd, addr, addrlen, flags)] --> B{内核检查监听队列}B -->|队列非空| C[取出第一个完成三次握手的连接]B -->|队列空且阻塞| D[进程睡眠,等待新连接]B -->|队列空且非阻塞| E[返回 -1,errno=EAGAIN]C --> F[创建新客户端套接字(已连接状态)]F --> G{处理 flags}G -->|有 SOCK_NONBLOCK| H[设置新 fd 为非阻塞模式]G -->|有 SOCK_CLOEXEC| I[设置新 fd 的 FD_CLOEXEC 标志]G -->|无标志| J[不做额外设置]F --> K[填充客户端地址到 addr,更新 addrlen]K --> L[返回新客户端 fd(成功)]

和 accept 相比,accept4 的优势在于"一站式"设置属性。如果用 accept 接受连接后再用 fcntl 设置非阻塞和 CLOEXEC,需要多两次系统调用(fcntl),而 accept4 一次调用就能完成,减少了用户态和内核态的切换开销,这在高并发场景下能显著提升性能。

九、常见问题与避坑指南

  1. 混淆监听套接字和客户端套接字的阻塞属性:accept4 的 SOCK_NONBLOCK 标志只影响新创建的客户端套接字,监听套接字的阻塞属性需要单独用 fcntl 设置(如示例 2 所示)。如果监听套接字是阻塞的,即使 accept4 用了 SOCK_NONBLOCK,在没连接时也会阻塞。

  2. 错误组合 flags 参数:目前 accept4 只支持 SOCK_NONBLOCK 和 SOCK_CLOEXEC 两个标志,传入其他值会返回 EINVAL 错误。比如传入 SOCK_STREAM 是无效的,因为这是创建套接字时的参数,不是 accept4 的 flags。

  3. 忽略 EAGAIN 错误:在非阻塞模式下,accept4 返回 EAGAIN 是正常现象(表示暂时没连接),不应视为致命错误,程序应稍后重试,而不是退出。

  4. 忘记关闭客户端 fd:每个客户端 fd 都是宝贵的系统资源,处理完连接后必须用 close() 关闭,否则会导致 fd 泄露,最终耗尽系统资源。

  5. 在 Windows 系统使用 accept4:accept4 是 Linux 特有的函数,Windows 不支持(Windows 有类似的 WSAAccept 但用法不同),跨平台程序需要注意兼容性处理。

十、总结:accept4 的核心价值

accept4 作为 accept 的增强版,其核心价值在于通过 flags 参数实现了连接接受与属性设置的"一步到位",减少了系统调用次数,提升了程序效率,尤其适合高性能网络服务器。

我们用一张 Mermaid 图总结其核心要点:

accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags)
接受TCP连接,可同时设置新连接属性
减少系统调用,提升高并发场景性能
Linux(主要)、部分类Unix系统
sockfd:监听套接字fd
addr:存储客户端地址的缓冲区
addrlen:地址缓冲区长度(传入传出)
flags:SOCK_NONBLOCK(非阻塞)、SOCK_CLOEXEC(exec时关闭)
成功:客户端fd;失败:-1(设errno)
高性能Web服务器、游戏服务器、即时通讯后台

通过本文的讲解,相信你对 accept4 已经有了深入的理解。记住,它的关键在于灵活使用 flags 参数,根据实际需求选择非阻塞模式和 CLOEXEC 标志,让你的网络程序更高效、更健壮。在实际开发中,结合 epoll 等 I/O 多路复用机制,accept4 能发挥更大的作用,轻松应对海量并发连接。

http://www.dtcms.com/a/438303.html

相关文章:

  • Metasploit基础(MSF)
  • 浅析物理层过程
  • 总结 IP 协议的相关特性
  • 网球馆自动预约系统的反调试
  • PyQt5 QLineEdit组件详解:单行文本输入控件的完整指南
  • 网站建设的毕业报告公司名称变更流程及需材料
  • OSPF 多区域实验 概念及题目
  • 网站建设要经历哪些步骤丝芭传媒有限公司
  • 东莞微信网站建设怎样ceo是什么职位什么工作
  • model.fit(train_X, train_y)
  • 数据结构之队列:初始化、入队、出队与源码全解析
  • 国内外优秀网站网站建设江苏百拓
  • hive、spark任务报错或者异常怎么排查以及定位哪段sql
  • 南昌商城网站设计洛阳青峰网络做网站
  • 算法 - FOC闭环位置控制
  • 探索高效安全的去中心化应用——Solana区块链
  • 大模型openai服务网关,认证,限流,接口输入输出的修正,监控等功能
  • 贵州百度seo整站优化做网站收入怎样
  • AI驱动的视频生成革命:MoneyPrinterTurbo技术架构深度解析
  • 东莞p2p网站开发价钱店铺logo在线制作免费
  • LeetCode:89.分割等和子集
  • 基于 GEE 处理、可视化和导出 Landsat 5 和 Landsat 8 卫星遥感影像数据
  • Python基础入门例程88-NP88 句子拆分
  • 网站上添加图片的原则59网一起做网站
  • k8s学习(一)——kubernetes重要基础概念概述
  • Unity excel 表格文件导入
  • 【系统架构设计-零】系统架构设计总述与学习线路
  • 做网站郑州公司北京工商局网站如何做股东变更
  • 【C++】C++ 中多态是什么?咋用的?
  • Dijkstra最短路径算法