我使用TCP Keep-Alive选项来检测死连接。它适用于使用读取套接字的连接:
setsockopt(mysock,...) // set various keep alive options
epoll_ctl(ep,mysock,{EPOLLIN|EPOLERR|EPOLLHUP},)
epoll_wait -> (exits after several seconds when remove host disconnects cable)
Epoll等待在套接字上使用EPOLLIN | EPOLLHUP退出而没有问题。
然而,如果我尝试在插槽中写入很多东西,直到我获得EAGAIN然后轮询读取和写入,我在断开电缆时没有出现错误:
setsockopt(mysock,...) // set various keep alive options
while(send() != EAGAIN)
;
epoll_ctl(ep,mysock,{EPOLLIN|EPOLLOUT|EPOLERR|EPOLLHUP},)
epoll_wait -> --- Never exits!!!! even when the cable of the remove host is disconnected!!!
修改:其他信息
当我监控与wireshark的通信时,在第一种情况下(读取)我会在几秒钟内获得一次ack请求。但在第二种情况下,我根本没有发现它们。
答案 0 :(得分:13)
如果在传输所有数据之前拔出网络连接,则连接不是空闲的,因此在某些实现中,keepalive计时器不会启动。 (请记住,keepalive不是TCP规范的一部分,因此如果有的话,它实现的方式不一致。)通常,由于指数退避和大量重试的组合(tcp_retries2
默认为15 )在keepalive计时器启动之前,传输重试最多可能需要30分钟才能超时。
解决方法(如果有)取决于您使用的特定TCP实现。一些较新版本的Linux(2011年1月4日发布的内核版本2.6.37)实现了TCP_USER_TIMEOUT。更多信息here。
通常的建议是在应用程序级别实现通信超时,而不是使用基于TCP的keepalive。例如,请参阅HTTP Keep-Alive。
答案 1 :(得分:1)
即使您已经为应用程序套接字设置了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)设置较低的值,但这并不像我已经说过的那样精确。 此外,目前您无法仅为单个套接字设置这些值。那些是全局内核参数。 有一些尝试为单个套接字(http://patchwork.ozlabs.org/patch/55236/)应用tcp重新传输套接字选项,但我不认为它已应用于内核主线。我在系统头文件中找不到这些选项定义。
作为参考,您可以通过'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超时时,您可以根据您使用的平台遇到不同的返回事件,因此您不能仅通过返回事件来确定死连接状态。 例如,当发生keepalive超时时,HP返回POLLERR事件,AIX仅返回POLLIN事件。 那时你将在recv()调用中遇到ETIMEDOUT错误。
在最近的内核版本(自2.6.37开始)中,您可以使用TCP_USER_TIMEOUT选项将运行良好。此选项可用于单个插槽。
答案 2 :(得分:0)
我想谈谈几点......
1)根据this document,以下是在Linux中使用keepalive的必要条件:
Linux内置了对keepalive的支持。您需要启用TCP / IP 网络才能使用它。您还需要procfs支持和sysctl 支持能够在运行时配置内核参数。
涉及keepalive的程序使用三个用户驱动的变量:
tcp_keepalive_time
>发送的最后一个数据包之间的间隔(简单的ACK不是 考虑数据)和第一个keepalive探测器;连接后 标记为需要keepalive,此计数器不再使用
tcp_keepalive_intvl
>无论如何,后续的keepalive探测之间的间隔 在此期间连接交换了什么
tcp_keepalive_probes
>在考虑之前发送的未确认探测器的数量 连接已死并通知应用程序层
请记住,即使在内核中配置了keepalive支持,也是如此 不是Linux中的默认行为。 程序必须请求keepalive 使用setsockopt接口控制其套接字。有 实现keepalive的程序相对较少,但您可以轻松添加 按照说明为大多数人提供keepalive支持 本文稍后会解释。
尝试查看当前系统中这些变量的当前值,以确保它们正确或有意义。大胆的亮点是我的,似乎你正在这样做。
我假设这些变量的值以毫秒为单位,但不确定,请仔细检查。
tcp_keepalive_time
我希望在发送最后一个数据包之后“发送第一个探测”
,这意味着“ASAP”tcp_keepalive_intvl
我猜这个变量的值应该小于TCP关闭连接的默认时间。
tcp_keepalive_probes
这可能是制造或破坏您的申请的“神奇价值”;如果未确认探测器的数量太高,则可能是导致epoll_wait()
永不退出的原因。
该文档讨论了Linux内核版本(2.4.x,2.6.x)中的TCP keepalive的Linux实现,以及如何使用C语言编写支持TCP keepalive的应用程序。
http://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/
2)确保您没有在epoll_wait()
的超时参数中指定-1,因为它会导致epoll_wait()
无限期阻止。
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
timeout参数指定最小毫秒数 epoll_wait()将阻止。 (这个间隔将四舍五入到 系统时钟粒度和内核调度延迟意味着 阻塞间隔可能会少量超出。)指定超时 -1表示epoll_wait()无限期阻塞,同时指定a timeout等于零会导致epoll_wait()立即返回 如果没有可用的事件。