Socket中listen/accept函数的区别

本文详细解析了listen和accept函数的作用及用法。listen函数使套接字变为被动连接,可接收其他进程的连接请求。accept函数则用于从监听状态的套接字中取出连接请求并创建新的连接套接字。

listen函数

摘要:listen函数使用主动连接套接口变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程。在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接。

listen函数在一般在调用bind之后-调用accept之前调用,它的函数原型是:

intlisten(int sockfd, int backlog)

参数sockfd

被listen函数作用的套接字,sockfd之前由socket函数返回。在被socket函数返回的套接字fd之时,它是一个主动连接的套接字,也就是此时系统假设用户会对这个套接字调用connect函数,期待它主动与其它进程连接,然后在服务器编程中,用户希望这个套接字可以接受外来的连接请求,也就是被动等待用户来连接。由于系统默认时认为一个套接字是主动连接的,所以需要通过某种方式来告诉系统,用户进程通过系统调用listen来完成这件事。

参数backlog

这个参数涉及到一些网络的细节。进程处理一个一个连接请求的时候,可能还存在其它的连接请求。因为TCP连接是一个过程,所以可能存在一种半连接的状态,有时由于同时尝试连接的用户过多,使得服务器进程无法快速地完成连接请求。如果这个情况出现了,服务器进程希望内核如何处理呢?内核会在自己的进程空间里维护一个队列以跟踪这些完成的连接但服务器进程还没有接手处理或正在进行的连接,这样的一个队列内核不可能让其任意大,所以必须有一个大小的上限。这个backlog告诉内核使用这个数值作为上限。

毫无疑问,服务器进程不能随便指定一个数值,内核有一个许可的范围。这个范围是实现相关的。很难有某种统一,一般这个值会小30以内。

accept函数

摘要:accept()用来接受参数s的socket连接,它的函数原型是:

intaccept(int s,struct sockaddr * addr,int * addrlen)

服务程序调用accept函数从处于监听状态的流套接字s的客户连接请求队列中取出排在最前的一个客户请求,并且创建一个新的套接字来与客户套接字创建连接通道,如果连接成功,就返回新创建的套接字的描述符,以后与客户套接字交换数据的是新创建的套接字;如果失败就返回 INVALID_SOCKET。该函数的第一个参数指定处于监听状态的流套接字;操作系统利用第二个参数来返回新创建的套接字的地址结构;操作系统利用第三个参数来返回新创建的套接字的地址结构的长度。


listen只是把套接字从主动变为被动,并限制链接数;剩下的问题就是accept的,它会检测的;
listen意思是监听:但是它不是一直在监听,accept才是;

理解:listen函数不会阻塞,它只是相当于把socket的属性更改为被动连接,可以接收其他进程的连接。listen侦听的过程并不是一直阻塞,直到有客户端请求连接才会返回,它只是设置好socket的属性之后就会返回。监听的过程实质由操作系统完成。但是accept会阻塞(也可以设置为非阻塞),如果listen的套接字对应的连接请求队列为空(没有客户端连接请求),它会一直阻塞等待。


<think> 我们讨论的是C语言中的accept函数是否会阻塞。根据套接字编程的标准行为,accept函数的行为取决于套接字的阻塞模式。 默认情况下,当使用socket()创建的套接字是阻塞模式的,那么在没有新连接到来时,accept函数会阻塞(即挂起当前线程直到有连接到来)。 但是,我们可以通过设置套接字为非阻塞模式(使用fcntl或ioctl设置O_NONBLOCK标志),使得accept在没有新连接时立即返回一个错误(通常是EWOULDBLOCK或EAGAIN),而不是阻塞。 因此,回答需要包含: 1. 默认情况下,accept会阻塞。 2. 如果套接字被设置为非阻塞模式,则accept不会阻塞,而是立即返回,如果没有连接则返回错误。 3. 可以通过select、poll或epoll等I/O多路复用技术来管理非阻塞套接字。 下面按照要求给出回答,并在最后提出相关问题。 </think> 在C语言的Socket编程中,**`accept()`函数默认会阻塞**,但其阻塞行为可通过套接字模式设置改变。以下是详细说明: ### 阻塞行为分析 1. **默认阻塞模式**: ```c int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len); ``` - 当监听队列为空时,程序会在此处**永久阻塞**,直到有新连接到来 - 阻塞期间线程/进程处于睡眠状态(不消耗CPU) 2. **阻塞解除条件**: - 新客户端发起连接(完成三次握手) - 信号中断(如`SIGINT`) - 套接字被关闭(`close(server_fd)`) ### 非阻塞模式设置 通过`fcntl()`设置`O_NONBLOCK`标志可避免阻塞: ```c #include <fcntl.h> // 设置非阻塞模式 int flags = fcntl(server_fd, F_GETFL, 0); fcntl(server_fd, F_SETFL, flags | O_NONBLOCK); // 此时accept立即返回 int client_fd = accept(server_fd, NULL, NULL); if (client_fd == -1) { if (errno == EWOULDBLOCK) { // 特定错误码 printf("无连接,继续其他任务\n"); } } ``` ### 阻塞 vs 非阻塞对比 | 特性 | 阻塞模式 | 非阻塞模式 | |--------------|-------------------------------|---------------------------| | **返回值** | 必返回有效fd或系统中断 | 可能返回-1(需检查errno) | | **错误码** | 无特定错误码 | `EWOULDBLOCK/EAGAIN` | | **适用场景** | 简单单线程服务 | 高性能服务器(配合I/O复用) | | **资源占用** | 线程挂起不占CPU | 需轮询消耗CPU | ### 最佳实践建议 1. **多线程服务**:每个连接独立线程时可用阻塞模式 2. **高并发服务**:推荐使用非阻塞+epoll组合: ```c // 创建epoll实例 int epoll_fd = epoll_create1(0); struct epoll_event ev; ev.events = EPOLLIN | EPOLLET; // 边缘触发 ev.data.fd = server_fd; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev); while (1) { int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); for (int i=0; i<n; i++) { if (events[i].data.fd == server_fd) { // 此时必有连接待accept accept(server_fd, ...); // 不会阻塞 } } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值