正确设置非阻塞需先读取原标志再按位或o_nonblock,否则清空o_cloexec等;windows须用ioctlsocket()传mode指针且mode≠0才非阻塞;非阻塞connect()返回einprogress/wsaewouldblock不表示失败,须select/poll后调getsockopt检查so_error;send/recv错误码跨平台获取方式不同。

Linux/macOS 下用 fcntl() 设置非阻塞必须先读再写标志
直接 fcntl(sockfd, F_SETFL, O_NONBLOCK) 是错的——它会把原有文件描述符标志(比如 O_CLOEXEC)全清掉。正确做法是分两步:先用 fcntl(sockfd, F_GETFL, 0) 拿到当前标志,再用按位或把 O_NONBLOCK 加进去,最后写回。
- 不检查返回值是常见疏漏:
fcntl()失败时返回 -1,errno可能是EBADF(fd 已关闭)或EACCES(权限不足),得立刻处理 - 设置后,
connect()、accept()、recv()、send()全部行为都会变:不再等数据/连接就绪,而是立刻返回 - 注意:
socket()创建的 fd 默认继承父进程标志,但不能假设它一定没被设过其他 flag
Windows 下必须用 ioctlsocket(),且参数是地址不是值
ioctlsocket() 的第三个参数必须是指向 u_long 的指针,不是直接传 1。传值会导致未定义行为,大概率崩溃或静默失败。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 典型写法:
u_long mode = 1; ioctlsocket(sockfd, FIONBIO, &mode); - 失败时返回
SOCKET_ERROR,要用WSAGetLastError()查具体错误,不能看errno -
mode = 0表示阻塞,非零才表示非阻塞——和 Unix 的O_NONBLOCK置位逻辑相反,容易写反 - 该调用只影响后续操作,对已在进行的
connect()无作用
非阻塞 connect() 成功与否不能只看返回值
调用 connect() 后,非阻塞 socket 几乎总返回 -1,errno(Linux/macOS)或 WSAGetLastError()(Windows)是 EINPROGRESS 或 WSAEWOULDBLOCK。这不代表失败,只是“正在连”。
- 真正判断是否连上,得用
select()或poll()监听 socket 是否可写(write-ready) - 就绪后必须立刻调用
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, &len)检查err是否为 0 - 仅靠
select()返回可写 ≠ 连接成功:可能已出错(如ETIMEDOUT),但 socket 仍被标记为可写 - Windows 下
SO_ERROR获取的是连接最终状态,不是当前 socket 状态
跨平台代码里 send()/recv() 的错误码要分开判断
非阻塞模式下,send() 和 recv() 返回 -1 时,Linux 看 errno == EAGAIN || errno == EWOULDBLOCK,而 Windows 必须用 WSAGetLastError() == WSAEWOULDBLOCK。这两个宏在 Windows 上相等,但 Linux 没有 WSAEWOULDBLOCK 宏定义。
- 别直接写
#ifdef _WIN32就完事:错误码语义一致,但获取方式不同,封装函数里要隔离判断逻辑 -
recv()返回 0 表示对端正常关闭连接,不是错误,这点在 Linux/Windows 下一致 - 发送缓冲区满时,
send()可能只发部分数据(返回正值但小于请求长度),需循环处理偏移量,不能只依赖一次调用 - 别忽略信号中断(
errno == EINTR):虽然非阻塞 socket 较少遇到,但 POSIX 下仍合法
connect() 的两段式判断,稍一跳步就会把连接失败当成成功。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










