48 votes

Ruby - Voir si un port est ouvert

J'ai besoin d'un moyen rapide de savoir si un port donné est ouvert avec Ruby. Je suis en train de bricoler avec ceci :

require 'socket'

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

Cela fonctionne très bien si le port est ouvert, mais l'inconvénient de cette méthode est qu'elle peut parfois rester en attente pendant 10 à 20 secondes avant de s'arrêter, ce qui entraîne l'apparition d'un message d'erreur. ETIMEOUT exception (si le port est fermé). Ma question est donc la suivante :

Peut-on modifier ce code pour qu'il n'attende qu'une seconde (et qu'il renvoie l'information) ? false si nous ne recevons rien d'ici là) ou existe-t-il un meilleur moyen de vérifier si un port donné est ouvert sur un hôte donné ?

Edit : L'appel au 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.

55voto

joast Points 858

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 votes

J'ai eu quelques difficultés avec ce blocage (je pense). En fait, le délai d'attente ne s'écoulait pas vraiment. Je ne sais pas trop pourquoi, mais la solution netcat a bien fonctionné à sa place.

2 votes

Cette réponse propose une solution qui fonctionne également sous Windows : stackoverflow.com/a/3473208/362951

2 votes

Ne devrait-on pas intervertir le vrai et le faux ?

27voto

hipertracker Points 1417

Plus de syntaxe idiomatique 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

0 votes

Cela a donné un faux positif pour les entrées '192.0.2.0', 80, 10 qui ne devraient pas être valides (d'après l'indicateur stackoverflow.com/questions/10456044/ ). 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 false ? (J'ai même essayé d'écrire sur le socket avant de le fermer, mais cela renvoie toujours true).

0 votes

Je pense qu'il manque à cette fonction def une déclaration de début avant le timeout, ou est-ce optionnel ? ( rescue Timeout::Error devrait être dans un begin bloc, n'est-ce pas ?)

0 votes

@nash Ruby permet à une clause de sauvetage d'appartenir à la méthode elle-même. En effet, la méthode est un bloc implicite.

16voto

Tobias Cohen Points 14390

J'ai récemment trouvé cette solution, en utilisant le système unix lsof commandement :

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

2 votes

C'était très agréable pour moi. Je voulais introduire un système d'attribution de ports aux machines virtuelles dans vagrant et j'ai écrit ce one-liner pour vérifier si le port que j'étais sur le point d'attribuer était ouvert ou non : vms['port'] += 1 while ports.include? vms['port'] or system("lsof -i:#{vms['port']}")

1 votes

Cela ne fonctionne que pour l'utilisateur connecté. Pour fonctionner de manière générale, utilisez sudo lsof -i:<port>

0 votes

J'ai dû retirer le ! (non opérateur) pour que cela fonctionne.

9voto

Porges Points 17745

Pour être complet, le Bash serait quelque chose comme ça :

$ 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, lorsqu'il est connecté, il faut fermer la connexion dès que possible. stdin donne EOF (qui /dev/null fera tout de suite).

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

1 votes

C'est assez simple : il suffit de faire comme si /dev/{tcp}/HOST/PORT étaient des fichiers :)

2 votes

Pour référence future, j'ai trouvé ceci comme nc sur mon système plutôt que netcat

1 votes

Avertissement : Sous MacOS X, cela donne l'erreur nc: invalid option -- q . La méthode suivante fonctionne à la fois sur MacOS X et Ubuntu, et me semble plus simple : nc -z $HOST $PORT

1voto

Manther Points 1

Ma petite variation à la réponse de Chris Rice. Elle gère toujours le dépassement du délai d'attente lors d'une seule tentative, mais autorise également de multiples 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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X