recv返回0表示对端已正常关闭连接,是TCP优雅断开的标志,此时应处理完缓冲区残留数据后关闭本端socket;返回-1需结合errno判断:EAGAIN/EWOULDBLOCK为非阻塞下无数据,可重试;EINTR为信号中断,也应重试;ENETRESET、ECONNRESET等则需立即关闭连接。

recv 返回 0 表示对端已关闭连接
这不是错误,而是 TCP 正常关闭流程的一部分:对方调用 close() 或 shutdown(SD_SEND) 后,本端 recv() 下一次调用就会返回 0。此时 socket 仍可发送(除非也调用了 shutdown(SD_RECEIVE)),但不能再读新数据。
常见误判场景:
- 把返回 0 当作“没收到数据”而重试,导致逻辑卡死或无限循环
- 忽略缓冲区中可能残留的未处理完的数据(比如半包),直接退出接收循环
正确做法是:检查返回值为 0 时,先处理完 std::vector<uint8_t></uint8_t> 缓冲区里已有的数据(包括粘包/半包),再安全关闭本端 socket 或进入清理流程。
recv 返回 -1 且 errno 是 EAGAIN/EWOULDBLOCK 表示无数据可读
这仅在设置了非阻塞 socket(ioctlsocket(..., FIONBIO, &on) 或 fcntl(fd, O_NONBLOCK))时出现。它不是错误,只是当前内核接收缓冲区为空,需稍后重试。
立即学习“C++免费学习笔记(深入)”;
关键点:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- Windows 下用
WSAGetLastError()获取错误码;Linux/macOS 用errno -
EAGAIN和EWOULDBLOCK在大多数系统上等价,建议两个都判断 - 若 socket 是阻塞的,
recv()不会返回 -1 +EAGAIN—— 它会直接阻塞住,直到有数据、超时或连接异常
典型处理结构:
while (true) {
int n = recv(sock, buf, sizeof(buf), 0);
if (n > 0) {
// 处理数据
break;
} else if (n == 0) {
// 对端关闭,按上一节处理
break;
} else {
int err = WSAGetLastError(); // Windows
if (err == WSAEWOULDBLOCK || err == WSAEAGAIN) {
continue; // 非阻塞下无数据,继续轮询或等待事件
}
// 其他错误:WSAEINTR、WSAENETRESET、WSAETIMEDOUT 等,需分情况响应
break;
}
}
recv 返回 -1 且 errno 是 EINTR 表示被信号中断
系统调用被信号(如 SIGALRM、SIGHUP)打断时会返回 -1 并设 errno = EINTR。这不是失败,而是提醒你“刚才没做完,可以重试”。
容易踩的坑:
- 没检查
EINTR就直接报错退出,导致信号密集时连接频繁断开 - 在多线程环境里混用信号和 socket,引发竞态(不推荐)
稳妥做法:只要看到 EINTR,就忽略并重试 recv() —— 这和 EAGAIN 的处理逻辑一致,可合并判断。
其他常见 errno 值及应对优先级
真正需要终止连接或报错的错误码其实不多,优先关注这几个:
-
WSAENETRESET/ENETRESET:对端强制关闭(RST 包),连接已不可用,应立即关闭本地 socket -
WSAETIMEDOUT/ETIMEDOUT:设置了SO_RCVTIMEO后超时,说明网络异常或对端僵死,可重连或告警 -
WSAECONNABORTED/ECONNABORTED:本端主动中止连接(如 SO_LINGER=0),无需重试 -
WSAECONNRESET/ECONNRESET:对端异常断连(如进程崩溃),连接失效,必须关闭
注意:recv() 返回 -1 本身不等于出错 —— 必须结合 errno(或 WSAGetLastError())才能定性。很多程序只看返回值就 panic,反而掩盖了真实问题。

















