客户端突然出现故障
客户端出现故障指的是客户端的主机发生了宕机或者断电的场景。发生这种情况的时候,如果服务端一直不会发送数据给客户端,那么服务端是永远无法感知到客户端宕机这件事的,也就是服务端的TCP连接将一直处于ESTABLISH 状态,占用着资源。
为了避免这种情况,TCP有一个保活机制。
原理:
定义了一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP保活机制会开始作用,每隔一个时间间隔,发送一个探测报文,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的TCP连接已经死亡,系统内核将错误信息通知给上层应用程序。
在linux内核可以有对应的参数可以设置保活时间、保活探测的次数、保活探测的时间间隔,默认值:
net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75
net.ipv4.tcp_keepalive_probes=9
- tcp_keepalive_time =7200:表示保活时间是7200秒(2小时),也就是2小时内如果没有任何连接相关的活动,则会启动保活机制
- tcp_keepalive_intvl = 75:表示每次检测时间间隔75秒
- tcp_keepalive_probes=9:表示检测9次无响应,认为对方是不可达的,从而中断本次连接
也就是说在Linux系统中,最少需要经过2小时11分15秒才能够发现一个死亡连接
PS:应用程序若想使用TCP保活机制需要通过socket接口设置SO_KEEPALIVE 选项才能够生效,如果没有设置那么就无法使用TCP保活机制。
如果开启了TCP保活机制就要考虑以下几种情况:
- 第一种,对端程序正常工作。当TCP保活的探测报文发送给对端,对端会正常响应,这样TCP保活时间会被重置,等待下一个TCP保活时间的到来。
- 第二种,对端主机宕机并重启。当TCP保活的探测报文发送给对端后,对端是可以响应的,但由于没有该连接的有效信息,会产生一个RST报文 ,这样很快就会发现TCP连接已经被重置。
- 第三种,对端主机宕机(注意不是进程崩溃,进程崩溃后操作系统会在回收进程资源的时候,会发送FIN报文,而主机宕机则是无法感知的,所以需要TCP保活机制来探测对方是不是发生了主机宕机),或对端主机由于其他原因导致报文不可达。当TCP保活的探测报文发送给对端后没有响应,连续几次,达到保活探测次数后,TCP会报告该TCP连接已经死亡。
TCP保活的这个机制检测时间有点长,可以自己在应用层实现一个心跳机制。
比如,web服务软件一般都会提供== keepalive_timeout == 参数,指定HTTP长连接的超时时间。如果设置了HTTP长连接的超时时间是60秒,web服务软件就会启动一个定时器,如果客户端在完成一个HTTP请求后,在60秒内都没有再发起请求,定时器的时间一到,就会触发回调函数来释放该连接 。
服务端进程崩溃
TCP的连接信息是由内核维护的,所以当服务器的进程崩溃后,内核需要回收该进程的所有TCP资源,于是内核会发送第一次握手FIN报文,后续的挥手过程也是在内核完成的,并不需要进程的参与,所以即使服务端的进程退出了,还是能与客户端完成TCP四次挥手的过程