so_rcvdtimeo和so_sndtimeo仅对阻塞socket的recv/send有效;非阻塞下被忽略,需自行用select/poll控制超时;connect超时须结合非阻塞+可写事件检测及so_error检查;防假连接必须依赖应用层心跳。

setsockopt 设置 SO_RCVTIMEO 和 SO_SNDTIMEO 有效吗
有效,但只对阻塞式 socket 的 recv 和 send 调用起作用。非阻塞 socket 下这两个选项会被忽略,调用会立即返回 EWOULDBLOCK 或 EAGAIN,超时逻辑得自己控制。
常见错误是设了超时却没检查返回值,或者在非阻塞模式下误以为它能“暂停等待”。实际中,很多网络库(如 libevent、asio)底层也是靠这个 + select/poll 实现的。
-
SO_RCVTIMEO控制recv最多等多久才返回,单位是struct timeval(秒+微秒),设为{0, 0}表示永不等待 -
SO_SNDTIMEO同理,但注意:TCP 发送缓冲区满时才会真正阻塞,否则写操作常立刻成功,超时可能压根不触发 - Windows 下需用
DWORD毫秒值,Linux/macOS 用timeval;跨平台代码建议封装判断
非阻塞 socket 怎么做连接超时(connect)
connect 在非阻塞 socket 上会立即返回 EINPROGRESS,而不是等连接完成。真正的超时必须靠 select 或 poll 等待 socket 变为可写(表示 connect 完成或失败)。
容易踩的坑是只等可读 —— 连接失败时 socket 通常只变可写,不产生可读事件;而且成功后必须用 getsockopt(..., SO_ERROR, ...) 检查错误码,不能直接认为连接成功。
- 调用
connect后,用select等待该 socket 的写就绪(writefds),超时时间自己控制 -
select返回后,调用getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, &len),若err != 0则连接失败 - 别漏掉
fcntl(sockfd, F_SETFL, O_NONBLOCK)开启非阻塞,否则connect会阻塞到底
用 poll 实现跨平台连接超时更可靠吗
比 select 更简洁,且没有 fd 数量限制(select 通常限于 1024),但 Windows 需要额外处理:WSAPoll 不支持 POLLIN/POLLOUT 的语义一致性,推荐优先用 select 或直接上 epoll/kqueue。
关键点在于:连接完成时,pollfd.revents & POLLIN 或 & POLLOUT 都可能被置位,但唯一可靠的方式仍是查 SO_ERROR。
-
pollfd.fd = sockfd,pollfd.events = POLLOUT(不是 POLLIN) -
poll()返回 > 0 后,必须调用getsockopt(sockfd, SOL_SOCKET, SO_ERROR, ...),否则无法区分成功/失败 - Linux 下
epoll对 connect 超时支持更好,但需手动epoll_ctl(EPOLL_CTL_ADD)并监听EPOLLOUT
为什么心跳 + recv 超时比 connect 超时更难防住“假连接”
即使 connect 成功、recv 不超时,中间链路(防火墙、NAT、对端崩溃)仍可能导致后续通信静默中断。仅靠底层超时无法发现这类“半开连接”。
真实服务中,必须叠加应用层心跳(如定期发 PING 包并等待 PONG),且心跳超时阈值应明显短于 TCP keepalive(默认 2 小时太长)。
- TCP keepalive 默认关闭,开启后需设置
SO_KEEPALIVE+TCP_KEEPIDLE/TCP_KEEPINTVL/TCP_KEEPCNT(Linux),Windows 用SO_KEEPALIVE+ registry 或setsockopt(SIO_KEEPALIVE_VALS) - 应用层心跳建议 30 秒发一次,5 秒无响应即断连,比依赖内核 keepalive 更可控
- 注意:某些云环境(如 AWS NLB)会主动中断空闲连接,此时 keepalive 可能被中间设备拦截,心跳是唯一可靠手段
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











