40. socket 就绪条件

本文详细介绍了TCP套接字在什么条件下可读可写,包括缓冲区的运作机制、接收与发送低水位标记,以及在不同情况下的读写状态。重点讨论了监听socket的新连接请求、已连接socket的数据读取、通信对端关闭、错误处理等场景,同时也提到了非阻塞connect的就绪条件。了解这些内容对于优化网络通信和错误处理至关重要。

在实现网络通信前, 先来了解一下socket套接字什么时候就绪(可读可写). 第一次学习socket只需要明白我用红色标记的条件就行.


缓冲区

每个套接字接收区缓存 (用于读) 和发送区缓存 (用于写) :

  • 接收区缓存: 接收数据, 从内核缓冲复制到用户应用程序数据缓冲区.
  • 发送去缓存: 用户应用程序数据缓冲区复制到内核缓冲区, 等待发送.

每个套接字都有接收缓存区低水位标记(用于读)和发送缓存区低水位标记(用于写):

  • 接收低水位: 可读时套接字接收缓冲区中所需的最少数据量. 对于TCP, 其默认值为1.
  • 发送低水位: 可写时套接字发送缓冲区中所需的最少可用空间. 对于TCP, 其默认值常为2048.

可以修改接收缓冲区的低水位来保证不用频繁的调用读, 等累计到一定的量在一次性读. 这样就可以减少读的次数. 像libevent就设置了用户的低水位, 高水位.


socket可读

  1. 监听socket上有新的连接请求. 因为套接字收到了对方的connect请求, 执行了三次握手的第一步: 对方发送SYN请求过来, 使该方监听套接字处于可读状态; 通常情况下, 对这样的套接字执行accept操作不会阻塞.
  2. 已连接socket. socket内核接收缓存区中的字节数大于或等于其低水位标记SO_RCVLOWAT, 此时可以无阻塞地读该socket, 并且读操作返回的字节数大于0. 对于TCP和UDP套接字而言, 其缺省值为1, 默认情况下, 只要缓冲区中有数据, 那就是可读的.
  3. socket通信对端关闭连接[1], 此时对该socket读操作将返回0.
  4. socket上有未处理的错误[2]. 对这样的套接字的读操作将不会阻塞并返回-1(即返回一个错误), 同时把errno设置成确切的错误条件. 此时我们可以使用getsockopt来读取和清除该错误. select能处理的异常情况只有一种: socket上接收到带外数据

socket可写:

  1. 已连接socket. socket内核发送缓冲区中的可用字节数大于或等于其低水位标记SO_SNDLOWAT. 此时我们可以无阻塞写该socket, 并且写操作返回的字节数大于0. 对于TCP和UDP套接字而言, 低水位默认值为2048, 发送缓冲区默认大小为8K. 默认情况下, 一个套接字连接成功后,总是可写的.

  2. socket写操作被关闭. 对写操作被关闭的socket执行写操作将触发一个SIGPIPE信号, 该信号的缺省行为是终止进程.

  3. socket使用非阻塞connect连接成功或者失败(超时)[3]. 使用非阻塞式connect的套接字已建立连接, 或者connect已经以失败告终, 即connect已经完成.

  4. socket上有未处理的错误[2]. 对这样的套接字的写操作将不会阻塞并且返回-1(即返回一个错误), 同时把errno设置成确切的错误条件. 此时我们可以使用getsockopt来读取和清除该错误. select能处理的异常情况只有一种: socket上接收到带外数据.

注意 : 当socket错误时, 此时既可读也可写.


小结

  • 了解缓冲区
  • socket读写时期
