Pergunta

Eu preciso de uma maneira rápida de descobrir se uma determinada porta é aberta com Ruby. Eu atualmente estou brincar com isto:

require 'socket'

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

Ele funciona muito bem se a porta está aberta, mas a desvantagem desta situação é que, ocasionalmente, ele vai apenas sentar e esperar por 10-20 segundos e depois, eventualmente, tempo fora, jogando uma exceção ETIMEOUT (se a porta está fechada). A minha pergunta é o seguinte:

Pode este código ser alterado apenas esperar por um segundo (e false retorno se não recebemos nada de volta até então) ou há uma maneira melhor para verificar se uma determinada porta está aberta em um determinado host?

Editar: Código de ligação bash é aceitável também, contanto que funciona em várias plataformas (por exemplo, Mac OS X, * nix, e Cygwin), embora eu prefiro código Ruby

Foi útil?

Solução

Algo como o seguinte trabalho podem:

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

Outras dicas

Mais de Ruby sintaxe idiomática:

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

Todas as outras resposta existente são indesejáveis. Usando Timeout é desanimado . Talvez as coisas dependem da versão rubi. Pelo menos desde 2.0 pode-se simplesmente usar:

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

Para mais velha rubi o melhor método que eu poderia encontrar é usar o modo não-bloqueio e, em seguida, select. Descrito aqui:

Recentemente, surgiu com esta solução, fazendo uso do comando lsof unix:

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

Apenas para completar, o Bash seria algo como isto:

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

-w 1 especifica um tempo limite de 1 segundo, e -q 0 diz que, quando ligado, perto da conexão assim que stdinEOF (que /dev/null vai fazer de imediato).

Bash também tem a sua própria base de serviços TCP / UDP, mas eles são uma opção em tempo de compilação e eu não tenho um Bash compilado com eles: P

Todas as plataformas * nix:

tentar nc / comando netcat como segue.

`nc -z -w #{timeout_in_seconds} -G #{timeout_in_seconds} #{host} #{port}`
if $?.exitstatus == 0
  #port is open
else
  #refused, port is closed
end

A bandeira -z pode ser usado para dizer nc denunciar portas abertas, em vez de iniciar uma conexão.

O sinalizador -w significa tempo de espera para se conecta e líquido final lê

A bandeira -G é tempo limite de conexão em segundos

Use -n bandeira para o trabalho com o endereço IP em vez de hostname.

Exemplos:

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

Meu ligeira variação para a resposta de Chris Rice. Ainda lida com o tempo limite em uma única tentativa, mas também permite que várias tentativas até que você desista.

    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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top