在需要长连接的网络通信程序中,经常需要心跳检测机制,来实现检测对方是否在线或者维持网络连接的需要。这一机制是在应用层实现的,对应的,在TCP协议中,也有类似的机制,就是TCP保活机制。
一、为什么需要保活机制?
设想这种情况,TCP连接建立后,在一段时间范围内双发没有互相发送任何数据。思考以下两个问题:
- 怎么判断对方是否还在线。这是因为,TCP对于非正常断开的连接系统并不能侦测到(比如网线断掉)。
- 长时间没有任何数据发送,连接可能会被中断。这是因为,网络连接中间可能会经过路由器、防火墙等设备,而这些有可能会对长时间没有活动的连接断掉。
基于上面两点考虑,需要保活机制。
二、TCP保活机制的实现(Linux)
保活机制是由一个保活计时器实现的。当计时器被激发,连接一段将发送一个保活探测报文,另一端接收报文的同时会发送一个ACK
作为响应。
具体实现上有以下几个相关的配置:
- 保活时间:默认7200秒(2小时)
- 保活时间间隔:默认75秒
- 保活探测数:默认9次
查看Linux系统中TCP保活机制对应的系统配置如下(不同系统实现可能不同):
sl@Li:/proc/sys/net/ipv4$ cat tcp_keepalive_time
7200
sl@Li:/proc/sys/net/ipv4$ cat tcp_keepalive_intvl
75
sl@Li:/proc/sys/net/ipv4$ cat tcp_keepalive_probes
9
连接中启动保活功能的一端,在保活时间内连接处于非活动状态,则向对方发送一个保活探测报文,如果收到响应,则重置保活计时器,如果没有收到响应报文,则经过一个保活时间间隔后再次向对方发送一个保活探测报文,如果还没有收到响应报文,则继续,直到发送次数到达保活探测数,此时,对方主机将被确认为不可到达,连接被中断。
TCP保活功能工作过程中,开启该功能的一端会发现对方处于以下四种状态之一:
- 对方主机仍在工作,并且可以到达。此时请求端将保活计时器重置。如果在计时器超时之前应用程序通过该连接传输数据,计时器再次被设定为保活时间值。
- 对方主机已经崩溃,包括已经关闭或者正在重新启动。这时对方的TCP将不会响应。请求端不会接收到响应报文,并在经过保活时间间隔指定的时间后超时。超时前,请求端会持续发送探测报文,一共发送保活探测数指定次数的探测报文,如果请求端没有收到任何探测报文的响应,那么它将认为对方主机已经关闭,连接也将被断开。
- 客户主机崩溃并且已重启。在这种情况下,请求端会收到一个对其保活探测报文的响应,但这个响应是一个重置报文段
RST
,请求端将会断开连接。 - 对方主机仍在工作,但是由于某些原因不能到达请求端(例如网络无法传输,而且可能使用ICMP通知也可能不通知对方这一事实)。这种情况与状态2相同,因为TCP不能区分状态2与状态4,结果是都没有收到探测报文的响应。
理解了上面的实现,就可以发现其存在以下两点主要弊端:
- 在出现短暂的网络错误的时候,保活机制会使一个好的连接断开;
- 保活机制会占用不必要的带宽;
所以,保活机制是存在争议的,主要争议之处在于是否应在TCP协议层实现,有两种主要观点:其一,保活机制不必在TCP协议中提供,而应该有应用层实现;其二,认为大多数应用都需要保活机制,应该在TCP协议层实现。
保活功能在默认情况下是关闭的。没有经过应用层的请求,Linux系统不会提供保活功能。