ruby – 看看一个端口是否打开

我需要一个快速的方法来找出给定的端口是否打开了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秒,然后超时,抛出一个ETIMEOUTexception(如果端口closures)。 我的问题是这样的:

这个代码是否可以修改为只等待一秒(如果到那时我们什么都得不到的话,返回false )还是有一个更好的方法来检查一个给定的端口是否在一个给定的主机上打开?

编辑:调用bash代码也是可以接受的,只要它能够跨平台工作(例如,Mac OS X,* nix和Cygwin),尽pipe我更喜欢Ruby代码。

像下面的东西可能会工作:

 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 

更多的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 

为了完整性,Bash应该是这样的:

 $ netcat $HOST $PORT -w 1 -q 0 </dev/null && do_something 

-w 1指定1秒的超时时间, -q 0表示当连接时,只要stdin给出EOF/dev/null将立即执行)就closures连接。

Bash也有自己的内置TCP / UDP服务,但它们是编译时选项,我没有用它们编译的Bash:P

我最近想出了这个解决scheme,使用了unix lsof命令:

 def port_open?(port) !system("lsof -i:#{port}", out: '/dev/null') end 

所有其他现有的答案是不可取的。 使用Timeout是不鼓励的 。 也许事情取决于ruby版本。 至less从2.0开始,可以简单地使用:

 Socket.tcp("www.ruby-lang.org", 10567, connect_timeout: 5) {} 

对于较老的ruby,我可以find最好的方法是使用非阻塞模式,然后select 。 在这里描述:

我对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 

所有* 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标志表示连接和最终networking读取的超时

-G标志是以秒为单位的连接超时

使用-n标志来处理IP地址而不是主机名。

例子:

 # `nc -z -w 1 -G 1 google.com 80` # `nc -z -w 1 -G 1 -n 123.234.1.18 80`