那是因为你没有使用 keepalive 超时。
在接收端,keepalive socket选项是检测死连接的最佳方案。
但是,如果您的应用程序继续写入套接字,则需要考虑更多。
即使您已经为您的应用程序套接字设置了keepalive选项,您也无法及时检测到套接字的死连接状态,以防您的应用程序一直在套接字上写入。
那是因为内核 tcp 堆栈的 tcp 重传。
tcp_retries1 和 tcp_retries2 是用于配置 tcp 重传超时的内核参数。
重传超时的精确时间很难预测,因为它是通过 RTT 机制计算的。
你可以在 rfc793 中看到这个计算。 (3.7. 数据通信)
https://www.rfc-editor.org/rfc/rfc793.txt
每个平台都有用于 tcp 重传的内核配置。
Linux : tcp_retries1, tcp_retries2 : (exist in /proc/sys/net/ipv4)
http://linux.die.net/man/7/tcp
HPUX : tcp_ip_notify_interval, tcp_ip_abort_interval
http://www.hpuxtips.es/?q=node/53
AIX : rto_low, rto_high, rto_length, rto_limit
http://www-903.ibm.com/kr/event/download/200804_324_swma/socket.pdf
如果您想及早检测到死连接,您应该为 tcp_retries2 设置较低的值(默认为 15),但这不是我已经说过的精确时间。
此外,目前您不能只为单个套接字设置这些值。这些是全局内核参数。
有一些尝试为单套接字应用 tcp 重传套接字选项(http://patchwork.ozlabs.org/patch/55236/),但我认为它没有应用于内核主线。我在系统头文件中找不到这些选项定义。
作为参考,您可以通过如下所示的“netstat --timers”监控您的 keepalive 套接字选项。
https://stackoverflow.com/questions/34914278
netstat -c --timer | grep "192.0.0.1:43245 192.0.68.1:49742"
tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (1.92/0/0)
tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (0.71/0/0)
tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (9.46/0/1)
tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (8.30/0/1)
tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (7.14/0/1)
tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (5.98/0/1)
tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (4.82/0/1)
另外,当keepalive超时发生时,你可以根据你使用的平台,遇到不同的返回事件,所以你不能仅仅通过返回事件来决定死连接状态。
例如,HP 返回 POLLERR 事件,而 AIX 在发生 keepalive 超时时仅返回 POLLIN 事件。
届时您将在 recv() 调用中遇到 ETIMEDOUT 错误。
在最近的内核版本中(自 2.6.37 起),您可以使用 TCP_USER_TIMEOUT 选项会很好地工作。此选项可用于单套接字。