我需要一种快速方法来查明给定端口是否已使用 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 如果到那时我们什么也没得到)或者是否有更好的方法来检查给定主机上给定端口是否打开?

编辑: 调用 bash 代码也是可以接受的,只要它可以跨平台运行(例如,Mac OS X、*nix 和 Cygwin),尽管我更喜欢 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

其他提示

更多红宝石惯用语法:

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

所有其他现有答案都是不可取的。使用 Timeout灰心. 。也许事情取决于 ruby​​ 版本。至少从 2.0 开始,人们可以简单地使用:

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

对于较旧的红宝石,我能找到的最好方法是使用非阻塞模式,然后 select. 。此处描述:

我最近想出了这个解决方案,使得使用UNIX lsof命令:

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

只是为了保持完整性,猛砸会是这样的:

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

-w 1指定1秒的超时,并-q 0说,当连接时,一旦关闭连接作为stdin给出EOF(其/dev/null将做直线距离)。

击也有其自身内置的TCP / UDP服务,但他们是一个时间编译选项,我没有击编译它们:P

所有* 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`

我稍有改变克里斯·赖斯的回答。不过在处理一个单一的企图超时,但也允许多次重试,直到你放弃。

    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
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top