一、无数据传输的场景
1. 客户端主机崩溃
客户端主机崩溃了,服务端是无法感知到的。如果服务端没有开启 TCP 的保活机制,又没有进行数据交互,那么服务端的 TCP 连接将会一直处于 ESTABLISHED 连接状态,直到服务端重启进程。
所以,在没有 TCP 保活机制且双方不传输数据的情况下,一方的 TCP 连接处于 ESTABLISHED 状态,并不代表另一方的连接还一定正常。
2. 客户端进程崩溃
TCP 连接信息是由内核维护的,所以当服务端的进程崩溃后,内核需要回收该进程的所有 TCP 连接资源,于是内核会发送第一次挥手 FIN 报文,后续的挥手过程也都是在内核完成,并不需要进程的参与。所以即使服务端的进程退出了,还是能和客户端完成 TCP 四次挥手的过程。
我曾经做过实验,使用 kill -9
来模拟进程崩溃的情况,发现在 kill 掉进程后,服务端会发送 FIN 报文,与客户端进行四次挥手。
所以,即使没有开启 TCP 保活机制,且双方也没有数据交互的情况下,如果其中一方的进程发生了崩溃,这个过程操作系统是可以感知到的,操作系统会发送 FIN 报文给对方,然后与对方进行 TCP 四次挥手。
二、有数据传输的场景
在数据传输的场景下,有两种情况:
- 客户端主机宕机,又迅速重启,会发生什么?
- 客户端主机宕机,一直没有重启,会发生什么?
1. 客户端主机宕机,又迅速重启
在客户端主机宕机后,服务端向客户端发送的报文会得不到任何的响应,在一定时长后,服务端就会触发超时重传机制,重传未得到响应的报文。
服务端重传报文的过程中,客户端主机重启完成后,客户端的内核就会接收重传的报文,然后根据报文的信息传递给对应的进程:
- 如果客户端主机上没有进程绑定该 TCP 报文的目标端口号,那么客户端内核就会回复 RST 报文,重置该 TCP 连接;
- 如果客户端主机上有进程绑定该 TCP 报文的目标端口号,由于客户端主机重启后,之前的 TCP 连接的数据结构已经丢失了,客户端内核里协议栈会发现找不到该 TCP 连接的 socket 结构体,于是就会回复 RST 报文,重置该 TCP 连接。
所以,只要有一方重启完成后,收到之前 TCP 连接的报文,都会回复 RST 报文,以断开连接。
2. 客户端主机宕机,一直没有重启
这种情况,服务端超时重传报文的次数达到一定阈值后,内核就会判定出该 TCP 有问题,然后通过 Socket 接口告诉应用程序该 TCP 连接出问题了,于是服务端的 TCP 连接就会断开。
那么 TCP 的数据报文具体重传几次呢?在 Linux 系统中,提供了配置项:/proc/sys/net/ipv4/tcp_retries2
,默认值是 15。也就是说在 TCP 连接建立的情况下,超时重传的最大次数为 15。
不过 tcp_retries2
设置了 15 次,并不代表 TCP 超时重传了 15 次才会通知应用程序终止该 TCP 连接。内核会根据 tcp_retries2
设置的值,计算出一个超时时间。如果重传的间隔超过了这个超时时间,则认为超过了阈值,就会停止重传,然后就会断开 TCP 连接。