SO_RCVTIMEO与SO_SNDTIMEO仅对阻塞socket生效,超时后recv/send返回-1且errno置为ETIMEOUT(Linux)或WSAETIMEDOUT(Windows);非阻塞socket下该设置无效,此时EAGAIN/EWOULDBLOCK仅表示无数据或缓冲区满。

setsockopt 设置 SO_RCVTIMEO 和 SO_SNDTIMEO 失败?检查 struct timeval 初始化
直接调用 setsockopt 设置接收/发送超时却没生效,大概率是 struct timeval 成员赋值错误。这个结构体两个字段都是 long 类型,不是 int 或 time_t,尤其在 64 位系统上容易因类型截断导致超时设为 0(即立即返回)或极大值(看似不超时)。
实操建议:
-
tv_sec填秒数,tv_usec填微秒数(0–999999),别填毫秒 - 务必清零整个结构体:
struct timeval tv = {0};,避免未初始化的tv_usec是垃圾值 - Windows 下需用
DWORD毫秒数 +SO_RCVTIMEO/SO_SNDTIMEO,和 POSIX 不兼容,跨平台要条件编译
recv / send 返回 -1 且 errno == EAGAIN/EWOULDBLOCK 就代表超时了?
不是。这仅表示当前无数据可读或缓冲区满,**前提是 socket 已设为非阻塞模式**。而 SO_RCVTIMEO/SO_SNDTIMEO 只对阻塞 socket 生效——它让原本永久等待的 recv/send 在超时后返回 -1 并置 errno 为 ETIMEOUT(Linux)或 WSAETIMEDOUT(Windows)。
关键区别:
立即学习“C++免费学习笔记(深入)”;
- 非阻塞 socket:永远不等,
recv立即返回 -1 +EAGAIN,超时设置无效 - 阻塞 socket + 超时设置:
recv等到有数据、连接关闭、或超时;超时则返回 -1 +ETIMEOUT - 想兼顾响应性和可控等待,常用
select/poll/epoll管理多个 socket,它们本身支持超时参数
Windows 下 setsockopt 返回 0 却 recv 仍阻塞?确认 socket 是阻塞模式
Windows 的 SO_RCVTIMEO 对非阻塞 socket 完全无效,且不会报错。即使 setsockopt 成功返回 0,若 socket 是通过 ioctlsocket(sock, FIONBIO, &nonblocking) 设为非阻塞的,超时设置就形同虚设。
验证与修复步骤:
- 调用
ioctlsocket(sock, FIONBIO, &arg)前先读arg值,确认当前是否为 0(阻塞) - Windows 下不要混用
WSAAsyncSelect或WSAEventSelect后再设超时——事件模型下应改用WSAWaitForMultipleEvents控制等待 - 若必须用非阻塞 + 超时逻辑,自己用
GetTickCount64()或QueryPerformanceCounter包一层循环检测,但注意 CPU 占用
超时时间设太小(如 1ms)会导致频繁失败?考虑系统调度粒度
Linux 内核中 SO_RCVTIMEO 最小精度约 10ms,Windows 约 15ms。设成 1ms 实际还是按最小粒度触发,且高频率短超时会显著增加系统调用开销和上下文切换。
合理设定建议:
- 心跳/保活类场景:2–5 秒较稳妥,避开网络抖动
- 实时性要求高的本地通信:优先用
AF_UNIXsocket + 非阻塞 +epoll,比 TCP 超时更准 - 调试时临时设 100ms 可以,上线前务必压测——某些嵌入式或容器环境调度延迟可能远超预期
真正难处理的是半开连接(one-side close):对方崩溃断网,本端 recv 仍阻塞,此时仅靠 SO_RCVTIMEO 不够,得配合应用层心跳或 TCP_KEEPIDLE 等底层选项。


















