简体   繁体   English

Ruby - 查看端口是否打开

[英]Ruby - See if a port is open

I need a quick way to find out if a given port is open with Ruby.我需要一种快速的方法来确定给定的端口是否已使用 Ruby 打开。 I currently am fiddling around with this:我目前正在摆弄这个:

require 'socket'

def is_port_open?(ip, port)
  begin
    TCPSocket.new(ip, port)
  rescue Errno::ECONNREFUSED
    return false
  end
  return true
end

It works great if the port is open, but the downside of this is that occasionally it will just sit and wait for 10-20 seconds and then eventually time out, throwing a ETIMEOUT exception (if the port is closed).如果端口打开,它工作得很好,但它的缺点是偶尔它会坐下来等待 10-20 秒,然后最终超时,抛出ETIMEOUT异常(如果端口关闭)。 My question is thus:我的问题是:

Can this code be amended to only wait for a second (and return false if we get nothing back by then) or is there a better way to check if a given port is open on a given host?是否可以将此代码修改为仅等待一秒钟(如果到那时我们一无所获,则返回false ),或者是否有更好的方法来检查给定主机上的给定端口是否打开?

Edit: Calling bash code is acceptable also as long as it works cross-platform (eg, Mac OS X, *nix, and Cygwin), although I do prefer Ruby code.编辑:调用 bash 代码也是可以接受的,只要它跨平台工作(例如,Mac OS X、*nix 和 Cygwin),尽管我更喜欢 Ruby 代码。

Something like the following might work:像下面这样的东西可能会起作用:

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

More Ruby idiomatic syntax:更多 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

All other existing answer are undesirable.所有其他现有答案都是不可取的。 Using Timeout is discouraged . 不鼓励使用Timeout Perhaps things depend on ruby version.也许事情取决于 ruby​​ 版本。 At least since 2.0 one can simply use:至少从 2.0 开始可以简单地使用:

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

For older ruby the best method I could find is using non-blocking mode and then select .对于较旧的 ruby​​,我能找到的最佳方法是使用非阻塞模式,然后select . Described here:这里描述:

I recently came up with this solution, making use of the unix lsof command:我最近想出了这个解决方案,使用 unix lsof命令:

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

Just for completeness, the Bash would be something like this:为了完整起见,Bash 将是这样的:

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

-w 1 specifies a timeout of 1 second, and -q 0 says that, when connected, close the connection as soon as stdin gives EOF (which /dev/null will do straight away). -w 1指定 1 秒的超时, -q 0表示,连接后,只要stdin给出EOF就关闭连接( /dev/null将立即执行)。

Bash also has its own built-in TCP/UDP services, but they are a compile-time option and I don't have a Bash compiled with them :P Bash 也有自己的内置 TCP/UDP 服务,但它们是一个编译时选项,我没有用它们编译 Bash :P

My slight variation to Chris Rice's answer.我对克里斯赖斯的回答略有不同。 Still handles timing out on a single attempt but also allows multiple retries until you give up.仍然处理一次尝试超时,但也允许多次重试,直到您放弃。

    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

All *nix platforms:所有 *nix 平台:

try nc / netcat command as follow.尝试 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

The -z flag can be used to tell nc to report open ports, rather than initiate a connection. -z 标志可用于告诉 nc 报告打开的端口,而不是启动连接。

The -w flag means Timeout for connects and final net reads -w 标志表示连接和最终网络读取超时

The -G flag is connection timeout in seconds -G 标志是以秒为单位的连接超时

Use -n flag to work with IP address rather than hostname.使用 -n 标志来处理 IP 地址而不是主机名。

Examples:例子:

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

My solution is derived from the posted solutions.我的解决方案来自已发布的解决方案。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM