linux下使用TCP存活(keepalive)定时器

linux下使用TCP存活(keepalive)定时器

一、什么是keepalive定时器?

在一个空闲的(idle)TCP连接上,没有任何的数据流,许多TCP/IP的初学者都对此感到惊奇。也就是说,如果TCP连接两端没有任何一个进程在向对方发送数据,那么在这两个TCP模块之间没有任何的数据交换。你可能在其它的网络协议中发现有轮询(polling),但在TCP中它不存在。言外之意就是我们只要启动一个客户端进程,同服务器建立了TCP连接,不管你离开几小时,几天,几星期或是几个月,连接依旧存在。中间的路由器可能崩溃或者重启,电话线可能go down或者back up,只要连接两端的主机没有重启,连接依旧保持建立。

这就可以认为不管是客户端的还是服务器端的应用程序都没有应用程序级(application-level)的定时器来探测连接的不活动状态(inactivity),从而引起任何一个应用程序的终止。然而有的时候,服务器需要知道客户端主机是否已崩溃并且关闭,或者崩溃但重启。许多实现提供了存活定时器来完成这个任务。

存活定时器是一个包含争议的特征。许多人认为,即使需要这个特征,这种对对方的轮询也应该由应用程序来完成,而不是由TCP中实现。此外,如果两个终端系统之间的某个中间网络上有连接的暂时中断,那么存活选项(option)就能够引起两个进程间一个良好连接的终止。例如,如果正好在某个中间路由器崩溃、重启的时候发送存活探测,TCP就将会认为客户端主机已经崩溃,但事实并非如此。

存活(keepalive)并不是TCP规范的一部分。在Host Requirements RFC罗列有不使用它的三个理由:(1)在短暂的故障期间,它们可能引起一个良好连接(good connection)被释放(dropped),(2)它们消费了不必要的宽带,(3)在以数据包计费的互联网上它们(额外)花费金钱。然而,在许多的实现中提供了存活定时器。

一些服务器应用程序可能代表客户端占用资源,它们需要知道客户端主机是否崩溃。存活定时器可以为这些应用程序提供探测服务。Telnet服务器和Rlogin服务器的许多版本都默认提供存活选项。

个人计算机用户使用TCP/IP协议通过Telnet登录一台主机,这是能够说明需要使用存活定时器的一个常用例子。如果某个用户在使用结束时只是关掉了电源,而没有注销(log off),那么他就留下了一个半打开(half-open)的连接。在图18.16,我们看到如何在一个半打开连接上通过发送数据,得到一个复位(reset)返回,但那是在客户端,是由客户端发送的数据。如果客户端消失,留给了服务器端半打开的连接,并且服务器又在等待客户端的数据,那么等待将永远持续下去。存活特征的目的就是在服务器端检测这种半打开连接。
 

二、keepalive如何工作?

在此描述中,我们称使用存活选项的那一段为服务器,另一端为客户端。也可以在客户端设置该选项,且没有不允许这样做的理由,但通常设置在服务器。如果连接两端都需要探测对方是否消失,那么就可以在两端同时设置(比如NFS)。

若在一个给定连接上,两小时之内无任何活动,服务器便向客户端发送一个探测段。(我们将在下面的例子中看到探测段的样子。)客户端主机必须是下列四种状态之一:

1) 客户端主机依旧活跃(up)运行,并且从服务器可到达。从客户端TCP的正常响应,服务器知道对方仍然活跃。服务器的TCP为接下来的两小时复位存活定时器,如果在这两个小时到期之前,连接上发生应用程序的通信,则定时器重新为往下的两小时复位,并且接着交换数据。

2) 客户端已经崩溃,或者已经关闭(down),或者正在重启过程中。在这两种情况下,它的TCP都不会响应。服务器没有收到对其发出探测的响应,并且在75秒之后超时。服务器将总共发送10个这样的探测,每个探测75秒。如果没有收到一个响应,它就认为客户端主机已经关闭并终止连接。

3) 客户端曾经崩溃,但已经重启。这种情况下,服务器将会收到对其存活探测的响应,但该响应是一个复位,从而引起服务器对连接的终止。

