Ruby - посмотреть, открыт ли порт - PullRequest
42 голосов
/ 05 февраля 2009

Мне нужен быстрый способ узнать, открыт ли данный порт в 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.

Ответы [ 7 ]

48 голосов
/ 05 февраля 2009

Может работать что-то вроде следующего:

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
27 голосов
/ 26 января 2012

Более идиоматический синтаксис 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
12 голосов
/ 08 июля 2016

Все остальные существующие ответы нежелательны. Использование Timeout не рекомендуется . Возможно, все зависит от версии ruby. По крайней мере, начиная с 2.0 можно просто использовать:

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

Для более старого ruby ​​лучший метод, который я мог бы найти, это использовать неблокирующий режим, а затем select Описано здесь:

12 голосов
/ 31 марта 2014

Я недавно придумал это решение, используя команду unix lsof:

def port_open?(port)
  !system("lsof -i:#{port}", out: '/dev/null')
end
10 голосов
/ 06 февраля 2009

Просто для полноты, 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

2 голосов
/ 05 сентября 2016

Все * платформы 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`
1 голос
/ 21 февраля 2014

Мое небольшое отклонение от ответа Криса Райса. Тем не менее обрабатывает тайм-аут на одну попытку, но также позволяет несколько попыток, пока вы не сдадите.

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