Подтвердить что ты не робот

Ruby - Посмотрите, открыт ли порт

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

4b9b3361

Ответ 1

Возможно, что-то вроде следующего:

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

Подробнее Идиоматический синтаксис 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

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

Ответ 4

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

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

Ответ 5

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

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

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

Ответ 6

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

    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

Все платформы * 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`