2010-05-14 36 views
19

Quiero obtener cualquier puerto TCP abierto aleatorio en localhost en Python. ¿Cuál es la manera más sencilla?obtener puerto TCP abierto en Python

+4

Esto es como perros persiguiendo coches: ¿qué vas a hacer cuando lo consigas? –

+1

Necesito un número de puerto TCP para otra aplicación de consola que necesita un puerto como parámetro donde se escuchará. – Albert

+4

Hay una condición de carrera obvia (aunque menor) con el enfoque que está contemplando. El puerto puede tomarse para cuando lo pase por el otro proceso. ¿Eres dueño del código del otro proceso? Si es así, podría ser mejor obtener un puerto aleatorio e informarlo al primer proceso. –

Respuesta

37

Mi solución actual:

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 

No es muy agradable y no es 100% correcto, pero funciona por ahora.

+7

¿Por qué la llamada listen()? Para mí, s.getsockname() [1] se completa tan pronto como llamo bind() – ncoghlan

+0

¿Es necesario llamar a 's.shutdown()' antes de 's.close()' en este caso? –

2

De hecho, me haga lo siguiente en uno de mis programas:

port = random.randint(10000,60000) 

Por supuesto, esto es aún más propenso a las colisiones que el código que tiene. Pero nunca tuve un problema con eso. El punto es que, en un momento dado, la mayoría de los puertos con numeración alta no están en uso y si solo elige uno al azar, es poco probable tener un conflicto con otro proceso. Si hace algo así como la solución que publicó en su respuesta (abrir un socket y tomar su número de puerto), es casi seguro que el puerto no va a entrar en conflicto. Entonces, si esto es algo que solo usará usted (en lugar de algo que va a lanzar al público), piense si vale la pena encontrar una solución verdaderamente a prueba de balas. Las probabilidades son que nunca harán la diferencia.

Motivado por el comentario de Marcelo Cantos sobre su pregunta, añadiré que la solución estándar en casos como este es hacer que el proceso que utilizará el puerto se vincule y luego compartir esa información con cualquier otro programa que necesite eso. Por lo general, hará algo así como escribir un archivo temporal que contenga el número de puerto en alguna ubicación estándar en el sistema de archivos. Dado que el proceso con el que estás trabajando no hace eso, en cierto sentido cualquier solución que se te ocurra será un truco. Pero, de nuevo, si es solo para tu propio uso, probablemente esté bien.

1

Esta es mi versión, sin embargo, no es realmente aleatoria si especifica un rango de puertos. Esto también sufrirá por las condiciones de carrera, pero esta es la mejor forma que conozco si necesita conocer el puerto con anticipación.

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') 
+0

Desafortunadamente, esto no es 100% a prueba de fallas y puede devolver True cuando el puerto está realmente en uso por otra aplicación. No he descifrado por qué, pero.bind() no siempre fallará cuando otro proceso ya esté escuchando en el puerto. – Joe

+0

@Joe ¿es posible que la otra aplicación esté vinculada a una dirección diferente pero con el mismo puerto? o familia de dirección diferente? –

+0

Es posible. Todo lo que sé es que, en mi caso, la aplicación de servidor que estaba tratando de iniciar no pudo abrir el puerto aunque el código anterior decía que el puerto era libre. La otra aplicación era erl.exe (erlang), que obtuve a través de la configuración de RabbitMQ si quieres probarlo por ti mismo. – Joe

1

Los puertos efímeros básicamente se encuentran en el rango de 49152 - 65535. si desea comprobar los puertos en serie más grande, entonces simplemente cambiar los valores en 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 
4

El puerto libre se puede encontrar al vincular un socket a un puerto seleccionado por el sistema operativo. Después de que el sistema operativo selecciona un puerto, se puede eliminar el zócalo. Sin embargo, esta solución no es resistente a las condiciones de carrera: en el corto tiempo entre obtener el número de puerto libre y usar este puerto, otro proceso puede usar este puerto.

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. 
Cuestiones relacionadas