在TCP链接建立之后,如果应用程序或者上层协议一直不发送数据,或者隔很长时间才发送一次数据,当链接很久没有数据报文传输时如何去确定对方还在线,到底是掉线了还是确实没有数据传输,链接还需不需要保持,这种情况在TCP协议设计中是需要考虑到的。
TCP协议通过keepalive这种巧妙的方式去解决这个问题,其原理就是TCP内嵌的一个心跳包。当超过一段时间之后,TCP自动发送一个数据为空的报文给对方,如果对方回应了这个报文,说明对方还在线,链接可以继续保持,如果对方没有报文返回,并且重试了多次之后则认为链接丢失,没有必要保持链接。
tcp_keepalive_time
一个连接需要TCP开始发送keepalive探测数据包之前的空闲时间,以秒为单位。
tcp_keepalive_probes
发送TCP keepalive探测数据包的最大数量,默认是9.如果发送9个keepalive探测包后对端仍然没有响应,就关掉这个连接。
tcp_keepalive_intvl
发送两个TCP keepalive探测数据包的间隔时间,默认是75秒。
下面是Linux默认值,可以看出在默认参数下,若tcp对端不通知就掉线,需要经过7200+9*75=7875秒=131.25分钟=2.1875小时才能确认对方掉线。
$ sysctl -a | grep keepalive
net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_intvl = 75
对于服务器端发送的keepalive包,client端有三种情况:
1. client 端仍然存在,网络连接状况良好。此时 client 端会返回一个 ACK 。server 端接收到 ACK 后重置计时器(复位存活定时器),在 2 小时后再发送探测。如果 2 小时内连接上有数据传输,那么在该时间基础上向后推延 2 个小时。
2. 客户端异常关闭,或是网络断开。在这两种情况下, client 端都不会响应。服务器没有收到对其发出探测的响应,并且在一定时间后重复发送 keep-alive packet ,并且重复发送一定次数。重复发送次数后没有收到响应,则会进入TIME_WAIT状态,此状态下等待2*MSL时间后连接关闭。MSL,即Maximum Segment Lifetime,是一个数据分片(报文)在网络中能够生存的最长时间,在RFC 793中定义MSL通常为2分钟。
在Linux系统中查看MSL值,默认为1分钟,如下图:
bash-4.1$ cat /proc/sys/net/ipv4/tcp_fin_timeout
60
3. 客户端曾经崩溃,但已经重启。这种情况下,服务器将会收到对其存活探测的响应,但该响应是一个复位,从而引起服务器对连接的终止。
减少TIME_WAIT连接数量,编辑/etc/sysctl.conf
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_timestamps = 1 开启tw_recylce和tw_reuse一定需要timestamps的支持
net.ipv4.tcp_max_tw_buckets = 3000 #表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息。
执行 sysctl -p 使之生效。
执行sysctl -a | grep tcp_tw 查看现在的设置。