TCP断开连接是通过四次挥手实现的,双方都可以主动断开连接,断开连接后主机中的资源将被释放,四次挥手的过程如下:
可以看到,每个方向都需要一个FIN和一个ACK,因此通常被称为四次握手
PS:主动关闭连接的,才有TIME_WAIT状态。
回顾一下四次挥手双方FIN包的过程就能理解为什么需要四次挥手了。
从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的ACK和FIN一般会分开发送,因此是需要四次挥手
当客户端(主动关闭方)调用close函数后,就会向服务端发送FIN报文,试图与服务端断开连接,此时客户端的连接进入到FIN_WAIT_1状态
正常情况下,如果能及时收到服务端(被动关闭方)的ACK,则会很快变为FIN_WAIT2状态。
如果第一次挥手丢失了,那么客户端迟迟收不到被动方的ACK的话,也就会触发超时重传机制,重传FIN报文,重发次数tcp_orphan_retries参数控制。
当客户端重传FIN报文的次数超过tcp_orphan_retries后,就不再发送FIN报文,则会再等待一段时间(时间为上一次超时时间的2倍),如果还是没能收到第二次挥手,那么直接进入到close状态.
具体过程:
当服务端收到客户端的第一次挥手后,就会先回一个ACK确认报文,此时服务端的连接进入到CLOSE_WAIT状态。
由于ACK报文不会重传,所以如果服务端的第二次挥手丢失了,客户端就会触发超时重传。重传FIN报文,直到服务端的第二次挥手,或者达到最大重传次数。
具体过程:
当客户端收到第二次挥手后,会处于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报文的重传次数控制方式是一样的
具体过程:
当客户端收到服务端的第三次挥手的FIN报文后,就会回ACK报文,也就是第四次挥手,此时客户端连接进入TIME_WAIT状态。
在linux系统中,TIME_WAIT状态会维持2MSL后才会进入关闭状态
然后,服务端(被动关闭方)没有收到ACK报文前,还是处于LAST_ACK状态。
如果第四次挥手的ACK报文没有到达服务端,服务端就会重发FIN报文,重发次数依然由tcp_orphan_retries
参数控制。
具体过程: