2009-02-05 35 views
38

Necesito una forma rápida de averiguar si un puerto determinado está abierto con Ruby. Actualmente estoy perder el tiempo con esto:Ruby: compruebe si hay un puerto abierto

require 'socket' 

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

Funciona muy bien si el puerto está abierto, pero la desventaja de esto es que en ocasiones se acaba de sentarse y esperar durante 10-20 segundos y luego con el tiempo el tiempo de espera, lanzando una excepción ETIMEOUT (si el puerto está cerrado). Mi pregunta es así:

¿Se puede modificar este código para que solo espere un segundo (y devuelva false si no recibimos nada para entonces) o existe una mejor manera de verificar si un puerto determinado está abierto en un host determinado? ?

Edit: El código de llamada bash también es aceptable siempre que funcione multiplataforma (por ejemplo, Mac OS X, * nix y Cygwin), aunque prefiero el código Ruby.

Respuesta

43

algo como lo siguiente podría funcionar:

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 
+0

funciona como un encanto! ¡Gracias! –

+0

Tuve algunos problemas con este bloqueo (creo). Básicamente, el tiempo de espera no excedería el tiempo de espera. No estoy seguro por qué, pero la solución de netcat funcionó bien en su lugar. –

+2

Esta respuesta tiene una solución que también funciona en Windows: http://stackoverflow.com/a/3473208/362951 – mit

10

simplemente para la corrección, el golpe sería algo como esto:

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

-w 1 especifica un tiempo de espera de 1 segundo y -q 0 dice que, cuando esté conectado, cierre la conexión tan pronto como stdinEOF (lo que /dev/null hará de inmediato).

Bash también tiene sus propios servicios/UDP incorporados en TCP, pero son una opción en tiempo de compilación y no tengo un golpe compilado con ellos: P

+1

Son bastante simples: solo pretende que/dev/{tcp}/HOST/PORT son archivos :) – ephemient

+2

Para referencia futura, encontré esto como 'nc' en mi sistema en lugar de' netcat' – HXCaine

+1

Advertencia: En MacOS X, esto da el error 'nc: opción inválida - q'. Lo siguiente funciona tanto en MacOS X como en Ubuntu, y me parece más simple: 'nc -z $ HOST $ PORT' – mercurial

26

Más Rubí sintaxis 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 
+0

Esto dio un falso positivo para las entradas '192.0.2.0', 80, 10 que no deberían ser válidas (según http://stackoverflow.com/questions/10456044/what-is-a-good-invalid-ip-address-to-use-for-unit-tests). Obtuve el mismo resultado con Ruby 1.9.3p448 y 2.0.0p195, ambos en Mac. ¿En qué situaciones logra este método devolver falso? (Incluso intenté escribir en el zócalo antes de cerrarlo, ¡pero aún así me salió bien!) –

+0

Acabo de probar '0.0.0.0', 80, 1 y esto también dio la verdad. –

+0

funciona bien para mí! – sunsations

1

Mi pequeña variación a la respuesta de Chris Rice. Aún maneja el tiempo de espera en un solo intento, pero también permite múltiples intentos hasta que te des por vencido.

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 
9

Recientemente me encontré con esta solución, haciendo uso del comando UNIX lsof:

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

Esto fue muy bueno para mí. Quería introducir un sistema de asignación de puertos a máquinas virtuales en vagabundo y escribí este one-liner para verificar si el puerto que estaba a punto de asignar estaba abierto o no: 'vms ['port'] + = 1 while ports. ¿incluir? vms ['port'] o system ("lsof -i: # {vms ['port']}") ' – Dannid

+1

Esto solo funciona para el usuario que ha iniciado sesión. Para trabajar en general, use 'sudo lsof -i: ' – alpinweis

+0

Tuve que eliminar el '!' (Operador no) para que esto funcione. –

7

El resto de respuesta existente no son deseables. El uso de Timeout es discouraged. Tal vez las cosas dependen de la versión de ruby. Al menos desde 2,0 simplemente se puede utilizar:

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

Para mayor Ruby el mejor método que pude encontrar es el uso de modo de no bloqueo y luego select. Se describe aquí:

+3

Funcionó perfectamente para mí: 'port_is_open = Socket.tcp (host, puerto, connect_timeout: 5) {true} rescue false'. Es fácil expandirse desde un solo linaje para rescatar las excepciones específicas que se necesitan. – anothermh

2

Todas las plataformas * nix:

intento de comando nc/netcat de la siguiente manera.

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

El distintivo -z se puede utilizar para indicar a nc que informe los puertos abiertos, en lugar de iniciar una conexión.

La bandera w significa tiempo de espera para los conecta y lee neto final

La bandera -G es tiempo de espera de conexión en segundos

Uso bandera -n para trabajar con la dirección IP en lugar de nombre de host.

Ejemplos:

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