Reputation: 532
I'm trying to create a use-once HTTP server to handle a single callback and need help with finding a free TCP port in Ruby.
This is the skeleton of what I'm doing:
require 'socket'
t = STDIN.read
port = 8081
while s = TCPServer.new('127.0.0.1', port).accept
puts s.gets
s.print "HTTP/1.1 200/OK\rContent-type: text/plain\r\n\r\n" + t
s.close
exit
end
(It echoes standard input to the first connection and then dies.)
How can I automatically find a free port to listen on?
This seems to be the only way to start a job on a remote server which then calls back with a unique job ID. This job ID can then be queried for status info. Why the original designers couldn't just return the job ID when scheduling the job I'll never know. A single port cannot be used because conflicts with multiple callbacks may occur; in this way the ports are only used for +- 5 seconds.
Upvotes: 15
Views: 9660
Reputation: 105083
You can try random-port, a simple Ruby gem (I'm the author):
require 'random-port'
port = RandomPort::Pool.new.aquire
The best way, though, is to release it afterward:
RandomPort::Pool::SINGLETON.new.acquire do |port|
# Use the TCP port, it will be returned back
# to the pool afterward.
end
The pool is thread-safe and it guarantees that the port won't be used by another thread or anywhere else in the app, until it's released.
Upvotes: 1
Reputation: 1239
Maybe you want test weather one port is listening, following is worked for me
system('(6<>/dev/tcp/127.0.0.1/9292) &>/dev/null')
, return true if 9292 is listening, otherwise, return false.
Upvotes: 0
Reputation: 14716
Pass 0 in for the port number. This will cause the system to pick a port for you out of the ephemeral port range. Once you create the server, you can ask it for its addr, which will contain the port that the server is bound to.
server = TCPServer.new('127.0.0.1', 0)
port = server.addr[1]
Upvotes: 69
Reputation: 532
It is actually quite easy when you don't try to do everything in one line :-/
require 'socket'
t = STDIN.read
port = 8080 # preferred port
begin
server = TCPServer.new('127.0.0.1', port)
rescue Errno::EADDRINUSE
port = rand(65000 - 1024) + 1024
retry
end
# Start remote process with the value of port
socket = server.accept
puts socket.gets
socket.print "HTTP/1.1 200/OK\rContent-type: text/plain\r\n\r\n" + t
socket.close
This accomplishes (strong word) the same as the snippet in the question.
Upvotes: 3
Reputation: 23890
Don't communicate on random ports. Pick a default one and make it configurable. Random ports are incompatible with firewalls. FTP does this and firewall support for it is a nightmare - it has to deeply inspect packets.
Upvotes: -8
Reputation: 78538
I guess you could try all ports > 5000 (for example) in sequence. But how will you communicate to the client program what port you are listening to? It seems simpler to decide on a port, and then make it easily configurable, if you need to move your script between different enviroments.
For HTTP, the standard port is 80. Alternative ports i've seen used are 8080, 880 and 8000.
Upvotes: -9