2013-05-01 65 views
4

我使用TCP Keep-Alive选项检测死连接。它与连接是否正常使用阅读插座:使用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) 

与EPOLLIN是Epoll等待退出| 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

TCP保持活动状态在两小时后运行,而不是几秒钟。你确定你不是指'阅读超时'吗? – EJP 2013-05-01 22:54:39

+0

@EJP有修改保持活动时间的选项 - 这就是我在setsockopts中做的事情......以便在几秒钟内检测到问题。因此,如果我拔掉远程端的电缆,并且读取轮询插座(第一个示例),则会在几秒钟内检测到断开连接...但第二种情况不会。 – Artyom 2013-05-02 03:59:23

+0

调用epoll_ctl(...)时是否检查错误? – user2343912 2013-05-06 08:38:42

回答

13

如果在传输所有数据之前拉起网络连接,则连接不是空闲的,因此在某些实现中,保活定时器不会启动。 (请记住,keepalive不是TCP规范的一部分,因此如果它完全不一致地执行)。一般来说,由于指数退避和大量重试(tcp_retries2默认值为15)的组合,可能需要在Keepalive定时器启动之前,传输重试超时至多30分钟。

解决方法(如果有的话)取决于您正在使用的特定TCP实施。一些较新版本的Linux(2011年1月4日发布的内核版本2.6.37)实现TCP_USER_TIMEOUT。更多信息here

通常的建议是在应用程序级别实现通信超时,而不是使用基于TCP的keepalive。例如参见HTTP Keep-Alive

+0

有关如何在Linux上运行的任何解决方法? – Artyom 2013-05-08 14:49:09

+0

也可能有办法定义tcp_retries的数量? – Artyom 2013-05-08 14:49:42

+0

tcp_retries2是为所有连接设置的内核级变量,而不是每个连接变量,因此您可能不想惹它。 – 2013-05-08 17:14:56

0

我想谈谈的几点....

1)根据this document,这里是在Linux中使用的keepalive什么必要:

Linux有内置支持Keepalive。您需要启用TCP/IP 网络才能使用它。您还需要procfs支持和sysctl 支持才能在运行时配置内核参数。

的过程涉及使用存活三个用户驱动的变量:

tcp_keepalive_time 

>和第一存活探测发送的最后一个数据包之间的时间间隔(简单的ACK不 考虑的数据);连接 被标记后,需要保持活动,该计数器不使用任何进一步

tcp_keepalive_intvl 

>的后续存活探测之间,间隔不管 什么的连接在此期间已交换

tcp_keepalive_probes 

>在考虑 连接死亡并通知应用层之前发送未确认探测的数量

请记住,Keepalive支持(即使在内核中配置)不是Linux中的默认行为,而是 。 程序必须使用setsockopt接口请求存活控制 的套接字。有 相对较少的程序实现keepalive,但您可以轻松地添加 Keepalive支持大多数人按照本文后面解释说明 。

尝试查看当前系统中这些变量的当前值,以确保它们是正确的或有意义的。大胆的亮点是我的,看来你正在这样做。

我假设这些变量的值以毫秒为单位,但不确定,请仔细检查。

tcp_keepalive_time 

我期望的值围绕意味着什么“最后发送的数据包后尽快,发送第一探针”

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); 

超时参数指定毫秒 epoll_wait()将块的最小数目。 (此间隔将四舍五入至系统时钟粒度的 ,并且内核调度延迟意味着阻塞时间间隔可能会超出一小部分。)指定-1的超时 会导致epoll_wait()无限期阻塞,同时指定 超时等于零会导致epoll_wait()立即返回,如果没有事件可用,则甚至会返回 。

从手册页http://linux.die.net/man/2/epoll_wait

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)如果你想及早发现死连接设置较低的值,但它的我已经没有精确的时间了说过。 另外,目前您不能仅为单个套接字设置这些值。这些是全局内核参数。 有一些尝试为单插槽应用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超时ocurrs,可满足您不同的返回事件取决于您使用的平台,所以你必须不返回事件决定只死连接状态。 例如,当Keepalive超时发生时,HP返回POLLERR事件,并且AIX仅返回POLLIN事件。 您当时会在recv()调用中遇到ETIMEDOUT错误。

在最近的内核版本(自2.6.37开始)之后,可以使用TCP_USER_TIMEOUT选项将很好的工作。该选项可用于单个套接字。