socket四次挥手(CLOSE_WAIT和TIME_WAIT状态描述和处理)

1 问题描述:

最近websocket服务程序在绑定某些固定端口失败,使用statnet -noa查看后发现,系统中残留大量CLOSE_WAIT的状态和TIME_WAIT状态的端口。从而导致在绑定监听端口时,socket失败的情况。

socket四次挥手(CLOSE_WAIT和TIME_WAIT状态描述和处理)_第1张图片 CLOSE_WAIT

2 原理讲解

我们知道,在socket编程中,TCP的连接和断开需要经过三次握手和四次挥手的过程。这里着重讲四次挥手。当服务器端/客户端程序主动调用closesocket端口后,主动断开方就会向被动方发送FIN信号,被动方收到FIN信号后,就进入了 CLOSE_WAIT 状态。如果一切正常,稍后被动方需要再次发出 FIN 包,进而迁移到 LAST_ACK 状态;主动方收到FIN包后,回复ACK信号,并且状态变为TIME_WAIT状态,并且等待2倍的MSL周期后,状态变为CLOSED状态;当被动断开方收到ACK信号后,状态变为CLOSED状态。

socket四次挥手(CLOSE_WAIT和TIME_WAIT状态描述和处理)_第2张图片 四次挥手交互图以及调用函数

以上理论,在很多的文章都有详细的描述,个人感觉https://www.cnblogs.com/kevingrace/p/9988354.html文章描述的很好。之所以,TCP结束要设计如此的繁复,其实和日常打电话的情况是一样的;要挂电话之前,先要问对方是否还有其他事情,没有其他事情的情况下,才能真真的做出挂电话的动作。

实现代码在附件中,

wireshark的截取的交互流程如下图

socket四次挥手(CLOSE_WAIT和TIME_WAIT状态描述和处理)_第3张图片 正常连接断开交互

但是,我们把recv()==0的情况下,closesocket的代码注释后,被动断开方的端口的状态就会标称CLOSE_WAIT的状态,而导致程序无法释放。可以说这是一种异常的状态,如遇到此情况,我们一定要把这个bug给解决掉。下图就显示了这种情况下的交互

socket四次挥手(CLOSE_WAIT和TIME_WAIT状态描述和处理)_第4张图片 被动方在收到FIN后,没有调用closesocket的情况下,双方的交互

这种情况下,我们可以清楚看到被动方的端口变为CLOSE_WAIT状态,而且此状态在进程退出之前会一直存在,不能释放。主动方在经过2MSL后,在上图中,是被动在回复FIN的ACK后,120秒后,向被动方发送了RST复位的信号,最后变为CLOSED的状态。

socket四次挥手(CLOSE_WAIT和TIME_WAIT状态描述和处理)_第5张图片 出现CLOSE_WAIT的端口

 

 

3 解决问题:

从上图可以看出,TIME_WAIT的状态是正常状态。如果,发现程序中过多,最简单的方法可以通过设置回收TIME_WAIT端口周期来实现。WINDOWS可以通过设置HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters, 添加名为TcpTimedWaitDelay的DWORD键,设置为30或更短,以缩短TIME_WAIT的等待时间,需要重启电脑。能很明显的看到TcpTimedWaitDelay值影响TIME_WAIT端口数量变多或变少,大家可以自行进行测试。

CLOSE_WAIT状态的端口,一定是程序出问题,也就是说被动断开方,没有在合适的时刻去调用closesocket函数,导致被动断开方一直处于此种状态,而不能释放端口资源。本人大量的CLOSE_WAIT端口的问题,PID是百度云盘导致的。

你可能感兴趣的:(C++,软件工程)