1 问题描述:
最近websocket服务程序在绑定某些固定端口失败,使用statnet -noa查看后发现,系统中残留大量CLOSE_WAIT的状态和TIME_WAIT状态的端口。从而导致在绑定监听端口时,socket失败的情况。
2 原理讲解
我们知道,在socket编程中,TCP的连接和断开需要经过三次握手和四次挥手的过程。这里着重讲四次挥手。当服务器端/客户端程序主动调用closesocket端口后,主动断开方就会向被动方发送FIN信号,被动方收到FIN信号后,就进入了 CLOSE_WAIT 状态。如果一切正常,稍后被动方需要再次发出 FIN 包,进而迁移到 LAST_ACK 状态;主动方收到FIN包后,回复ACK信号,并且状态变为TIME_WAIT状态,并且等待2倍的MSL周期后,状态变为CLOSED状态;当被动断开方收到ACK信号后,状态变为CLOSED状态。
以上理论,在很多的文章都有详细的描述,个人感觉https://www.cnblogs.com/kevingrace/p/9988354.html文章描述的很好。之所以,TCP结束要设计如此的繁复,其实和日常打电话的情况是一样的;要挂电话之前,先要问对方是否还有其他事情,没有其他事情的情况下,才能真真的做出挂电话的动作。
实现代码在附件中,
wireshark的截取的交互流程如下图
但是,我们把recv()==0的情况下,closesocket的代码注释后,被动断开方的端口的状态就会标称CLOSE_WAIT的状态,而导致程序无法释放。可以说这是一种异常的状态,如遇到此情况,我们一定要把这个bug给解决掉。下图就显示了这种情况下的交互
这种情况下,我们可以清楚看到被动方的端口变为CLOSE_WAIT状态,而且此状态在进程退出之前会一直存在,不能释放。主动方在经过2MSL后,在上图中,是被动在回复FIN的ACK后,120秒后,向被动方发送了RST复位的信号,最后变为CLOSED的状态。
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是百度云盘导致的。
来源:CSDN
作者:恒哥的爸爸
链接:https://blog.csdn.net/rendawei636/article/details/104543001