Ruby——查看端口是否打开

时间:2022-12-11 18:16:53

I need a quick way to find out if a given port is open with Ruby. I currently am fiddling around with this:

我需要一种快速的方法来确定给定的端口是否用Ruby打开。我现在正在摆弄这个:

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). My question is thus:

如果端口是打开的,那么它会工作得很好,但是它的缺点是,它偶尔只会等待10-20秒,然后最终超时,抛出一个ETIMEOUT异常(如果端口是关闭的)。我的问题是:

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 (e.g., Mac OS X, *nix, and Cygwin), although I do prefer Ruby code.

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

7 个解决方案

#1


46  

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

#2


27  

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

#3


10  

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).

- w1指定1秒的超时,而- q0表示,当连接时,只要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

#4


10  

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

#5


8  

All other existing answer are undesirable. Using Timeout is discouraged. Perhaps things depend on ruby version. At least since 2.0 one can simply use:

所有其他现有的答案都是不可取的。使用超时是气馁。也许事情取决于ruby版本。至少从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. Described here:

对于旧的ruby,我能找到的最好的方法是使用非阻塞模式,然后选择。这里描述:

#6


2  

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`

#7


1  

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

#1


46  

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

#2


27  

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

#3


10  

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).

- w1指定1秒的超时,而- q0表示,当连接时,只要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

#4


10  

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

#5


8  

All other existing answer are undesirable. Using Timeout is discouraged. Perhaps things depend on ruby version. At least since 2.0 one can simply use:

所有其他现有的答案都是不可取的。使用超时是气馁。也许事情取决于ruby版本。至少从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. Described here:

对于旧的ruby,我能找到的最好的方法是使用非阻塞模式,然后选择。这里描述:

#6


2  

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`

#7


1  

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