Obtenha a porta TCP aberta em Python
Pergunta
Quero obter qualquer porta TCP aberta aleatória no localhost em Python. Qual é a maneira mais fácil?
Solução
Minha solução atual:
def get_open_port():
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("",0))
s.listen(1)
port = s.getsockname()[1]
s.close()
return port
Não é muito bom e também não está 100% correto, mas funciona por enquanto.
Outras dicas
A porta livre pode ser encontrada vinculando um soquete a uma porta selecionada pelo sistema operacional. Depois que o sistema operacional seleciona uma porta, o soquete pode ser descartado. No entanto, essa solução não é resistente às condições de corrida - no curto período de tempo, entre obter o número da porta gratuita e o uso desse processo da porta pode usar esta porta.
def find_free_port():
s = socket.socket()
s.bind(('', 0)) # Bind to a free port provided by the host.
return s.getsockname()[1] # Return the port number assigned.
Na verdade, eu uso o seguinte em um dos meus programas:
port = random.randint(10000,60000)
Obviamente, isso é ainda mais propenso a colisões do que o código que você possui. Mas nunca tive um problema com isso. O ponto é que, a qualquer momento, a maioria dessas portas de alto número não está em uso e, se você apenas escolher um aleatoriamente, ter um conflito com outro processo é bastante improvável. Se você fizer algo como a solução que postou em sua resposta (abrindo um soquete e agarrando o número da porta), é quase certo que a porta não vai conflito. Portanto, se isso é algo que você só usará para si mesmo (em oposição a algo que você lançará ao público), pense se vale a pena encontrar uma solução verdadeiramente à prova de balas. As chances são de que nunca fará diferença.
Motivado pelo comentário de Marcelo Canto sobre sua pergunta, acrescentarei que a solução padrão em casos como esse é ter o processo que usará a porta vincular a ela e compartilhar essas informações com qualquer outro programa que precise. Normalmente, ele fará algo como escrever um arquivo temporário contendo o número da porta em algum local padrão no sistema de arquivos. Como o processo com o qual você está trabalhando não faz isso, em certo sentido, qualquer que seja a solução que você encontrar será um pouco de hack. Mas, novamente, se for apenas para seu próprio uso, provavelmente está bem.
Esta é a minha versão, no entanto, não é realmente aleatório se você especificar um intervalo de portas. Isso também sofre de condições de corrida, mas esta é a melhor maneira que eu sei se você precisa conhecer o porto com antecedência.
import socket
import errno
import contextlib
reserved_ports = set()
def get_open_port(lowest_port = 0, highest_port = None, bind_address = '', *socket_args, **socket_kwargs):
if highest_port is None:
highest_port = lowest_port + 100
while lowest_port < highest_port:
if lowest_port not in reserved_ports:
try:
with contextlib.closing(socket.socket(*socket_args, **socket_kwargs)) as my_socket:
my_socket.bind((bind_address, lowest_port))
this_port = my_socket.getsockname()[1]
reserved_ports.add(this_port)
return this_port
except socket.error as error:
if not error.errno == errno.EADDRINUSE:
raise
assert not lowest_port == 0
reserved_ports.add(lowest_port)
lowest_port += 1
raise Exception('Could not find open port')
As portas efêmeras estão basicamente no intervalo 49152 - 65535. Se você deseja verificar as portas em maior alcance, basta alterar os valores em Randint.
import pustil
from random import randint
def getfreeport():
port = randint(49152,65535)
portsinuse=[]
while True:
conns = pstuil.net_connections()
for conn in conns:
portsinuse.append(con.laddr[1])
if port in portsinuse:
port = randint(49152,65535)
else:
break
return port