2010-05-14 19 views
19

Voglio ottenere qualsiasi porta TCP aperta casuale su localhost in Python. Qual è il modo più semplice?aprire la porta TCP in Python

+4

Questo è come i cani che inseguono le auto - cosa hai intenzione di fare quando ne hai uno? –

+1

Ho bisogno di un numero di porta TCP per un'altra applicazione console che necessita di una porta come parametro su cui ascolterà. – Albert

+4

C'è una condizione di gara evidente (anche se minore) con l'approccio che stai contemplando. La porta potrebbe essere presa quando si passa l'altro processo. Possiedi il codice dell'altro processo? Se è così, potrebbe essere meglio fargli acquisire una porta casuale e riportarla al primo processo. –

risposta

37

mia soluzione attuale:

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 

Non è molto bello e, inoltre, non corretta al 100%, ma funziona per ora.

+7

Perché la chiamata listen()? Per me, s.getsockname() [1] viene popolato non appena chiamo bind() – ncoghlan

+0

È necessario chiamare 's.shutdown()' prima di 's.close()' in questo caso? –

2

ho effettivamente utilizzare il seguente in uno dei miei programmi:

port = random.randint(10000,60000) 

Naturalmente, questo è ancora più incline a collisioni rispetto al codice che avete. Ma non ho mai avuto un problema con questo. Il punto è che, in qualsiasi momento, la maggior parte di quelle porte ad alto numero non sono in uso e se ne scegli una a caso, avere un conflitto con un altro processo è piuttosto improbabile. Se fai qualcosa come la soluzione che hai postato nella tua risposta (aprendo un socket e prendendo il suo numero di porta), è quasi certo che la porta non entrerà in conflitto. Quindi, se questo è qualcosa che userete solo per voi stessi (al contrario di qualcosa che pubblicherete al pubblico), pensate se valga la pena di proporre una soluzione veramente a prova di proiettile. Le probabilità sono che non farà mai la differenza.

Motivato dal commento di Marcelo Cantos sulla tua domanda, aggiungerò che la soluzione standard in casi come questo è di avere il processo che userà la porta vincolare ad esso e quindi condividere tali informazioni con qualsiasi altro programma che necessita esso. Tipicamente farà qualcosa di simile alla scrittura di un file temporaneo contenente il numero di porta in una posizione standard nel filesystem. Dal momento che il processo con cui stai lavorando non lo fa, in un certo senso qualunque soluzione tu crei sarà un po 'incisiva. Ma di nuovo, se è solo per uso personale, probabilmente va bene.

1

Questa è la mia versione, tuttavia non è davvero casuale se si specifica un intervallo di porte. Questo soffrirà anche delle condizioni di gara, ma questo è il modo migliore che conosca se hai bisogno di conoscere il porto prima del tempo.

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

Sfortunatamente, questo non è failproof al 100% e potrebbe restituire True quando la porta è effettivamente utilizzata da un'altra applicazione. Non ho inchiodato perché, ma.bind() non fallirà sempre quando un altro processo è già in ascolto sulla porta. – Joe

+0

@Joe è possibile che l'altra applicazione sia associata a un indirizzo diverso ma con la stessa porta? o famiglia di indirizzi diversi? –

+0

È possibile. Tutto quello che so è che nel mio caso, l'applicazione server che stavo tentando di avviare non è riuscita ad aprire la porta anche se il codice sopra riportato diceva che la porta era libera. L'altra applicazione era erl.exe (erlang), che ho ottenuto con l'installazione di RabbitMQ se vuoi provare tu stesso. – Joe

1

Le porte effimere in fondo si trovano nella gamma 49152 - 65535. se si desidera controllare porti più grande gamma poi basta cambiare i valori in 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

La porta libera può essere trovata collegando un socket a una porta selezionata dal sistema operativo. Dopo che il sistema operativo seleziona una porta, il socket può essere eliminato. Tuttavia, questa soluzione non è resistente alle condizioni di gara - nel breve lasso di tempo tra l'ottenimento del numero di porta libera e l'utilizzo di questa porta, altri processi potrebbero utilizzare questa 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.