TCP四次挥手过程
TCP断开连接是通过四次挥手实现的,双方都可以主动断开连接,断开连接后主机中的资源将被释放,四次挥手的过程如下:
- 客户端打算关闭连接时,会发送一个TCP首部FIN标志位为1的报文,也就是FIN报文,之后客户端进入FIN_WAIT_1状态。
- 服务端收到该报文后,就向客户端发送ACK应答报文,接着服务端进入CLOSE_WAIT状态。
- 客户端收到服务端的ACK应答报文后,之后进入FIN_WAIT_2状态。
- 等待服务端处理完数据后,也向客户端发送FIN报文,之后服务端进入LAST_ACK状态。
- 客户端收到服务端的FIN报文后,回一个ACK应答报文,之后进入TIME_WAIT状态
- 服务端收到了ACK应答报文后,就进入了CLOSE状态,至此服务端已经完成连接的关闭。
- 客户端在经过2MSL一段时间后,自动进入了CLOSE状态,至此客户端也完成了连接的关闭。
可以看到,每个方向都需要一个FIN和一个ACK,因此通常被称为四次握手
PS:主动关闭连接的,才有TIME_WAIT状态。
为什么需要四次挥手?
回顾一下四次挥手双方FIN包的过程就能理解为什么需要四次挥手了。
- 关闭连接时,客户端向服务端发送FIN时,仅仅表示客户端不再发送数据了但是还能接收数据。
- 服务端收到客户端的FIN报文时,先回一个ACK应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送FIN报文给客户端来表示同意现在关闭连接。
从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的ACK和FIN一般会分开发送,因此是需要四次挥手
第一次挥手丢失
当客户端(主动关闭方)调用close函数后,就会向服务端发送FIN报文,试图与服务端断开连接,此时客户端的连接进入到FIN_WAIT_1状态
正常情况下,如果能及时收到服务端(被动关闭方)的ACK,则会很快变为FIN_WAIT2状态。
如果第一次挥手丢失了,那么客户端迟迟收不到被动方的ACK的话,也就会触发超时重传机制,重传FIN报文,重发次数tcp_orphan_retries参数控制。
当客户端重传FIN报文的次数超过tcp_orphan_retries后,就不再发送FIN报文,则会再等待一段时间(时间为上一次超时时间的2倍),如果还是没能收到第二次挥手,那么直接进入到close状态.
具体过程:
- 当客户端超时重传3次FIN报文后,由于tcp_orphan_retries为3,已达到最大重传次数,于是再等待一段时间(时间为上次超时时间的2倍),如果还是没能收到服务端的第二次挥手(ACK报文),那么客户端就会断开连接
第二次挥手丢失
当服务端收到客户端的第一次挥手后,就会先回一个ACK确认报文,此时服务端的连接进入到CLOSE_WAIT状态。
由于ACK报文不会重传,所以如果服务端的第二次挥手丢失了,客户端就会触发超时重传。重传FIN报文,直到服务端的第二次挥手,或者达到最大重传次数。
具体过程:
- 当客户端超时重传2次FIN报文后,由于tcp_orphan_retries为2,已达到最大超时重传次数,于是再等待一段时间(时间为上一次超时时间的2倍)如果还是没能收到服务端的第二次挥手(ACK报文),那么客户端就会断开连接。
当客户端收到第二次挥手后,会处于FIN_WAIT_2状态,这个状态需要等待服务端第三次挥手,也就是服务端的FIN报文。
对于close函数关闭的连接,由于无法再发送和接收数据,所以FIN_WAIT_2状态不可以持续太久,而tcp_fin_timeout控制了这个状态下连接的持续时长,默认值是60秒
这意味着对于调用close关闭的连接,如果在60秒后还没有收到FIN报文,客户端(主动关闭方)的连接就会直接关闭:
但是,如果主动关闭方使用shutdown函数关闭连接,指定了只关闭发送方向,而接受方向并没有关闭,那么意味着主动关闭方还是可以接收数据的。
此时,如果主动关闭方一直没收到第三次挥手,那么主动方的连接会一直处于FIN_WAIT2状态(
tcp_fin_timeout
无法控制 shutdown 关闭的连接)
第三次挥手丢失
当服务端收到客户端的FIN报文后,内核会自动回复ACK,同时连接处于CLOSE_WAIT状态,它表示等待应用程序调用close函数关闭连接。
此时内核是没有权利代替进程关闭连接诶,必须由进程主动调用close函数来触发服务端发送FIN报文。
服务端处于CLOSE_WAIT状态时,调用了close函数,内核就会发出FIN报文,同时连接进入LAST_ACK状态,等待客户端返回ACK来确认连接关闭。
如果迟迟收不到这个ACK,服务端就会重发FIN报文,重发次数依然由tcp_orphan_retries参数控制,这与客户端重发FIN报文的重传次数控制方式是一样的
具体过程:
- 当服务端重传第三次挥手报文的次数达到了3次后,由于tcp_orphan_retries 为3,达到了重传最大次数,于是再等待一段时间(时间为上次超时时间的2倍),如果还是没能收到客户端的第四次挥手(ACK报文),那么服务端就会断开连接。
第四次挥手丢失
当客户端收到服务端的第三次挥手的FIN报文后,就会回ACK报文,也就是第四次挥手,此时客户端连接进入TIME_WAIT状态。
在linux系统中,TIME_WAIT状态会维持2MSL后才会进入关闭状态
然后,服务端(被动关闭方)没有收到ACK报文前,还是处于LAST_ACK状态。
如果第四次挥手的ACK报文没有到达服务端,服务端就会重发FIN报文,重发次数依然由tcp_orphan_retries
参数控制。
具体过程:
- 当服务端重传第三次挥手报文到达2时,达到了最大重传次数,于是再等待一段时间(时间为上次超时时间的2倍),如果还是没能收到客户端的第四次挥手(ACK报文),那么服务端就会断开连接
- 客户端在收到第三次挥手后,就会进入TIME_WAIT状态,开启时长为2MSL的定时器,如果途中再次收到第三次挥手(FIN报文)后,就会重置定时器,当等待2MSL时长后,客户端就会断开连接