TCP: due socket diversi possono condividere una porta?


124

Questa potrebbe essere una domanda molto semplice ma mi confonde.

Due prese collegate differenti possono condividere una porta? Sto scrivendo un application server che dovrebbe essere in grado di gestire più di 100k connessioni simultanee e sappiamo che il numero di porte disponibili su un sistema è di circa 60k (16 bit). Un socket connesso viene assegnato a una nuova porta (dedicata), quindi significa che il numero di connessioni simultanee è limitato dal numero di porte, a meno che più socket non possano condividere la stessa porta. Quindi la domanda.

Grazie per l'aiuto in anticipo!

Risposte:


175

Un socket del server è in ascolto su una singola porta. Tutte le connessioni client stabilite su quel server sono associate alla stessa porta di ascolto sul lato server della connessione. Una connessione stabilita viene identificata in modo univoco dalla combinazione di coppie IP / Porta lato client e lato server. Più connessioni sullo stesso server possono condividere la stessa coppia IP / Porta lato server purché siano associate a coppie IP / Porta diverse lato client e il server sarebbe in grado di gestire tanti client quanti sono consentiti dalle risorse di sistema disponibili per.

Sul lato client , è pratica comune che le nuove connessioni in uscita utilizzino una porta lato client casuale , nel qual caso è possibile esaurire le porte disponibili se si effettuano molte connessioni in un breve lasso di tempo.


2
Grazie per la risposta, Remy! La tua risposta è tutto ciò di cui ero curioso. ;)
KJ

2
Le connessioni @Remy sono discriminate non solo dalla porta di origine / destinazione / IP, ma anche da un protocollo (TCP, UDP ecc.), Se non mi sbaglio.
Ondrej Peterka

1
@OndraPeterka: sì, ma non tutte le piattaforme lo limitano. Ad esempio, Windows consente felicemente a socket server IPv4 e IPv6 separati di ascoltare sullo stesso IP locale: porta senza salti mortali, ma i sistemi * Nix (inclusi Linux e Android) no.
Remy Lebeau

6
@ user2268997: non è possibile utilizzare un singolo socket per connettersi a più server. È necessario creare un socket separato per ogni connessione.
Remy Lebeau

3
@FernandoGonzalezSanchez: un singolo client può avere più socket TCP associati alla stessa coppia IP / Porta locale purché siano collegati a diverse coppie IP / Porta remote. Questo non è specifico di Windows, fa parte del modo in cui TCP funziona in generale.
Remy Lebeau

182

Ascolto TCP / HTTP sulle porte: quanti utenti possono condividere la stessa porta

Allora, cosa succede quando un server ascolta le connessioni in entrata su una porta TCP? Ad esempio, supponiamo che tu abbia un server web sulla porta 80. Supponiamo che il tuo computer abbia l'indirizzo IP pubblico di 24.14.181.229 e la persona che cerca di connettersi abbia l'indirizzo IP 10.1.2.3. Questa persona può connettersi aprendo un socket TCP su 24.14.181.229:80. Abbastanza semplice.

Intuitivamente (e erroneamente), la maggior parte delle persone presume che assomigli a questo:

    Local Computer    | Remote Computer
    --------------------------------
    <local_ip>:80     | <foreign_ip>:80

    ^^ not actually what happens, but this is the conceptual model a lot of people have in mind.

Questo è intuitivo, perché dal punto di vista del client, ha un indirizzo IP e si connette a un server su IP: PORT. Dato che il client si connette alla porta 80, anche la sua porta deve essere 80? Questa è una cosa sensata da pensare, ma in realtà non ciò che accade. Se ciò fosse corretto, potremmo servire solo un utente per indirizzo IP esterno. Una volta che un computer remoto si connette, allora trascina la porta 80 alla connessione alla porta 80 e nessun altro può connettersi.

Tre cose devono essere comprese:

1.) Su un server, un processo è in ascolto su una porta. Una volta ottenuta una connessione, la passa a un altro thread. La comunicazione non ostacola mai la porta di ascolto.

2.) Le connessioni sono identificate in modo univoco dal sistema operativo dalle seguenti 5 tuple: (IP locale, porta locale, IP remoto, porta remota, protocollo). Se qualsiasi elemento nella tupla è diverso, si tratta di una connessione completamente indipendente.

