一、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状态,至此客户端也完成连接的关闭
在 TCP 连接关闭过程中,主动发起关闭连接操作的一方才有 TIME_WAIT 状态
二、为什么挥手需要四次?
- 关闭连接时,客户端向服务端发送FIN时,仅仅表示客户端不在发送数据了,但是还能接收数据
- 服务端收到客户端的FIN报文时,先回一个ACK应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送FIN报文给客户端来表示同意现在关闭连接
三、第一次挥手丢失了,会发生什么?(超时重传)
每一次重传的时间为上一次超时时间的2倍
四、第二次挥手丢失了,会发生什么?
在上一篇文章提到过,ACK报文是不会重传的,所以如果服务端的第二次挥手丢失了,客户端就会触发超时重传机制,重传FIN报文(第一次挥手)
但是注意,如果主动关闭方使用shutdown函数关闭连接,指定了只关闭发送方向,而接受方向并没有关闭,那么意味着主动关闭还是可以接收数据的
此时,如果主动关闭方一直没收到第三次挥手,那么主动关闭方的连接将会一直处于FIN_WAIT_2状态
五、第三次挥手丢失了,会发生什么?
当服务端(被关闭方)收到客户端(主动关闭方)的FIN报文后,内核会自动回复ACK,同时连接处于CLOSE_WAIT状态,顾名思义,他表示等待应用进程调用close函数关闭连接
此时,内核是没有权力替代进程关闭连接,必须由进程主动调用close函数来触发服务端发送FIN报文
服务端处于CLOSE_WAIT状态时,调用了close函数,内核就会发出FIN报文,同时连接进入LAST_ACK状态,等待客户端返回ACK来确认连接关闭
若收不到这个ACK,服务端就会重发FIN报文
具体过程:
- 当服务端重传第三次挥手报文的次数达到了重传最大次数,于是再等待一段时间,如果还是没能收到客户端的第四次挥手(ACK报文),那么服务端就会断开连接
- 客户端因为通过close函数关闭连接的,处于FIN_WAIT_2状态是有时长限制的,如果限制时间内还是没能收到服务端的三次挥手(FIN报文),那么客户端将会断开连接
六、第四次挥手丢失了,会发生什么?
当客户端收到服务端的第三次挥手的FIN报文后,就会回ACK报文,也就是第四次挥手,此时客户端连接进入TIME_WAIT状态。
在Linux系统,TIME_WAIT状态会持续到2MSL后才会进入关闭状态
如果第四次挥手的ACK报文没有到达服务端,服务端就会重发FIN报文
具体过程:
- 当服务端重传第三次挥手报文达到最大重传次数,于是再等待一段时间,如果还是没能收到客户端的第四次挥手(ACK报文),那么服务端就会断开连接
- 客户端在收到第三次挥手后,就会进入TIME_WAIT状态,开启时长为2MSL的定时器,如果途中再次收到第三次挥手(FIN报文)后,就会重置定时器,当等待2MSL时长后,客户端就会断开连接