Ruby - 查看端口是否打开

时间:2009-02-05 18:24:58

标签: ruby bash network-programming port

我需要一个快速的方法来确定某个端口是否使用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代码。

8 个答案:

答案 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)

所有其他现有答案都是不受欢迎的。使用Timeoutdiscouraged。也许事情取决于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