在TCP简介文章中介绍过,TCP是面向连接的协议,因此需要对连接进行管理,但是连接管理不止包括连接的建立和释放,还需要检测连接的双方正常,及TCP保活机制要做的内容。本篇文章将会首先介绍什么保活机制以及为什么需要保活机制,然后介绍保活机制的作用,最后介绍保活机制的原理。
保活机制介绍
保活机制是一种在不影响数据流内容的情况下探测对方是否存活的方式。
保活机制不是TCP规范中的一部分。原因主要有以下三点。
- 在出现短暂的网络错误的时候,保活机制会使一个好的连接断开。
- 保活机制会占用不必要的带宽。
- 在按流量计费的情况下会在互联网上花掉更多的钱。
然而,大部分TCP的实现方,都提供了保活机制。保活功能在默认情况下是关闭的,TCP连接的任何一端都可以请求打开这一功能。保活功能可以被设置在连接的一端、两端或者两端都没有。
为什么需要保活机制
双方建立交互的连接,并不是一直存在数据交互,有些连接会在数据交互完毕后,主动释放连接,而有些不会,那么在长时间无数据交互的时间段内,交互双方都有可能出现掉电、死机、异常重启等各种意外,当这些意外发生之后,这些TCP连接并未来得及正常释放,那么,连接的另一方并不知道对端的情况,它会一直维护这个连接,长时间的积累会导致非常多的半打开连接,造成端系统资源的消耗和浪费,为了解决这个问题,在传输层可以利用TCP的保活报文来实现。
有了保活机制可以解决下面俩个问题
- 客户端和服务器需要了解什么时候与对方断开连接
- 在没有数据交换的情况下,需要通过连接保持一个最小的数据流,从而保证连接不被断开。
TCP保活机制的作用
根据上面介绍的保活机制解决的主要问题,可以得出保活机制使用的主要场景。
探测连接的对端是否存活
在应用交互的过程中,可能存在以下几种情况:
- 客户端或服务器端意外断电、死机、崩溃、重启
- 中间网络已经中断,而客户端与服务器端并不知道
利用保活探测功能,可以探知这种对端的意外情况,从而保证在意外发生时,可以释放半打开的TCP连接。
防止中间设备因超时删除连接相关的连接表
中间设备如防火墙等,会为经过它的数据报文建立相关的连接信息表,并为其设置一个超时时间的定时器,如果超出预定时间,某连接无任何报文交互的话,中间设备会将该连接信息从表中删除,在删除后,再有应用报文过来时,中间设备将丢弃该报文,从而导致应用出现异常,这个交互的过程大致如下图所示:
这种情况在有防火墙的应用环境下非常常见,这会给某些长时间无数据交互但是又要长时间维持连接的应用(如数据库)带来很大的影响,为了解决这个问题,应用本身或TCP可以通过保活报文来维持中间设备中该连接的信息,(也可以在中间设备上开启长连接属性或调高连接表的释放时间来解决,但是,这个影响可能较大,有机会再针对这个做详细的描述,在此不多说)。
实现
主要原理是在一段时间(称为保活时间,keepalivetime)内连接处于非活动状态,及双方未交换数据,开启保活功能的一端将向对方发送一个保活探测报文。如果发送端没有收到响应报文,那么经过一个已经提前配置好的保活时间间隔(keepaliveinterval),将继续发送保活探测报文,直到发送探测报文的次数达到保活探测数(keepaliveprobe),这时对方主机将被确认为不可到达,连接也将被中断。
TCP保活的交互过程大致如下图所示:
保活探测报文是一个空报文段(或1个字节),序列号等于对方主机发送的ACK报文的最大序列号减1。因为这一序列号的数据段已经被成功接收,所以不会对到达的报文段造成影响,但探测报文返回的响应可以确定连接是否仍在工作。接收方收到该报文以后,会认为是之前丢失的报文,所以不会添加进数据流中,但是仍然要发送一个ACK确认。同时探测报文以及响应报文丢失后都不会自动重传。因此就需要探测方进行主动重传,来解决丢失的问题,所以要指定保活探测数。
在linux系统中,定义了三个变量来描述保活时间,保活时间间隔,保活探测次数。
名称 | 作用 | 默认值 |
---|---|---|
tcp_keepalive_time | 保活时间,未发送数据多长时间后开启保活机制 | 7200秒 |
tcp_keepalive_intvl | 保活时间间隔 | 75秒 |
tcp_keepalve_probes | 保活探测数 | 9 |
保活结果 |
TCP保活功能工作过程中,开启该功能的一端会发现对方处于以下四种状态之一:
- 对方主机仍在工作,并且可以到达。对方的TCP响应正常,并且请求端也知道对方在正常工作。请求端将保活计时器重置(重新设定为保活时间值)。如果在计时器超时之前有应用程序通过该连接传输数据,那么计时器将再次被设定为保活时间值
- 对方主机已经崩溃,包括已经关闭或者正在重新启动。这时对方的TCP将不会响应。 请求端不会接收到响应报文,并在经过保活时间间隔指定的时间后超时。超时前,请求端会持续发送探测报文,一共发送保活探测数指定次数的探测报文,如果请求端没有收到任何探测报文的响应,那么它将认为对方主机已经关闭,连接也将被断开
- 客户主机崩溃并且已重启。在这种情况下,请求端会收到一个对其保活探测报文的响应,但这个响应是一个重置报文段,请求端将会断开连接
- 对方主机仍在工作,但是由于某些原因不能到达请求端(例如网络无法传输,而且可能使用ICMP通知也可能不通知对方这一事实)。这种情况与状态2相同,因为TCP不能区分状态2与状态4,结果都是没有收到探测报文的响应
请求端不必担心对方主机正常关闭然后重启(不同于主机崩溃)的情况。当系统关机时,所有的应用进程也会终止(即对方的进程),这会使对方的TCP发送一个FIN。请求端接收 到FIN后,会向请求端进程报告文件结束,并在检测到该状态后退出
在第1种情况下,请求端的应用层不会觉察到保活探测的进行(除非请求端应用层激活保活功能)。一切操作均在TCP层完成,因此这一过程对应用层是透明的,直至第2,3,4种情况中的某种情况发生。在这三种情况中,请求端的应用层将收到一个来自其TCP层的差错报告(通常请求端已经向网络发出了读操作请求,并且等待来自对方的数据。如果保活功能返回了一个差错报告,则该差错报告将作为读操作请求的返回值返回给请求端)。在第2种情况下,差错是诸如“连接超时”之类的信息,而在第3种情况下则为“连接被对方重置”。第4种情况可能是连接超时,也可能是其他的错误信息
TCP保活可能带来的问题
中间设备因大量保活连接,导致其连接表满
网关设备由于保活问题,导致其连接表满,无法新建连接(XX局网闸故障案例)或性能下降严重
正常连接被释放
当连接一端在发送保活探测报文时,中间网络正好由于各种异常(如链路中断、中间设备重启等)而无法将该保活探测报文正确转发至对端时,可能会导致探测的一方释放本来正常的连接,但是这种可能情况发生的概率较小,另外,一般也可以增加保活探测报文发生的次数来减小这种情况发生的概率和影响。