Ruby - Veja se a porta está aberta
-
21-08-2019 - |
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
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 stdin
dá EOF
(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