4) 客户端主机活跃运行,但从服务器不可到达。这与状态2类似,因为TCP无法区别它们两个。它所能表明的仅是未收到对其探测的回复。

服务器不必担心客户端主机被关闭然后重启的情况(这里指的是操作员执行的正常关闭,而不是主机的崩溃)。当系统被操作员关闭时,所有的应用程序进程(也就是客户端进程)都将被终止,客户端TCP会在连接上发送一个FIN。收到这个FIN后,服务器TCP向服务器进程报告一个文件结束,以允许服务器检测这种状态。

在第一种状态下,服务器应用程序不知道存活探测是否发生。凡事都是由TCP层处理的,存活探测对应用程序透明,直到后面2,3,4三种状态发生。在这三种状态下,通过服务器的TCP,返回给服务器应用程序错误信息。(通常服务器向网络发出一个读请求,等待客户端的数据。如果存活特征返回一个错误信息,则将该信息作为读操作的返回值返回给服务器。)在状态2,错误信息类似于“连接超时”。状态3则为“连接被对方复位”。第四种状态看起来像连接超时,或者根据是否收到与该连接相关的ICMP错误信息,而可能返回其它的错误信息。
 

三、在Linux中如何使用keepalive?

three user-driven variables

Linux hasbuilt-in support for keepalive. You need to enable TCP/IP networking in orderto use it. You also need procfs support andsysctl supportto be able to configure the kernel parameters at runtime.

The proceduresinvolving keepalive use three user-driven variables:

1)   tcp_keepalive_time

the interval between the last data packetsent (simple ACKs are not considered data) and the first keepalive probe; afterthe connection is marked to need keepalive, this counter is not used anyfurther

2)   tcp_keepalive_intvl

the interval between subsequentialkeepalive probes, regardless of what the connection has exchanged in themeantime

3)   tcp_keepalive_probes

the number of unacknowledged probes to sendbefore considering the connection dead and notifying the application layer

Remember thatkeepalive support, even if configured in the kernel, is not the defaultbehavior in Linux. Programs must request keepalive control for their socketsusing the setsockopt interface. There are relatively fewprograms implementing keepalive, but you can easily add keepalive support formost of them following the instructions.

上面一段话已经说得很明白,linux内核包含对keepalive的支持。其中使用了三个参数:tcp_keepalive_time(开启keepalive的闲置时长)tcp_keepalive_intvl(keepalive探测包的发送间隔)和tcp_keepalive_probes (如果对方不予应答,探测包的发送次数);如何配置这三个参数呢?

two ways to configure keepalive parameters

There are twoways to configure keepalive parameters inside the kernel via userspacecommands:

·        procfs interface

·        sysctl interface

We mainlydiscuss how this is accomplished on the procfs interface because it's the most used,recommended and the easiest to understand. The sysctl interface, particularlyregarding the sysctl(2) syscall and not the sysctl(8) tool,is only here for the purpose of background knowledge.

The procfs interface

This interfacerequires both sysctl and procfs tobe built into the kernel, and procfs mountedsomewhere in the filesystem (usually on/proc,as in the examples below). You can read the values for the actual parametersby "catting" files in /proc/sys/net/ipv4/directory:

  # cat /proc/sys/net/ipv4/tcp_keepalive_time

  7200

  # cat /proc/sys/net/ipv4/tcp_keepalive_intvl

  75

  # cat /proc/sys/net/ipv4/tcp_keepalive_probes

  9

The first twoparameters are expressed in seconds, and the last is the pure number. Thismeans that the keepalive routines wait for two hours (7200 secs) before sendingthe first keepalive probe, and then resend it every 75 seconds. If no ACKresponse is received for nine consecutive times, the connection is marked asbroken.

Modifying thisvalue is straightforward: you need to write new values into the files. Supposeyou decide to configure the host so that keepalive starts after ten minutes ofchannel inactivity, and then send probes in intervals of one minute. Because ofthe high instability of our network trunk and the low value of the interval,suppose you also want to increase the number of probes to 20.

