如何在启用O_NONBLOCK的代码中捕获IO错误?
非阻塞IO(O_NONBLOCK)下的错误察觉与处理
当启用O_NONBLOCK标志调用非阻塞IO函数时,返回-1并不一定意味着发生了真实错误——这可能只是当前没有可用的IO资源(比如无数据可读、缓冲区已满)。要准确区分正常的非阻塞状态和真实IO错误,按以下步骤处理:
1. 先判断函数返回值
所有非阻塞IO函数(如read、write、connect、accept)的返回值是核心判断依据:
- 返回值 ≥0:操作成功(对于
read/write,返回值是处理的字节数;对于accept是新的fd;connect返回0表示成功)。 - 返回值 = -1:需要进一步检查
errno变量,区分临时状态和真实错误。
2. 读取errno区分情况
必须在函数返回-1后立即读取errno,因为后续的系统调用会覆盖这个变量的值。使用前要包含<errno.h>头文件。
临时非阻塞状态(非错误)
如果errno是EAGAIN或EWOULDBLOCK,说明当前IO操作无法立即完成,属于非阻塞模式下的正常情况:
- 读操作:当前无数据可读,后续可通过
select/poll/epoll等IO多路复用工具,等待fd变为可读后重试。 - 写操作:输出缓冲区已满,等待fd变为可写后重试。
connect操作:若errno是EINPROGRESS,表示连接正在建立中,需后续通过select检查fd可写状态,再调用getsockopt(fd, SOL_SOCKET, SO_ERROR, ...)确认连接是否成功。
真实IO错误
除上述值外的errno均为真实错误,常见错误码及处理方式:
EBADF:无效文件描述符,检查fd是否已关闭或未正确打开。ECONNRESET:TCP连接被对方重置,需关闭当前fd并清理相关资源。EINVAL:函数参数错误,检查调用时的参数合法性(比如缓冲区长度为0)。EPIPE:管道破裂或TCP连接已关闭后尝试写操作,需关闭fd。
代码示例:非阻塞read的错误处理
#include <unistd.h> #include <errno.h> #include <stdio.h> #define BUF_SIZE 1024 int handle_nonblock_read(int fd) { char buf[BUF_SIZE]; ssize_t n = read(fd, buf, BUF_SIZE); if (n == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { // 无数据,等待后重试 printf("No data available, will wait for fd ready\n"); return 0; } else { // 真实错误,输出信息并清理 perror("read failed"); close(fd); return -1; } } else if (n == 0) { // Socket场景下,说明对方关闭了连接 printf("Peer closed connection\n"); close(fd); return -1; } else { // 处理读取到的数据 printf("Read %zd bytes: %.*s\n", n, (int)n, buf); return 0; } }
注意事项
- 不要对
EAGAIN/EWOULDBLOCK调用perror,这会误导问题排查,因为它们不是错误。 - 结合IO多路复用使用非阻塞IO时,先等待fd的可读/可写事件再执行操作,能减少临时状态的出现,同时也能及时捕获错误事件(比如fd出错时,
select会标记为可读可写)。 - 非阻塞
connect不能直接用errno判断成功,必须通过getsockopt获取SO_ERROR来确认连接状态。
内容的提问来源于stack exchange,提问作者gitman-2021
相关产品推荐
相关产品推荐