3.) Quando un client si connette a un server, seleziona una porta sorgente di ordine superiore casuale e inutilizzata . In questo modo, un singolo client può avere fino a ~ 64k connessioni al server per la stessa porta di destinazione.

Quindi, questo è davvero ciò che viene creato quando un client si connette a un server:

    Local Computer   | Remote Computer           | Role
    -----------------------------------------------------------
    0.0.0.0:80       | <none>                    | LISTENING
    127.0.0.1:80     | 10.1.2.3:<random_port>    | ESTABLISHED

Guardando cosa succede realmente

Per prima cosa, usiamo netstat per vedere cosa sta succedendo su questo computer. Useremo la porta 500 invece di 80 (perché un sacco di cose stanno accadendo sulla porta 80 poiché è una porta comune, ma funzionalmente non fa differenza).

    netstat -atnp | grep -i ":500 "

Come previsto, l'output è vuoto. Ora iniziamo un server web:

    sudo python3 -m http.server 500

Ora, ecco l'output di eseguire nuovamente netstat:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      - 

Quindi ora c'è un processo che è attivamente in ascolto (Stato: LISTEN) sulla porta 500. L'indirizzo locale è 0.0.0.0, che è il codice per "ascoltare tutti gli indirizzi IP". Un semplice errore da commettere è ascoltare solo sulla porta 127.0.0.1, che accetterà solo connessioni dal computer corrente. Quindi questa non è una connessione, significa solo che un processo ha richiesto di bind () alla porta IP, e quel processo è responsabile della gestione di tutte le connessioni a quella porta. Questo suggerisce la limitazione che può esserci un solo processo per computer in ascolto su una porta (ci sono modi per aggirare questo problema usando il multiplexing, ma questo è un argomento molto più complicato). Se un server web è in ascolto sulla porta 80, non può condividere quella porta con altri server web.

Quindi ora colleghiamo un utente alla nostra macchina:

    quicknet -m tcp -t localhost:500 -p Test payload.

