挥手是为了终止TCP连接,四次挥手是指总共要发出四个包来确认连接的断开,这一过程由客户端或者服务器端任意一方执行CLOSE来触发本文假设客户端主动触发CLOSE
TCP的四次挥手的流程图如下
- 刚开始客户端和服务端都处于
ESTABLISHED的状态
,我们这里假设客户端主动关闭,服务器被动关闭. - 首先,客户端进程发出FIN报文,并且停止发送数据,在报文中的
FIN结束标志位为1
,seq序号为u(该值为在连接状态下已经发送给服务器的数据的最后一个字节的序号+1)
,报文发送结束后,客户端就进入了 FIN-WAIT-1结束等待1的状态,此时TCP会消耗掉一个序号. - 当服务器端
收到FIN结束报文
之后,向客户端发出确认报文,其中ACK=1
,seq=v(v就是服务器端给客户端发的报文的最后一个字节的序号+1)
,ack=u+1(因为前面消耗掉了一个序号)
.发送之后服务器端就进入了 CLOSE-WAIT关闭等待状态. 该状态下,客户端不会再发送数据,但是服务器发送数据客户端依旧可以收到,相当于是一个半关闭状态 - 客户端收到服务器端的
确认报文
后,客户端就进入了FIN-WAIT-2结束等待2的状态
,等待服务器发送第三次挥手的报文. - 服务器在此期间将剩下的数据发送完以后,就向客户端发送***FIN报文***,其中
FIN=1
,ACK=1
,seq=w(w等于服务器端向客户端发送的数据的最后一个字节的序号+1)
,ack=u+1
,发送结束报文之后,服务器就进入了LAST-ACK最后确认状态,等待客户端的最终确认. - 客户端收到
连接释放报文
后,向服务器发送确认报文,其中ACK=1
,seq=u+1
,ack=w+1
,发送报文后,客户端就进入了TIME-WAIT状态.注意此时客户端的TCP连接还没有释放,要等待2MSL(MSL即最长报文段寿命,规定为2分钟,linux下一般为30秒)时间后才进入CLOSED状态,即客户端的TCP连接此时才中断. - 服务器端接收到确认报文后便进入了
CLOSED状态
,即TCP连接已断开,完成四次挥手!
总结:
为什么会有TIME-WAIT状态
- 确保有足够的时间让对方收到ACK包,如果服务器端没有收到ACK包的话会向客户端重发FIN包,然后客户端再重新向服务器端重发
ACK包
,这样一来一去刚好是两个MSL
. - 有足够的时间保证这个连接不会跟后面的连接混在一起,避免新旧连接混淆,因为有可能路由器会缓存IP数据包,如果这个连接立即被其他客户端重用了,那么这些数据包可能会跟新发送的数据混在一起,造成接收数据错误.
为什么需要四次握手才能断开连接
我们知道TCP协议是全双工的
协议,即建立连接后,在同一时间,客户端可以发送数据给服务器端,同时服务器端也可以发送数据给客户端,所以每个方向都必须单独关闭,
这也就导致了发送方和接收方都需要FIN报文和ACK报文才能确认关闭连接
为什么服务器会出现大量CLOSE-WAIT状态(即客户端一直请求关闭连接,但是服务器端没有做出应答)
产生这种现象的原因: 服务器端收到客户端发来的FIN结束报文后,没有向客户端发送ACK确认报文
或者没有发送FIN结束报文
. 也就是说有可能服务器端已经关闭了连接,但是程序没有检测到或者程序本身就没有关闭连接这个功能,于是 资源就一直被占用着.
这种情况的发生有可能是代码有bug,检查代码去吧,尤其是释放资源的代码,有可能是没有释放连接导致的,也有可能是线程的配置有问题.