I need a quick way to find out if a given port is open with Ruby. I currently am fiddling around with this:
require 'socket'
def is_port_open?(ip, port)
begin
TCPSocket.new(ip, port)
rescue Errno::ECONNREFUSED
return false
end
return true
end
It works great if the port is open, but the downside of this is that occasionally it will just sit and wait for 10-20 seconds and then eventually time out, throwing a ETIMEOUT
exception (if the port is closed). My question is thus:
Can this code be amended to only wait for a second (and return false
if we get nothing back by then) or is there a better way to check if a given port is open on a given host?
Edit: Calling bash code is acceptable also as long as it works cross-platform (eg, Mac OS X, *nix, and Cygwin), although I do prefer Ruby code.
Something like the following might work:
require 'socket'
require 'timeout'
def is_port_open?(ip, port)
begin
Timeout::timeout(1) do
begin
s = TCPSocket.new(ip, port)
s.close
return true
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
return false
end
end
rescue Timeout::Error
end
return false
end
More Ruby idiomatic syntax:
require 'socket'
require 'timeout'
def port_open?(ip, port, seconds=1)
Timeout::timeout(seconds) do
begin
TCPSocket.new(ip, port).close
true
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
false
end
end
rescue Timeout::Error
false
end
All other existing answer are undesirable. Using Timeout
is discouraged . Perhaps things depend on ruby version. At least since 2.0 one can simply use:
Socket.tcp("www.ruby-lang.org", 10567, connect_timeout: 5) {}
For older ruby the best method I could find is using non-blocking mode and then select
. Described here:
I recently came up with this solution, making use of the unix lsof
command:
def port_open?(port)
!system("lsof -i:#{port}", out: '/dev/null')
end
Just for completeness, the Bash would be something like this:
$ netcat $HOST $PORT -w 1 -q 0 </dev/null && do_something
-w 1
specifies a timeout of 1 second, and -q 0
says that, when connected, close the connection as soon as stdin
gives EOF
(which /dev/null
will do straight away).
Bash also has its own built-in TCP/UDP services, but they are a compile-time option and I don't have a Bash compiled with them :P
My slight variation to Chris Rice's answer. Still handles timing out on a single attempt but also allows multiple retries until you give up.
def is_port_open?(host, port, timeout, sleep_period)
begin
Timeout::timeout(timeout) do
begin
s = TCPSocket.new(host, port)
s.close
return true
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
sleep(sleep_period)
retry
end
end
rescue Timeout::Error
return false
end
end
All *nix platforms:
try nc / netcat command as follow.
`nc -z -w #{timeout_in_seconds} -G #{timeout_in_seconds} #{host} #{port}`
if $?.exitstatus == 0
#port is open
else
#refused, port is closed
end
The -z flag can be used to tell nc to report open ports, rather than initiate a connection.
The -w flag means Timeout for connects and final net reads
The -G flag is connection timeout in seconds
Use -n flag to work with IP address rather than hostname.
Examples:
# `nc -z -w 1 -G 1 google.com 80`
# `nc -z -w 1 -G 1 -n 123.234.1.18 80`
My solution is derived from the posted solutions.
require 'socket'
def is_port_open?(ip, port)
begin
s = Socket.tcp(ip, port, connect_timeout: 5)
s.close
return true
rescue => e
# possible exceptions:
# - Errno::ECONNREFUSED
# - Errno::EHOSTUNREACH
# - Errno::ETIMEDOUT
puts "#{e.class}: #{e.message}"
return false
end
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.