非阻塞网络套接字文件描述符何时不可写并触发EAGAIN?
非阻塞网络套接字write()返回EAGAIN的常见场景
当非阻塞网络套接字调用write(fd, buff, len)返回-1且errno设为EAGAIN(或EWOULDBLOCK,两者等价),核心原因是当前无法将数据写入套接字的发送缓冲区,常见场景如下:
发送缓冲区已满
这是最普遍的情况。TCP套接字的内核发送缓冲区有固定大小(可通过SO_SNDBUF调整),当以下情况发生时,缓冲区会被占满:- 对端的TCP接收窗口过小(或为0,即"零窗口"),导致本地发送的数据无法被对端ACK,内核无法释放发送缓冲区空间;
- 短时间内向套接字写入了大量数据,超过了缓冲区剩余可用空间;
- 网络拥塞导致数据在链路中堆积,内核无法及时将缓冲区数据发送出去。
TCP连接处于未完全建立状态
针对非阻塞TCP套接字:如果调用connect()后,连接还处于SYN_SENT或SYN_RECV阶段(即三次握手未完成),此时调用write()会返回EAGAIN。必须等待连接完全建立(比如通过epoll监听EPOLLOUT事件)后,才能正常写入数据。未等待可写事件触发就执行写入
在使用epoll等多路复用工具时,如果没有先监听并等待EPOLLOUT事件触发就直接调用write(),此时若发送缓冲区没有足够空间,就会立即返回EAGAIN。正确的做法是当epoll通知EPOLLOUT时,再执行写入操作。UDP套接字缓冲区耗尽
虽然UDP无连接,但如果短时间内发送大量UDP数据包,导致内核的UDP发送缓冲区被占满,非阻塞模式下调用write()也会返回EAGAIN。不过这种情况比TCP少见,因为UDP本身不保证交付,内核对UDP缓冲区的限制相对宽松。
需要注意:如果对端关闭了套接字(发送FIN或RST),此时调用write()通常会返回EPIPE(忽略SIGPIPE信号时),而非EAGAIN,这不属于你问的场景。
内容的提问来源于stack exchange,提问作者Aritra Sur Roy
相关产品推荐
相关产品推荐

