我需要一个快速的方法来确定某个端口是否使用Ruby打开。我目前正在摆弄这个:
require 'socket'
def is_port_open?(ip, port)
begin
TCPSocket.new(ip, port)
rescue Errno::ECONNREFUSED
return false
end
return true
end
如果端口是打开的,它的效果很好,但是它的缺点是它偶尔会坐下等待10-20秒然后最终超时,抛出ETIMEOUT
异常(如果端口是关闭)。我的问题是:
这段代码可以修改为只等待一秒钟(如果我们当时没有得到任何回报,则返回false
)或者是否有更好的方法来检查给定主机上的给定端口是否打开?< / p>
编辑:只要它跨平台工作(例如,Mac OS X,* nix和Cygwin),调用bash代码也是可以接受的,尽管我更喜欢Ruby代码。
答案 0 :(得分:48)
以下内容可能有效:
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
答案 1 :(得分:27)
更多Ruby惯用语法:
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
答案 2 :(得分:12)
我最近提出了这个解决方案,使用了unix lsof
命令:
def port_open?(port)
!system("lsof -i:#{port}", out: '/dev/null')
end
答案 3 :(得分:12)
所有其他现有答案都是不受欢迎的。使用Timeout
是discouraged。也许事情取决于ruby版本。至少从2.0开始就可以使用:
Socket.tcp("www.ruby-lang.org", 10567, connect_timeout: 5) {}
对于较旧的ruby,我能找到的最佳方法是使用非阻塞模式,然后使用select
。这里描述:
答案 4 :(得分:10)
为了完整起见,Bash会是这样的:
$ netcat $HOST $PORT -w 1 -q 0 </dev/null && do_something
-w 1
指定超时1秒,而-q 0
表示,连接后,stdin
提供EOF
后/dev/null
关闭连接我会马上做的。)
Bash也有自己的内置TCP / UDP服务,但它们是编译时选项,我没有用它们编译的Bash:P
答案 5 :(得分:2)
所有* nix平台:
尝试nc / netcat命令如下。
`nc -z -w #{timeout_in_seconds} -G #{timeout_in_seconds} #{host} #{port}`
if $?.exitstatus == 0
#port is open
else
#refused, port is closed
end
-z标志可用于告诉nc报告开放端口,而不是发起连接。
-w标志表示连接和最终网络读取的超时
-G标志是以秒为单位的连接超时
使用-n标志来处理IP地址而不是主机名。
示例:
# `nc -z -w 1 -G 1 google.com 80`
# `nc -z -w 1 -G 1 -n 123.234.1.18 80`
答案 6 :(得分:1)
我对Chris Rice的回答略有不同。仍然可以在单次尝试时处理超时,但也允许多次重试,直到您放弃为止。
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
答案 7 :(得分:0)
我的解决方案来自发布的解决方案。
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