Haven't paid attention to the question for a long time, but here is the script I use to see if a anything is listening on a port. Notice that the method I suggested in the accepted answer ins't used.
#!/usr/bin/env ruby
# encoding: ASCII-8bit
# warn_indent: true
# frozen_string_literal: true
# rubocop:disable Lint/MissingCopEnableDirective
# rubocop:disable Style/StderrPuts
# Test a connection to a port on a host. A host name, IPv4 address, or IPv6
# address is allowed for the host. A service name or a port number is allowed
# for the port.
# Trap a few signals and exit. No problem if the operating system doesn't
# support one of the signals. Rescuing ArgumentError takes care of that.
%w[HUP INT PIPE QUIT TERM].each do |signame|
Signal.trap(signame) do
$stdout.puts
exit 1
end
rescue ArgumentError
# the operating system doesn't support the signal, try the next list element
end
require 'socket'
def usage
me = File.basename($PROGRAM_NAME)
$stderr.puts "usage: #{me} [-v] host_name_or_address service_name_or_port_number"
$stderr.puts " or: #{me} [-v] host_name_or_address:service_name_or_port_number"
exit 1
end
# address family to string or nil
def af_to_s(addr)
if addr.ipv4?
'IPv4'
elsif addr.ipv6?
'IPv6'
end
end
# VERY simple command line argument handling follows...
verbose = if ARGV[0] == '-v'
ARGV.shift
true
else
false
end
case ARGV.size
when 1
if ARGV[0] =~ /^(.*):([^:]+)$/
host = Regexp.last_match(1)
port = Regexp.last_match(2)
else
usage
end
when 2
host, port = *ARGV
else
usage
end
host = host.strip # get thawed copy of host with white space removed
port = port.strip # get thawed copy of port with white space removed
host = Regexp.last_match(1) if host =~ /^\[(.*)\]$/
usage if host.empty? || port.empty?
begin
addrs = Addrinfo.getaddrinfo(host, port, nil, :STREAM, nil, Socket::AI_ALL)
rescue SocketError => e
$stderr.puts "error: host #{host}: #{e}"
exit 2
end
if addrs.empty? # can this ever happen?
$stderr.puts "error: host #{host}: getaddrinfo failed to return anything!"
exit 3
end
num_conn = 0
num_addrs = 0
addrs.each do |addr|
family = af_to_s(addr)
next if family.nil? # not an IPv4 or IPv6 address
num_addrs += 1
# Get an IPv4 or IPv6 socket (which is determined by the address family).
s = Socket.new(addr.afamily, Socket::SOCK_STREAM)
# Connect in non-blocking mode. If the connection can be made immediately,
# then skip the select() call. If the connection can't be made immediately,
# then use select() to wait up to 10 seconds for the connection to be
# allowed. If select() times out, then just go on to the next address.
begin
s.connect_nonblock(addr.to_sockaddr)
rescue Errno::EINPROGRESS
next if IO.select(nil, [s], nil, 10).nil?
begin
# Socket is ready. Attempt real connection.
s.connect_nonblock(addr.to_sockaddr)
rescue Errno::EISCONN
# connected
rescue Errno::ECONNREFUSED => e
$stderr.puts "error: #{e}" if verbose
next
rescue SystemCallError => e
$stderr.puts "error: #{e}" if verbose
next
end
rescue Errno::ENETUNREACH => e
$stderr.puts "error: #{e}" if verbose
next
end
puts "connected #{family} #{host}"
num_conn += 1
end
if num_addrs.zero?
$stderr.puts "error: no IPv4 or IPv6 addresses found for #{host}"
exit 4
elsif num_conn.zero?
$stderr.puts 'error: all connections failed'
exit 5
else
exit 0
end