繁体   English   中英

在写入被阻止的套接字上使用TCP Keep-Alive获取断开连接通知

[英]Getting disconnection notification using TCP Keep-Alive on write blocked socket

我使用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退出而没有问题。

但是,如果我尝试向socket写入很多东西,直到我得到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请求。 但在第二种情况下,我根本检测不到。

如果在传输所有数据之前拉网络连接,则连接不是空闲的,因此在某些实现中,保持活动计时器不会启动。 (请记住,keepalive不是TCP规范的一部分,因此,如果有的话,它实现的方式不一致。)通常,由于指数退避和大量重试( tcp_retries2默认为15)的组合,它可能需要在keepalive计时器启动之前,传输重试最多需要30分钟才能超时。

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

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

即使您已经为应用程序套接字设置了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超时时,您可以根据您使用的平台遇到不同的返回事件,因此您不能仅通过返回事件来确定死连接状态。 例如,当发生keepalive超时时,HP返回POLLERR事件,AIX仅返回POLLIN事件。 那时您将在recv()调用中遇到ETIMEDOUT错误。

在最近的内核版本(自2.6.37)中,您可以使用TCP_USER_TIMEOUT选项将运行良好。 此选项可用于单个插槽。

我想谈谈几点......

1)根据这个文档 ,这里是在Linux中使用keepalive的必要条件:

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

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

 tcp_keepalive_time 

>发送的最后一个数据包(简单ACK不被视为数据)与第一个keepalive探测之间的间隔; 在连接被标记为需要保持连接后,此计数器不再使用

 tcp_keepalive_intvl 

>后续keepalive探测之间的间隔,无论在此期间连接的交换情况如何

 tcp_keepalive_probes 

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

请记住,即使在内核中配置了keepalive支持,也不是Linux中的默认行为。 程序必须使用setsockopt接口请求对其套接字进行keepalive控制。 实现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()的timeout参数中指定-1,因为它会导致epoll_wait()无限期地阻塞。

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

timeout参数指定epoll_wait()将阻止的最小毫秒数。 (此间隔将向上舍入到系统时钟粒度,并且内核调度延迟意味着阻塞间隔可能会超出一小部分。)指定超时为-1会导致epoll_wait()无限期地阻塞,同时指定超时等于零使epoll_wait()立即返回,即使没有可用的事件。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM