TIME_WAIT状态详细分析

 

 

对于 TCP 通信中关闭连接时需要的四次握手,最值得一说的就是 TIME_WAIT 状态。

在阅读相关资料和实践的基础上,我对这个状态的总结是

1.   仅在主动关闭一段产生;

2.   设置 SO_REUSEADDR 选项需要在 bind 之前进行。

以下分别从客户端和服务器端来分析。

从客户端的角度来看。如果采用的是长连接,那么根本就不会有 close 的行为;如果采用的是短连接,那么重新 connect 时会临时分配随机端口,不会与之前处于 TIME_WAIT 端口冲突。

举例说明,在服务器端开 10000 的端口号,观察不同阶段的 socket 状态。服务器和客户端部署在同一台机器上,以下部分以 LOCAL 代替实际的终端输出 "localhost.localdomain"

服务器启动完毕后 :

LOCAL:10000 *:* LISTEN

一个客户端实例连接成功后 :  

LOCAL:10000 *:* LISTEN       

LOCAL:10000 LOCAL:45358 ESTABLISHED 

LOCAL:45358 LOCAL:10000 ESTABLISHED

该客户端实例关闭成功后      

LOCAL:10000 *:* LISTEN      
LOCAL:45358 LOCAL:10000 TIME_WAIT  //
服务器端进入 CLOSE 状态后已成功关闭

客户端实例再次连接成功后

LOCAL:10000 *:* LISTEN      
LOCAL:10000 LOCAL:45359 ESTABLISHED
LOCAL:45358 LOCAL:10000 TIME_WAIT  //
这个连接仍在存在
LOCAL:45359 LOCAL:10000 ESTABLISHED

可见,原有的 45358 端口的 TIME_WAIT 不影响 45359 端口连接实例的创建。

从负面影响来看,客户端的 TIME_WAIT 会占用客户端的 fd ,处于 TIME_WAIT 状态的 fd 过多,以至于超出系统的 limit 限制,会导致无法再建立新的连接。

从服务器端角度来看。如果服务器主动关闭,情况就有些复杂。

服务器启动完毕后 :

LOCAL:10000 *:* LISTEN

一个客户端实例连接成功后

LOCAL:10000 *:* LISTEN      
LOCAL:10000 LOCAL:45360 ESTABLISHED 
LOCAL:45360 LOCAL:10000 ESTABLISHED

服务器关闭后 :

LOCAL:45360 LOCAL:10000 TIME_WAIT   // 客户端进入 CLOSE 状态后已成功关闭

      服务器再次启动 :

unable to bind address:Address already in use

由于服务器的启动并不是绑定临时端口,而是仍要建立在固定的 10000 端口上,此时 10000 端口处于 TIME_WAIT 的状态。到时 bind 无法正常进行。

解决该问题就需要 SO_REUSEADDR 参数,而该参数的设置很显然要在 bind 之前。 SO_REUSEADDR 官方解释是:允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将该端口用作他们的本地端口的连接仍存在。设置该参数后。

服务器再次启动 :

LOCAL:10000 *:* LISTEN      
LOCAL:10000 LOCAL:45360 TIME_WAIT  //10000
端口被复用

      一个客户端实例连接成功后 :

LOCAL:10000 *:* LISTEN     
LOCAL:10000 LOCAL:45360 TIME_WAIT

LOCAL:45361 LOCAL:10000 ESTABLISHED

LOCAL:10000 LOCAL:45361 ESTABLISHED

TIME_WAIT 的值与系统实现相关。在我的机器上测试为 60s 。那么启动时间小于 60s 的服务示例都要面临这个端口占用不能重启的问题。

SO_REUSEADDR 其他用途如可以允许同时启动多个 IP(IP 别名 ) 的服务器绑定在相同的端口上。但这也带来一些安全问题,恶意的服务器可以绑定同样的端口,截获部分请求。  

       参考文献:《 UNIX 网络编程》第一卷

 

你可能感兴趣的:(TIME_WAIT状态详细分析)