charleschchen
作者charleschchen·2022-07-14 18:26
系统架构师·浪潮商用机器有限公司

TIME_WAIT状态说明

字数 2557阅读 1288评论 0赞 0

问题

客户反馈系统中存在大量TIME_WAIT状态的TCP连接,导致新连接无法建立。

分析

参考 ”TCP/IP 详解 卷 1 :协议 ” ,根据 TCP 规范,主动关闭的一方最后会进入 TIME_WAIT 状态,此状态的持续时间为2MSL。
说明:
Maximum Segment Lifetime,即一个数据帧在网络中生存的最长时间。
这个设计的主要目的是两点:

  1. 确保终止 TCP 全双工连接的可靠性
  2. 允许老的重复分节在网络中超时消失
    主动发起关闭的一方完成TIME_WAIT状态后才会最终关闭连接。

据上述,可以看到 TIME_WAIT 状态是有助于连接可靠性的(避免新连接收到旧连接的旧数据包)。

这种 2MSL 等待的一个附带后果是这个 TCP 连接在 2MSL 等待期间,定义这个连接的 socket 四元组 ( 客户的 IP 地址和端口号,服务器的 IP 地址和端口号 ) 不能再被使用。这个 四元组只能在 2MSL 结束后才能再被使用。

在 AIX 上, TIME_WAIT( 即 2MSL) 默认的时长是 15 秒钟,参考 no 命令的 tcp_timewait 选项:

root:[/] no -h tcp_timewait  
Help for tunable tcp_timewait:  
Purpose:  
The tcp_timewait option is used to configure how long connections are kept in the timewait state.  
  
Values:  
Default: 1  
Range: 1 - 5  
Type: Dynamic  
Unit: 15_second  
Tuning:  
It is given in 15 second intervals. Increasing this value will degrade performance of Web servers or applications that open and close a lot of TCP connections.  

网络设置建议

  1. 在 Server 端 bind 之前,调用 setsockopt 设置 SO_REUSEADDR 选项;
    示例如下:
int ret, optval=1;  
fd_ = socket(AF_INET, SOCK_STREAM, 0);  
if(fd_ < 0)  
{  
printf("open failed\\n");  
return -1;  
}  
if (setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR,   
(const void *)&optval , sizeof(int)) < 0)   
return -1;  
  
ret = bind(fd_, (SA *) &addr.addrin_, sizeof(addr.addrin_));   
  1. 一般建议尽量从客户端关闭连接,这样从逻辑上更合理一些;而且这样一来, TIME_WAIT 状态会分散到各客户端;不会影响服务端。
  2. client 端采用通配端口,不绑定特定的本地端口。
    以C代码举例,可将传递给 bind 的第二个参数,即 struct sockaddr_in 结构的指针,指向的 sin_port 字段置为 0(htons(0)) 这样一来,每次连接客户端都是不同的临时端口,不会受到上次连接 TIME_WAIT 的影响。
  3. 如果客户端在处于 TIME_WAIT 状态,仍然试图连接,亦可以设置 SO_REUSEADDR 选项。方法同上。
  4. 可以考虑扩大客户端的通配端口范围,例如:

    # no -p -o tcp_ephemeral_low=9000 -o tcp_ephemeral_high=65500
    # no -p -o udp_ephemeral_low=9000 -o udp_ephemeral_high=65500
  5. 对 socket 句柄设置 SO_LINGER 选项,并将 l_onoff 字段设置为 1 , l_linger 字段设置为 0 ; 则在主动关闭 socket 时,将直接发送 RST;
    这样TCP连接关闭时不会进入 TIME_WAIT 状态。 但这种方式是不规范的,通常不建议使用
  6. 在 AIX 上, Server 端主动关闭 Socket 连接时,在 TIME_WAIT 期间允许一种例外情况:
    即与处于 TIME_WAIT 状态的 socket 四元组( 客户的 IP 地址和端口号,服务器的 IP 地址和端口号 ) 重复的新连接请求,如果到达的 SYN 的初始序列号 (ISN) " 大于 " 前一化身 ( 即处于 TIME_WAIT 状态的化身 ) 的结束序列号,则允许该重复连接请求, TCP 三次握手可以正常建立连接。
    注意TCP序列号是 32 位的无符号整数, " 大于 " 的定义是:

    #define SEQ_GT(a,b) ((int)((a)-(b)) > 0)  

    参考:
    TCP/IP illustrated Volume1 Protocol:
    18.6 TCP State Transition Diagram

TCP/IP illustrated Volume2 Implementation:
chapter 28-28
chapter 28-17

RFC 1122 规范对操作系统 TIME_WAIT 状态实现的要求 :
TCP REQUIREMENT SUMMARY
...
Send RST to indicate data lost SHOULD (应当实现)
In TIME-WAIT state for 2xMSL seconds MUST( 必须实现 )
Accept SYN from TIME-WAIT state MAY (可能实现)

Read more: http://www.faqs.org/rfcs/rfc1122.html

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

0

添加新评论0 条评论

Ctrl+Enter 发表

作者其他文章

X社区推广