<think>嗯,用户这次的问题是关于select系统调用中触发条件的机制,特别是询问"每个socket就绪都会触发还是需要循环调用"。结合之前的对话历史,用户已经对select的工作原理有一定了解(比如内核遍历fd数组的机制),现在更关注触发逻辑的细节。 用户提供的引用材料很有价值,特别是引用[1]提到"select会不断轮询所有socket",引用[2]强调遍历所有fd的性能问题,引用[4]则对比了epoll的事件回调机制。这些正好能解释select的触发特性。 需要重点澄清两个关键点: 1. **单次触发的本质**:select是单次通知机制,一次调用只会返回当前已就绪的fd集合,不会为每个就绪事件单独触发系统调用。这与信号驱动I/O有本质区别。 2. **循环调用的必要性**:由于新事件可能在其他fd上持续到达,且内核不维护状态,必须通过循环反复调用select才能持续监控。 用户可能存在的误解是:是否每个fd就绪都会像中断一样立即唤醒select?实际上,在select阻塞期间,多个fd可能先后就绪,但内核只会唤醒进程一次(除非使用ET模式,但select不支持ET)。 另外要强调超时参数的作用:当没有fd就绪时,select会阻塞直到超时或至少一个fd就绪。这个设计决定了它必须配合循环使用。 最后需要对比epoll的差异点(引用[4]的回调机制),因为用户似乎对事件驱动模型感兴趣。不过回答要聚焦在select本身,避免偏离主题。</think>### select监控多个socket的触发机制详解 #### 一、触发条件与核心机制 1. **触发条件** select的触发需要满足以下任一条件: - **至少一个被监视的fd就绪**(可读/可写/异常) - **达到用户设置的超时时间** - **被信号中断**(如系统调用中断) 2. **单次触发特性** - **单次唤醒**:即使多个fd同时就绪,**一次select调用只会返回一次通知**,告知用户"有fd就绪",但不会说明具体数量或位置(需用户遍历判断)[^1][^2]。 - **非事件驱动**:与epoll不同,select没有"每个fd就绪单独触发"的机制。例如: - 若3个socket同时有数据到达,select仅唤醒一次,用户需遍历所有fd找出就绪的3个[^1][^4]。 ```c // 伪代码:select返回后需遍历所有fd fd_set read_fds; int max_fd = ...; int ret = select(max_fd+1, &read_fds, NULL, NULL, &timeout); if (ret > 0) { for (int fd=0; fd<=max_fd; fd++) { if (FD_ISSET(fd, &read_fds)) { // 必须手动检查每个fd handle_io(fd); } } } ``` #### 二、循环调用的必要性 1. **必须循环的原因** - **状态重置**:每次调用select时,内核会覆盖原有的`fd_set`。用户必须在循环中**重新设置监视集合**(添加新fd或移除关闭的fd)[^1][^2]。 - **持续监控需求**:单次select仅检测当前时刻的就绪状态。后续新到达的数据或新创建的fd需通过**新一轮调用**才能被监视[^1][^4]。 2. **典型工作流程** ```mermaid graph LR A[开始循环] --> B[设置fd_set和超时] B --> C[调用select] C --> D{有fd就绪?} D -->|是| E[遍历所有fd找出就绪项] D -->|超时/中断| F[处理超时逻辑] E & F --> G[处理业务逻辑] G --> H[更新fd_set] --> A ``` #### 三、与epoll的触发机制对比 | **特性** | select | epoll | |-------------------|----------------------------------------|------------------------------------| | **触发方式** | 轮询检测所有fd | 回调通知就绪fd(事件驱动)[^4] | | **新fd加入** | 需在下轮循环手动添加 | 通过`epoll_ctl`动态添加 | | **就绪事件通知** | 单次返回所有就绪fd(需遍历) | 仅返回就绪fd列表($O(1)$获取)[^4] | | **内核数据结构** | 每次传递完整fd_set | 内核维护红黑树+就绪链表[^3][^4] | #### 四、关键结论 1. **非持续触发**:单个socket就绪**不会单独触发**select,必须等待循环中下一次调用。 2. **强制循环设计**:因以下原因必须循环调用: - 需动态更新监视的fd集合(如新socket加入) - 需处理多次I/O事件(如长连接持续收发数据) - 需重置超时时间和`fd_set`[^1][^2] 3. **性能瓶颈**:每次调用需全量遍历所有fd($O(n)$复杂度),当fd数量大时效率显著低于epoll[^2][^4]。 > **示例场景**:Web服务器用select监控1000个连接。当连接A和B同时收到数据时,select唤醒一次。服务器遍历所有1000个fd,发现A和B就绪并处理数据。处理完后**必须重新调用select**,否则后续C连接的数据将无法被检测到。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值