Pergunta

Quero obter qualquer porta TCP aberta aleatória no localhost em Python. Qual é a maneira mais fácil?

Foi útil?

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top