【问题标题】:Linux tcp server can't bind to a close_wait portLinux tcp 服务器无法绑定到 close_wait 端口
【发布时间】:2018-12-04 19:33:57
【问题描述】:

我尝试让 tcp 服务器绑定到一个 close_wait 端口,但它导致了 Errno::EADDRINUSE 错误。

我创建了一个侦听端口 55555 的 tcp 服务器。然后客户端连接到该服务器。一些操作后,运行ss -at | grep 55555。

# ss -at | grep 55555
LISTEN     0      128                     *:55555                    *:*
FIN-WAIT-2 0      0               127.0.0.1:55555            127.0.0.1:16413
CLOSE-WAIT 0      0               127.0.0.1:16413            127.0.0.1:55555

我尝试绑定端口16413,导致Errno::EADDRINUSE 错误。 但是如果我连接到一个 ESTAB 套接字,该套接字可以绑定到端口(例如下面的 22385)。

# ss -at | grep 55555
LISTEN     0      128                     *:55555                    *:*
ESTAB      0      0               127.0.0.1:22385            127.0.0.1:55555
ESTAB      0      0               127.0.0.1:55555            127.0.0.1:22385 

通过 ruby​​ 的一些脚本来重现问题。

tcp_server_close_wait.rb

require 'socket'

server = TCPServer.new 55555 # Server bind to port 2000
loop do
  client = server.accept    # Wait for a client to connect
  client.puts "Hello !"
  client.puts "Time is #{Time.now}"
  client.shutdown(Socket::SHUT_WR)
end

tcp_server.rb

require 'socket'

server = TCPServer.new 55555 # Server bind to port 2000
loop do
  client = server.accept    # Wait for a client to connect
  client.puts "Hello !"
  client.puts "Time is #{Time.now}"
  client.close
end

tcp_client.rb

require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 55555, '127.0.0.1' )
socket.connect( sockaddr )
res = socket.read
puts res

sleep 10000

** tcp_bind.rb **

require 'socket'

# use Addrinfo
socket = Socket.new(:INET, :STREAM, 0)
socket.bind(Addrinfo.tcp("0.0.0.0", ARGV[0].to_i))

创建close_wait绑定。

  1. 运行ruby tcp_server_close_wait.rb

  2. 运行ruby tcp_client.rb

  3. 运行ss -at | grep 55555 查找客户端端口

  4. 运行ruby tcp_bind.rb $client_port

创建ESTAB 绑定。 1.运行ruby tcp_server.rb

  1. 运行ruby tcp_client.rb

  2. 运行ss -at | grep 55555 查找客户端端口

  3. 运行ruby tcp_bind.rb $client_port

【问题讨论】:

  • 对SO_REUSEADDR 套接字选项做一些研究。
  • SO_REUSEADDR 与问题无关。每个 tcp server && client 都是在没有SO_REUSEADDR的情况下创建的。
  • 如果SO_REUSEADDR 设置在处于TIME_WAIT 状态的套接字上,那么另一个套接字应该能够绑定到与关闭套接字相同的地址。
  • 我的问题是close wait不能绑定,但是ESTAB可以。
  • 请记住,TCP 和 UDP 套接字的本地端点由协议(TCP 或 UDP)、地址和端口号标识。如果 any 与其他某个端点不同,则它们是不同的端点。并且由 TCP:*:55555 标识的端点与由 TCP:127.0.0.1:55555 标识的端点不同。

标签: linux networking tcp server tcp-ip


【解决方案1】:

我使用 C 程序对 Linux 版本 4.4.74-18.20 重复了相同的测试。

我得到的结果与 OP 不同。

当 SO_REUSEPORT 被禁用时,两个端口的绑定都失败了:

  • 已建立 TCP 连接 (ESTAB) 的客户端端口
  • 半开TCP连接(CLOSE-WAIT)的客户端端口

启用 SO_REUSEPORT 后(对所有套接字),两个端口都绑定成功。

有关 SO_REUSEPORT 的更多信息,请参阅socket(7) man page。

【讨论】:

    猜你喜欢
    • 2017-09-19
    • 1970-01-01
    • 1970-01-01
    • 2021-12-09
    • 2012-04-12
    • 2017-02-24
    • 2012-08-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多