我试图让 tcp 服务器绑定(bind)到 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
我尝试绑定(bind)端口 16413
,它导致了 Errno::EADDRINUSE
错误。
但是如果我连接到 ESTAB 套接字,套接字可以绑定(bind)到端口(例如下面的 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
绑定(bind)。
运行
ruby tcp_server_close_wait.rb
运行
ruby tcp_client.rb
运行
ss -at | grep 55555
查找客户端端口运行
ruby tcp_bind.rb $client_port
创建 ESTAB
绑定(bind)。
1. 运行 ruby tcp_server.rb
运行
ruby tcp_client.rb
运行
ss -at | grep 55555
查找客户端端口运行
ruby tcp_bind.rb $client_port
最佳答案
我使用 C 程序对 Linux 4.4.74-18.20 版重复了相同的测试。
我得到的结果与 OP 不同。
当 SO_REUSEPORT 被禁用时,两个端口的绑定(bind)都失败了:
- 建立TCP连接的客户端端口(ESTAB)
- 半开TCP连接(CLOSE-WAIT)的客户端端口
当启用 SO_REUSEPORT 时(对于所有套接字),两个端口的绑定(bind)成功。
参见 socket(7) man page获取有关 SO_REUSEPORT 的更多信息。
关于Linux tcp 服务器无法绑定(bind)到 close_wait 端口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51036790/