我们知道TCP连接关闭时,需要连接的两端中的某一方发起关闭动作,如果某一方突然断电,另外一端是无法知道的。tcp的keep_alive就是用以检测异常的一种机制。
有三个参数:
1 发送心跳消息的间隔
2 未收到回复时,重试的时间间隔
3 重试的次数
如果是Linux操作系统,这三个值分别为
# 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
也就意味着每隔7200s(两个小时)发起一次keepalive的报文,如果没有回应,75秒后进行重试,最多重试9次即认为连接关闭。
这三个选项分别对应TCP_KEEPIDLE、TCP_KEEPINTL和TCP_KEEPCNT的选项值,通过setsockopt进行设置。
但是,tcp自己的keepalive有这样的一个bug:
正常情况下,连接的另一端主动调用colse关闭连接,tcp会通知,我们知道了该连接已经关闭。但是如果tcp连接的另一端突然掉线,或者重启断电,这个时候我们并不知道网络已经关闭。而此时,如果有发送数据失败,tcp会自动进行重传。重传包的优先级高于keepalive,那就意味着,我们的keepalive总是不能发送出去。 而此时,我们也并不知道该连接已经出错而中断。在较长时间的重传失败之后,我们才会知道。
为了避免这种情况发生,我们要在tcp上层,自行控制。对于次消息,记录发送时间和收到回应的时间。如果长时间没有回应,就可能是网络中断。如果长时间没有发送,就是说,长时间没有进行通信,可以自行发一个包,用于keepalive,以保持该连接的存在。