web-dev-qa-db-fra.com

Ruby - Voir si un port est ouvert

Je besoin d'un moyen rapide de savoir si un port donné est ouvert avec Ruby. Je joue actuellement 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 bien si le port est ouvert, mais l'inconvénient de ceci est que, de temps en temps, il va simplement s'asseoir et attendre 10 à 20 secondes, puis éventuellement temps, jetant une exception ETIMEOUT (si le port est fermé) . Ma question est donc:

Ce code peut-il être modifié pour attendre une seconde (et retour false si nous ne récupérons rien d'ici à ce moment-là) ou y a-t-il un meilleur moyen de vérifier si un port donné est ouvert sur un hôte donné?

EDIT : appel de code Bash est également acceptable aussi longtemps que cela fonctionne dans la plate-forme multiplate-forme (par exemple, Mac OS X, * Nix et Cygwin), bien que je préfère Ruby = code.

43
Chris Bunch

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
53
joast

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
29
hipertracker

Toutes les autres réponses existantes sont indésirables. Utilisation Timeout est découragé . Peut-être que les choses dépendent de la versionRuby. Au moins depuis 2.0 on peut simplement utiliser:

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

Pour plus vieux Ruby la meilleure méthode que je pouvais trouver est d'utiliser le mode non bloquant, puis select. Décrit ici:

17
akostadinov

J'ai récemment proposé cette solution, en utilisant la commande UNIX lsof:

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

Juste pour la complétude, la 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é, fermez la connexion dès que stdin donne EOF (quel /dev/null va faire tout de suite).

Bash a également 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

10
porges

Toutes les plateformes * Nix:

essayez la commande nc/netcat comme suit.

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

Le drapeau -Z peut être utilisé pour indiquer à NC de signaler des ports ouverts plutôt que d'initier une connexion.

Le drapeau-w signifie le délai d'expiration de connexion et des lectures finales nettes

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

Utilisez -n drapeau pour fonctionner avec l'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`
2
Mikhail Chuprynski

Ma légère variation de la réponse de Chris Rice. Poignée toujours sur une seule tentative, mais permet é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
1
Manther