Su localhost, come posso scegliere un numero di porta gratuito?


161

Sto cercando di giocare con la comunicazione tra processi e dato che non sono riuscito a capire come usare le pipe con nome in Windows, ho pensato di usare i socket di rete. Tutto accade localmente. Il server è in grado di avviare gli slave in un processo separato ed è in ascolto su alcune porte. Gli schiavi fanno il loro lavoro e inviano il risultato al padrone. Come faccio a capire quale porta è disponibile? Presumo di non poter ascoltare sulla porta 80 o 21?

Sto usando Python, se ciò riduce le scelte.

Grazie!


1
Per inciso, se scegli semplicemente un numero di porta casuale o casuale (preferibilmente superiore a 1024), sarà probabilmente disponibile. Puoi persino usare la porta 80 o 21 o qualsiasi altra cosa, purché nessun altro programma stia ascoltando. In qualsiasi momento, su un sistema normale, sono in uso solo una piccola parte delle porte.
David Z,

19
Scegliere una porta casuale non è una buona idea: lascia che il sistema operativo ne scelga una per te.
Corehpf,

Risposte:


225

Non eseguire il binding a una porta specifica, né eseguire il binding alla porta 0, ad es sock.bind(('', 0)). Il sistema operativo sceglierà quindi una porta disponibile per te. È possibile ottenere la porta scelta utilizzando sock.getsockname()[1]e passarla agli slave in modo che possano riconnettersi.


4
Vedi stackoverflow.com/a/2838309/3538289 per un esempio disock.bind(('',0))
cevaris

10
Come si trasmette il numero agli schiavi? Mi sembra un problema con pollo e uova.
Sebastian

2
Se gli slave vengono creati dopo l'associazione, è possibile passarlo come parametro durante la creazione. In alternativa, è possibile scriverlo in una memoria condivisa o in un file a cui entrambi possono accedere, oppure un server centrale a cui si accede tramite un numero di porta noto potrebbe tenerne traccia.
mark4o

49

Per motivi di snippet di ciò che i ragazzi hanno spiegato sopra:

import socket
from contextlib import closing

def find_free_port():
    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
        s.bind(('', 0))
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        return s.getsockname()[1]

3
se su localhost: forse s.bind(('localhost', 0))è meglio
codeskyblue,

3
Buono anche aggiungere quanto segue in modo da poter riutilizzare rapidamente quella porta prima della dichiarazione di ritorno:s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
jonEbird

1
@jonEbird Aiuta socket.SO_REUSEADDRdavvero in questo caso? Da quello che ho letto, è rilevante solo il socket che sta cercando di associare SO_REUSEADDRed è irrilevante se quel flag sia impostato sul socket persistente.
Karl Bartel,

41

Associare il socket alla porta 0. Verrà selezionata una porta libera casuale da 1024 a 65535. È possibile recuperare la porta selezionata con getsockname()subito dopo bind().


2

Puoi ascoltare su qualunque porta tu voglia; in generale, le applicazioni utente dovrebbero ascoltare le porte 1024 e successive (attraverso 65535). La cosa principale se hai un numero variabile di ascoltatori è di assegnare un intervallo alla tua app, ad esempio 20000-21000, e CATCH EXCEPTIONS . In questo modo saprai se una porta è inutilizzabile (utilizzata da un altro processo, in altre parole) sul tuo computer.

Tuttavia, nel tuo caso, non dovresti avere problemi a utilizzare una singola porta hardcoded per il tuo listener, purché tu stampi un messaggio di errore se il bind fallisce.

Si noti inoltre che la maggior parte dei socket (per gli slave) non deve necessariamente essere esplicitamente associato a numeri di porta specifici: solo i socket che attendono connessioni in ingresso (come qui il proprio master) dovranno essere ascoltati e associati a una porta. Se una porta non è specificata per un socket prima che venga utilizzata, il sistema operativo assegnerà una porta utilizzabile al socket. Quando il master desidera rispondere a uno slave che gli invia i dati, l'indirizzo del mittente è accessibile quando l'ascoltatore riceve i dati.

Presumo che userete UDP per questo?


0

Se hai solo bisogno di trovare una porta libera per un uso successivo, ecco uno snippet simile a una risposta precedente , ma più breve, usando socketserver :

import socketserver

with socketserver.TCPServer(("localhost", 0), None) as s:
    free_port = s.server_address[1]

Non è garantito che la porta rimanga libera, pertanto potrebbe essere necessario inserire questo frammento e il codice utilizzandolo in un ciclo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.