我知道关于这一点有很多 SE 问题,我相信在到达这一点之前我已经阅读了尽可能多的相关问题。
我所说的“服务器端TIME_WAIT
”是指在服务器端发起 close() 的服务器端套接字对的状态。
我经常看到这些听起来相互矛盾的说法:
- 服务器端
TIME_WAIT
是无害的 - 您应该设计您的网络应用程序让客户端发起 close(),因此让客户端承担
TIME_WAIT
我发现这相互矛盾的原因是因为TIME_WAIT
客户端上可能存在问题——客户端可能会用尽可用端口,因此本质上上述内容建议将负担TIME_WAIT
从不存在问题的服务器端转移到可能存在问题的客户端。
TIME_WAIT
当然,客户端只是在有限数量的用例中才会出现问题。大多数客户端-服务器解决方案都涉及一台服务器和许多客户端,客户端通常不会处理足够多的连接,因此不会成为问题,即使需要处理,也有许多建议可以“理智地”(而不是SO_LINGER
使用 0 超时或干预 tcp_tw sysctls)对抗客户端TIME_WAIT
,避免过快创建过多连接。但这并不总是可行的,例如对于以下类型的应用程序:
- 监控系统
- 负载生成器
- 代理
另一方面,我甚至不明白服务器端TIME_WAIT
有什么用。 之所以TIME_WAIT
存在,是因为它可以防止将陈旧的TCP
片段注入它们不再属于的流中。 对于客户端,TIME_WAIT
它通过简单地使无法创建与ip:port
此陈旧连接可能具有的相同对的连接来实现(使用的对被锁定TIME_WAIT
)。 但对于服务器端,这无法阻止,因为本地地址将具有接受端口,并且始终相同,并且服务器不能(据我所知,我只有经验证据)仅仅因为传入对等方将创建套接字表中已存在的相同地址对而拒绝连接。
我确实编写了一个程序来显示服务器端的 TIME-WAIT 被忽略。此外,由于测试是在 127.0.0.1 上进行的,因此内核必须有一个特殊位来告诉它它是服务器端还是客户端(否则元组将是相同的)。
来源:http://pastebin.com/5PWjkjEf,在 Fedora 22 上测试,默认网络配置。
$ gcc -o rtest rtest.c -lpthread
$ ./rtest 44400 s # will do server-side close
Will initiate server close
... iterates ~20 times successfully
^C
$ ss -a|grep 44400
tcp TIME-WAIT 0 0 127.0.0.1:44400 127.0.0.1:44401
$ ./rtest 44500 c # will do client-side close
Will initiate client close
... runs once and then
connecting...
connect: Cannot assign requested address
因此,对于服务器端TIME_WAIT
,完全相同的端口对上的连接可以立即成功重新建立,而对于客户端TIME-WAIT
,在第二次迭代中connect()
理所当然地失败了
总而言之,问题有两个方面:
- 服务器端
TIME_WAIT
真的不做任何事情吗,只是因为RFC
要求它这样做而保持这种状态? - 建议客户端启动 close() 的原因是因为服务器
TIME_WAIT
无用吗?
答案1
在TCP这里的术语“服务器端”是指套接字处于侦听状态的主机。
RFC1122允许处于 TIME-WAIT 状态的套接字在某些条件下接受新连接
When a connection is closed actively, it MUST linger in TIME-WAIT state for a time 2xMSL (Maximum Segment Lifetime). However, it MAY accept a new SYN from the remote TCP to reopen the connection directly from TIME-WAIT state, if it:
有关条件的具体详细信息,请参阅RFC1122。我希望套接字上也必须有一个匹配的被动 OPEN(套接字处于 LISTEN 状态)。
主动 OPEN(客户端连接调用)不会出现此类异常,并且当套接字处于 TIME-WAIT 状态时必须给出错误,如RFC793。
我对客户端(在 TCP 术语中,执行主动 OPEN 即连接的主机)发起关闭的建议的猜测与您的大致相同,即在常见情况下,它会将 TIME-WAIT 套接字传播到更多主机上,这些主机上有足够的套接字资源。在常见情况下,客户端不会发送会重用服务器上的 TIME-WAIT 套接字的 SYN。我同意应用此类建议仍然取决于用例。
答案2
客户端将根据算法使用一个新的 TCP ISN(初始序列号)(大约每 4 微秒增加 1),并且 ISN 基本上总是大于在最后一个相同的“ip:端口对”TCP 套接字 FIN 中发送的序列号,因此即使服务器上的“ip:端口对”仍记录在 TIME_WAIT 状态中,服务器也将始终接受新的 SYN。
RFC 793 [RFC0793] suggests that the choice of the ISN of a connection
is not arbitrary, but aims to reduce the chances of a stale segment
from being accepted by a new incarnation of a previous connection.
RFC 793 [RFC0793] suggests the use of a global 32-bit ISN generator
that is incremented by 1 roughly every 4 microseconds.
答案3
这可能是 TIME-WAIT 实际作用的最清晰示例,更重要的是它的重要性。它还解释了为什么要避免使用 Linux 机器上的某些“专家”技巧来“减少”TIME-WAIT。
答案4
使用不可靠的协议,您永远无法确定是否已收到对等设备的最后一条消息,因此假设对等设备突然挂断电话是很危险的。TCP 协议的一个主要缺点是只能同时打开 65000 个左右的端口。但克服这个问题的方法是转移到服务器群,它可以更好地适应负载,而不是快速回收端口号。在客户端,如果是基本工作站,则不太可能用完端口。