Si tratta di un semplice script ( https://github.com/grokit/quickweb ) che apre un socket TCP, invia il payload ("Test payload." In questo caso), attende alcuni secondi e si disconnette. Facendo di nuovo netstat mentre questo accade, viene visualizzato quanto segue:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      -
    tcp        0      0 192.168.1.10:500        192.168.1.13:54240      ESTABLISHED -

Se ti connetti con un altro client e fai di nuovo netstat, vedrai quanto segue:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      -
    tcp        0      0 192.168.1.10:500        192.168.1.13:26813      ESTABLISHED -

... cioè, il client ha utilizzato un'altra porta casuale per la connessione. Quindi non c'è mai confusione tra gli indirizzi IP.


11
Questa è la migliore risposta che abbia mai visto su SO.
Lavori

1
@ N0thing "In questo modo, un singolo client può avere fino a ~ 64k connessioni al server per la stessa porta di destinazione." Quindi, in pratica, se un client non si connette allo stesso server e alla stessa porta, due o più volte contemporaneamente, un client può avere anche più di ~ 64.000 connessioni. È vero. Se sì, ciò implica che da una singola porta sul lato client può avere connessione a molti processi server diversi (in modo tale che la connessione socket sia diversa). Quindi, in tutto, più socket client possono risiedere sulla stessa porta sulla macchina client? Si prega di leggere il mio commento alla risposta "Remey Lebeau". Grazie: D
Prem KTiw

6
@premktiw: Sì, più socket client possono essere associati alla stessa coppia IP / porta locale allo stesso tempo, se sono collegati a coppie IP / Porta del server diverse, in modo che le tuple delle coppie locale + remota siano univoche. E sì, è possibile che un client abbia più di 64 KB di connessioni simultanee in totale. Da una singola porta, può essere collegato a un numero potenzialmente infinito di server (limitato dalle risorse del sistema operativo disponibili, dalle porte del router disponibili, ecc.) Purché le coppie IP / porta del server siano uniche.
Remy Lebeau

1
@RemyLebeau soddisfatto. Grazie mille: D
Prem KTiw

1
@bibstha In che modo il firewall gestisce le porte casuali, quando tutte le connessioni in entrata vengono rifiutate?
PatrykG

35

Una presa collegata viene assegnata a una nuova porta (dedicata)

È un'intuizione comune, ma non è corretta. Una presa collegata non è assegnata a una porta nuova / dedicata. L'unico vincolo effettivo che lo stack TCP deve soddisfare è che la tupla di (local_address, local_port, remote_address, remote_port) deve essere univoca per ogni connessione socket. Pertanto il server può avere molti socket TCP che utilizzano la stessa porta locale, a condizione che ciascuno dei socket sulla porta sia connesso a una posizione remota diversa.

Consulta il paragrafo "Socket Pair" all'indirizzo: http://books.google.com/books?id=ptSC4LpwGA0C&lpg=PA52&dq=socket%20pair%20tuple&pg=PA52#v=onepage&q=socket%20pair%20tuple&f=false


1
Grazie per la risposta perfetta, Jeremy!
KJ

6
Quello che dici è del tutto vero per il lato server. Tuttavia, la struttura dell'API BSD Sockets significa che le porte lato client in uscita devono essere univoche in pratica, poiché l' bind()operazione precede l' connect()operazione, anche implicitamente.
Marchese di Lorne

1
@EJP Salve, pensavo bind()fosse utilizzato solo sul lato server prima accept()?Quindi il lato client collegherà anche la porta in particolare?
GMsoF

5
@GMsoF: bind()può essere utilizzato prima sul lato client connect().
Remy Lebeau

10

Teoricamente, sì. Pratica, no. La maggior parte dei kernel (incluso linux) non ti consente un secondo bind()per una porta già allocata. Non è stata una grande patch per consentire questo.

Concettualmente, dovremmo distinguere tra socket e porta . I socket sono endpoint di comunicazione bidirezionale, cioè "cose" in cui possiamo inviare e ricevere byte. È una cosa concettuale, non esiste un campo simile nell'intestazione di un pacchetto chiamato "socket".

La porta è un identificatore in grado di identificare un socket. Nel caso del TCP, una porta è un numero intero a 16 bit, ma ci sono anche altri protocolli (ad esempio, su socket unix, una "porta" è essenzialmente una stringa).

Il problema principale è il seguente: se arriva un pacchetto in arrivo, il kernel può identificare il proprio socket tramite il numero di porta di destinazione. È un modo più comune, ma non è l'unica possibilità:

  • I socket possono essere identificati dall'IP di destinazione dei pacchetti in arrivo. Questo è il caso, ad esempio, se abbiamo un server che utilizza due IP contemporaneamente. Quindi possiamo eseguire, ad esempio, diversi server web sulle stesse porte, ma su diversi IP.
  • I socket possono essere identificati anche dalla porta sorgente e dall'ip. Questo è il caso di molte configurazioni di bilanciamento del carico.

Poiché stai lavorando su un server delle applicazioni, sarà in grado di farlo.


2
Non ha chiesto di fare un secondo bind().
Marchese di Lorne

1
@ user207421 Hai mai visto un sistema operativo in cui i socket di ascolto non sono configurati bind()? Posso immaginarlo, sì, è del tutto possibile, ma il fatto è che sia WinSock che l'API Posix utilizzano la bind()chiamata per questo, anche la loro parametrizzazione è praticamente la stessa. Anche se un'API non ha questa chiamata, in qualche modo devi dirla, da dove vuoi leggere i byte in arrivo .
peterh - Ripristina Monica l'

1
@ user207421 Ovviamente 100k o più connessioni TCP possono essere gestite con le stesse porte, le chiamate listen()/ accept()API possono creare i socket in modo che il kernel li differenzierà dalle loro porte in entrata. La questione del PO può essere interpretata nel modo in cui lo chiede essenzialmente. Penso che sia abbastanza realistico, ma non è questo che significa letteralmente la sua domanda.
peterh - Ripristina Monica l'

1

No. Non è possibile condividere la stessa porta in un particolare istante. Ma puoi rendere la tua applicazione in modo tale che la porta acceda in un istante diverso.

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.