什么会阻止 TCP 服务器端口(一旦关闭)立即重新打开?

什么会阻止 TCP 服务器端口(一旦关闭)立即重新打开?

我有一个使用 TCP 的服务器程序。

有时,我需要重新启动程序以进行更新或出于任何其他原因,但当我这样做时,程序会关闭服务器端口,然后在重新启动后,它会尝试在该端口上创建一个新的侦听器,但似乎只有在端口先前关闭后约一分钟(约 63 秒)才能成功完成此操作。为什么会这样?有什么方法可以解决它吗?

该程序在 RamNode 的 Ubuntu 18.04 上运行。

也许我可以在操作系统中更改任何设置,或者可能是 RamNode 的事情,等等?

答案1

对于处理 TCP 套接字的应用程序,最有可能是因为TIME-WAITTCP协议的状态:

TIME-WAIT- 表示等待足够的时间以确保远程 TCP 收到其连接终止请求的确认。

当关闭上一个连接时,两端中的一端(取决于哪一端发起关闭)将保持 TCP 套接字处于TIME-WAIT状态,以防止相同的 TCP 连接在一段时间内存在。当这在客户端时,这不是问题,因为端口是一个随机的临时端口。当它位于具有固定端口的服务器端并且服务器重新启动时,这会阻止服务器再次绑定到同一端口。


下面是使用的示例socat重现该行为。

术语1:

socat tcp4-listen:5555 -

期限2:

socat tcp4:127.0.0.1:5555 -

现在连接已建立,只需中断 term1 的socat命令即可。如果立即重新启动,您将获得 1 分钟的延迟:

术语1:

$ socat tcp4-listen:5555 -
2021/07/03 21:32:09 socat[320904] E bind(5, {AF=2 0.0.0.0:5555}, 16): Address already in use

防止此行为的功能(对于服务器尤其有用)是套接字选项 SO_REUSEADDR

SO_REUSEADDR

表示验证地址时使用的规则 绑定(2)呼叫应该允许重用本地地址。对于AF_INET 套接字,这意味着套接字可以绑定,除非有活动侦听套接字绑定到该地址。当侦听套接字INADDR_ANY与特定端口绑定时,则不可能将任何本地地址绑定到此端口。参数是一个整数布尔标志。

相反,当第一次开始聆听socat时:

socat tcp4-listen:5555,reuseaddr -

TIME-WAIT即使像以前一样在连接后中断,现在也可以立即重新启动,而无需使用此端口评估 TCP 连接(这包括处于状态的以前的 TCP 连接)SO_REUSEADDR如果它在之前的运行中也使用,才能bind()成功。

这是一个奇怪的例子(表明它不仅限于TIME-WAIT州):

术语1:

socat tcp4:127.0.0.1:5555,bind=127.0.0.1:5555,reuseaddr -

(上述命令在同时的 TCP 启动中连接到自身,如RFC 793

$ ss -tn sport == 5555
State  Recv-Q  Send-Q   Local Address:Port    Peer Address:Port  
ESTAB  0       0            127.0.0.1:5555       127.0.0.1:5555  

它不会阻止在端口 5555 之后创建监听套接字

期限2:

socat tcp4-listen:5555,reuseaddr 

术语3:

$ ss -atnp sport == 5555
State  Recv-Q Send-Q Local Address:Port   Peer Address:Port                                    
LISTEN 0      5            0.0.0.0:5555        0.0.0.0:*     users:(("socat",pid=321093,fd=5)) 
ESTAB  0      0          127.0.0.1:5555      127.0.0.1:5555  users:(("socat",pid=321047,fd=5)) 

$ socat -d -d tcp4:127.0.0.1:5555 -
2021/07/03 22:16:27 socat[322125] N opening connection to AF=2 127.0.0.1:5555
2021/07/03 22:16:27 socat[322125] N successfully connected from local address AF=2 127.0.0.1:52218
2021/07/03 22:16:27 socat[322125] N reading from and writing to stdio
2021/07/03 22:16:27 socat[322125] N starting data transfer loop with FDs [5,5] and [0,1]

回到问题:它在你的应用程序上。

任何一个:

  • 更正应用程序以setsockopt()在将要监听的套接字上使用,以SO_REUSEADDR允许bind()成功,尽管使用该端口的连接也存在(TIME-WAIT算作这样的连接,并且可能是实际情况下的唯一原因)。

或者如果你不能,在动态链接上(使用) 应用:

相关内容