Here's how wewould change the settings:

  # echo 600 > /proc/sys/net/ipv4/tcp_keepalive_time

  # echo 60 > /proc/sys/net/ipv4/tcp_keepalive_intvl

  # echo 20 > /proc/sys/net/ipv4/tcp_keepalive_probes

To be sure thatall succeeds, recheck the files and confirm these new values are showing inplace of the old ones.

这样,上面的三个参数配置完毕。使这些参数重启时保持不变的方法请阅读参考文献[2]。
 

四、在程序中如何使用keepalive?

Setsockopt enable first

All you need toenable keepalive for a specific socket is to set the specific socket option onthe socket itself. The prototype of the function is as follows:

int setsockopt(int s, int level, int optname,

                 const void *optval, socklen_t optlen)

The firstparameter is the socket, previously created with the socket(2); the second one must be SOL_SOCKET,and the third must be SO_KEEPALIVE . The fourth parameter must be a booleaninteger value, indicating that we want to enable the option, while the last isthe size of the value passed before.

According to themanpage, 0 is returned upon success, and -1 is returned onerror (and errno is properly set).

three other socket options

There are alsothree other socket options you can set for keepalive when you write yourapplication. They all use the SOL_TCP levelinstead of SOL_SOCKET, and they override system-wide variablesonly for the current socket. If you read without writing first, the currentsystem-wide parameters will be returned.

·  TCP_KEEPCNT:overrides tcp_keepalive_probes

·  TCP_KEEPIDLE:overrides tcp_keepalive_time

·  TCP_KEEPINTVL:overrides tcp_keepalive_intvlint

keepAlive = 1; // 开启keepalive属性

我们看到keepalive是一个开关选项,可以通过函数来使能。具体地说,可以使用以下代码:

setsockopt(rs,SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));

上面英文资料中提到的第二个参数可以取为SOL_TCP,以设置keepalive的三个参数(具体代码参考文献[3]),在程序中实现需要头文件“netinet/tcp.h”。当然,在实际编程时也可以采用系统调用的方式配置的keepalive参数。

关于setsockopt的其他参数可以参考文献[4]。
 

五、如何判断TCP连接是否断开?[3]

当tcp检测到对端socket不再可用时(不能发出探测包,或探测包没有收到ACK的响应包),select会返回socket可读,并且在recv时返回-1,同时置上errno为ETIMEDOUT。 

在程序中设置如下:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/tcp.h>
 
int keepAlive = 1// 开启keepalive属性
int keepIdle = 60// 如该连接在60秒内没有任何数据往来,则进行探测 
int keepInterval = 5// 探测时发包的时间间隔为5 秒
int keepCount = 3// 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.
 
setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));
setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));
setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));
setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));

在程序中表现为,当tcp检测到对端socket不再可用时(不能发出探测包,或探测包没有收到ACK的响应包),select会返回socket可读,并且在recv时返回-1,同时置上errno为ETIMEDOUT.

From: http://machael.blog.51cto.com/829462/211989


Tcp是面向连接的,在实际应用中通常都需要检测连接是否还可用.如果不可用,可分为:

a. 连接的对端正常关闭.

b. 连接的对端非正常关闭,这包括对端设备掉电,程序崩溃,网络被中断等.这种情况是不能也无法通知对端的,所以连接会一直存在,浪费国家的资源.

tcp协议栈有个keepalive的属性,可以主动探测socket是否可用,不过这个属性的默认值很大.

全局设置可更改/etc/sysctl.conf,加上:

net.ipv4.tcp_keepalive_intvl = 20
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_time = 60
 

在程序中设置如下:

#include 
#include 
#include 
#include 
#include 
int keepAlive = 1; // 开启keepalive属性
int keepIdle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测 
int keepInterval = 5; // 探测时发包的时间间隔为5 秒
int keepCount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.

setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));
setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));
setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));
setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));
 

在程序中表现为,当tcp检测到对端socket不再可用时(不能发出探测包,或探测包没有收到ACK的响应包),select会返回socket可读,并且在recv时返回-1,同时置上errno为ETIMEDOUT。

猜你喜欢

转载自blog.csdn.net/zjy900507/article/details/85835574
今日推荐