Reputation: 333
I am presenting a simple TCP server demo in Ruby. My first demo is using classical C-style bind-listen-accept-read-write-close methods. This code works well for the first time:
require 'socket'
class Server
def start(bind: '127.0.0.1', port: nil, backlog: 1)
Socket.new(:INET, :STREAM).tap do |sock|
sock.bind Addrinfo.tcp bind, port
sock.listen backlog
# the codes of client connecting with it here is for reproducing the issue more easily
c = Socket.new(:INET, :STREAM)
c.connect Addrinfo.tcp '127.0.0.1', port
client, client_addr = sock.accept
puts "connected from #{client_addr.ip_address}:#{client_addr.ip_port}"
client.puts "hi"
client.close
sock.close
end
end
end
Server.new.start(port: 23333)
However when I tried to run it again, I got an errors of EADDRINUSE
:
`some-script.rb:8:in `bind': Address already in use - bind(2) for 127.0.0.1:23333 (Errno::EADDRINUSE)`
After about 30 seconds, I can start the script successfully again. Not sure if this is done by the kernel.
Dosn't Socket#close
fully close the socket and kernel close the TCP connection?
I tried it on both Mac OS 10.11.1 and Ubuntu 14.04 and got the same result. After reading ruby's source code (socket.c) I still can't figure it out.
Any advice?
Upvotes: 2
Views: 368
Reputation: 4943
You can work around this by calling sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
before the call to sock.bind()
.
Upvotes: 2