【发布时间】: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绑定。
运行
ruby tcp_server_close_wait.rb运行
ruby tcp_client.rb运行
ss -at | grep 55555查找客户端端口运行
ruby tcp_bind.rb $client_port
创建ESTAB 绑定。
1.运行ruby tcp_server.rb
运行
ruby tcp_client.rb运行
ss -at | grep 55555查找客户端端口运行
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