2009-02-05 6 views
38

je besoin d'un moyen rapide de savoir si un port donné est ouvert avec Ruby. Je suis actuellement bidouiller avec ceci:Ruby - Voir si un port est ouvert

require 'socket' 

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

Il fonctionne très bien si le port est ouvert, mais l'inconvénient est que, parfois, il va juste rester assis et attendre pendant 10-20 secondes, puis chronométrer finalement sortir, jeter une exception ETIMEOUT (si le port est fermé). Ma question est donc:

ce code peut être modifié qu'à attendre une seconde (et retour false si nous ne recevons rien de nouveau d'ici là) ou est-il une meilleure façon de vérifier si un port donné est ouvert sur un hôte donné ? L'appel du code bash est également acceptable tant qu'il fonctionne sur plusieurs plates-formes (par exemple, Mac OS X, * nix et Cygwin), bien que je préfère le code Ruby.

Répondre

43

Quelque chose comme ce qui suit pourrait fonctionner:

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

fonctionne comme un charme! Merci! –

+0

J'ai eu quelques problèmes avec ce blocage (je pense). Fondamentalement, le délai d'expiration ne serait pas réellement expiré. Je ne sais pas pourquoi, mais la solution netcat a bien fonctionné à sa place. –

+2

Cette réponse a une solution qui fonctionne également sur les fenêtres: http://stackoverflow.com/a/3473208/362951 – mit

10

Juste pour être complet, le Bash serait quelque chose comme ceci:

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

-w 1 spécifie un délai d'attente de 1 seconde et -q 0 dit que, lorsque vous êtes connecté, fermez la connexion dès que stdin donne EOF (qui /dev/null fera tout de suite).

Bash a aussi ses propres services intégrés TCP/UDP, mais ils sont une option de compilation et je n'ai pas Bash compilé avec eux: P

+1

Ils sont assez simple: il suffit de faire semblant/dev/{} tcp/HOST/PORT sont des fichiers :) – ephemient

+2

pour toute référence ultérieure, je trouve cela comme 'nc' sur mon système plutôt que' netcat' – HXCaine

+1

Avertissement: Sur Mac OS X, ceci donne l'erreur 'nc: option invalide - q'. Les travaux suivants sur Mac OS X et Ubuntu, et semble plus simple pour moi: 'nc -Z $ HOST PORT' de $ – mercurial

26

Plus Ruby syntaxe idiomatiques:

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

Cela a donné un faux positif pour les entrées de la 192.0.2.0 ', 80, 10 qui devrait être invalide (selon http://stackoverflow.com/questions/10456044/what-is-a-good-invalid-ip-address-to-use-for-unit-tests). J'ai obtenu le même résultat avec Ruby 1.9.3p448 et 2.0.0p195, tous deux sur Mac. Dans quelles situations cette méthode parvient-elle à retourner faux? (J'ai même essayé d'écrire sur le socket avant de le fermer, mais cela restait vrai!) –

+0

Juste essayé '0.0.0.0', 80, 1 et cela a aussi donné vrai. –

+0

fonctionne très bien pour moi! – sunsations

1

Ma légère variation de la réponse de Chris Rice. Il gère toujours le délai d'attente en une seule tentative, mais autorise également plusieurs tentatives jusqu'à ce que vous abandonniez.

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

Je suis récemment venu avec cette solution, en utilisant la commande unix lsof:

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

C'était très bien pour moi. Je voulais introduire un système d'assignation de ports aux machines virtuelles dans vagrant et écrire ce one-liner pour vérifier si le port que j'allais assigner était ouvert ou non: 'vms ['port'] + = 1 tant que les ports. comprendre? vms ['port'] ou system ("lsof -i: # {vms ['port']}") ' – Dannid

+1

Ceci ne fonctionne que pour l'utilisateur connecté. Pour toute la ligne, utilisez 'sudo lsof -i: ' – alpinweis

+0

J'ai dû enlever le '' (non opérateur) pour obtenir ce travail!. –

7

Toute autre réponse existante sont indésirables. L'utilisation Timeout est discouraged. Peut-être que les choses dépendent de la version de rubis. Depuis au moins 2.0, on peut simplement utiliser:

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

Pour plus Ruby la meilleure méthode que je pourrais trouver est d'utiliser le mode non-bloquant et select. Décrite ici:

+3

Fonctionne parfaitement pour moi: 'port_is_open = Socket.tcp (hôte, port, connect_timeout: 5) {true} rescue false'. Il est facile de développer à partir d'un one-liner pour sauver les exceptions spécifiques nécessaires. – anothermh

2

Toutes les plates-formes * nix:

essayer commande nc/netcat comme suit. L'indicateur -z peut être utilisé pour indiquer à nc de signaler les ports ouverts, plutôt que d'initier une connexion.

Le moyen indicateur -w Délai d'attente pour Connects et net final lit

Le drapeau -G est le délai de connexion en secondes

Utiliser l'option -n pour travailler avec une adresse IP plutôt que le nom d'hôte.

Exemples:

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