5

我使用 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|EPOLLHUP 在套接字上退出 Epoll 等待没有问题。

但是,如果我尝试向套接字写入很多内容,直到我得到 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的通信时,在第一种情况下(阅读)我会在几秒钟内收到一次确认请求。但在第二种情况下,我根本没有检测到。

4

3 回答 3

14

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

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

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

于 2013-05-07T07:12:39.030 回答
0

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

1)根据this document,这是在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 支持。

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

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

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)确保您没有在超时参数中指定-1,epoll_wait()因为它会导致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

于 2013-05-10T20:20:14.067 回答
0

即使您已经为您的应用程序套接字设置了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 选项会很好地工作。此选项可用于单插槽。

于 2016-01-25T06:37:03.153 回答