Come funziona la funzione accept () dell’API socket?

L’API socket è lo standard de facto per le comunicazioni TCP / IP e UDP / IP (ovvero, codice di rete come lo conosciamo). Tuttavia, una delle sue funzioni principali, accept() è un po ‘magica.

Per prendere in prestito una definizione semi-formale:

accept () è usato sul lato server. Accetta un tentativo in entrata ricevuto di creare una nuova connessione TCP dal client remoto e crea un nuovo socket associato alla coppia di indirizzi socket di questa connessione.

In altre parole, accept restituisce un nuovo socket attraverso il quale il server può comunicare con il client appena connesso. Il vecchio socket (su cui è stato chiamato accept ) rimane aperto, sulla stessa porta, in attesa di nuove connessioni.

Come accept lavoro? Come viene implementato? C’è molta confusione su questo argomento. Molte persone affermano che accettare apre una nuova porta e comunichi con il cliente attraverso di essa. Ma ovviamente non è vero, poiché non viene aperto nessun nuovo porto. In realtà è ansible comunicare attraverso la stessa porta con diversi client, ma come? Quando diversi thread chiamano recv sulla stessa porta, come fanno i dati a sapere dove andare?

Immagino che sia qualcosa sulla falsariga che l’indirizzo del client sia associato a un descrittore di socket, e ogni volta che i dati arrivano attraverso recv indirizzati al socket corretto, ma non ne sono sicuro.

Sarebbe bello avere una spiegazione approfondita del funzionamento interno di questo meccanismo.

La tua confusione sta nel pensare che un socket è identificato da IP server: porta del server. Quando in realtà, le prese sono identificate in modo univoco da un quartetto di informazioni:

Client IP : Client Port e Server IP : Server Port

Quindi, mentre l’IP del server e la porta del server sono costanti in tutte le connessioni accettate, le informazioni sul lato client sono ciò che consente di tenere traccia di dove tutto sta andando.

Esempio per chiarire le cose:

Supponiamo di avere un server a 192.168.1.1:80 e due client, 10.0.0.1 e 10.0.0.2.

10.0.0.1 apre una connessione sulla porta locale 1234 e si connette al server. Ora il server ha un socket identificato come segue:

10.0.0.1:1234 – 192.168.1.1:80

Ora 10.0.0.2 apre una connessione sulla porta locale 5678 e si connette al server. Ora il server ha due socket identificati come segue:

10.0.0.1:1234 – 192.168.1.1:80
10.0.0.2:5678 – 192.168.1.1:80

Giusto per aggiungere alla risposta data dall’utente “17 di 26”

Il socket è in realtà costituito da 5 tuple: (IP di origine, porta di origine, IP di destinazione, porta di destinazione, protocollo). Qui il protocollo poteva TCP o UDP o qualsiasi protocollo di livello di trasporto. Questo protocollo è identificato nel pacchetto dal campo “protocollo” nel datagramma IP.

Pertanto è ansible avere diverse applicazioni sul server che comunicano con lo stesso client su esattamente le stesse 4-tuple ma differenti nel campo del protocollo. Per esempio

Apache sul lato server che parla (server1.com:880-client1:1234 su TCP) e World of Warcraft che parla (server1.com:880-client1:1234 su UDP)

Sia il client che il server gestiranno questo come campo del protocollo nel pacchetto IP in entrambi i casi è diverso anche se tutti gli altri 4 campi sono uguali.

Ciò che mi ha confuso quando stavo imparando questo, era che i termini socket e port suggeriscono che sono qualcosa di fisico, quando in realtà sono solo strutture dati che il kernel usa per astrarre i dettagli del networking.

Come tale, le strutture dati sono implementate per essere in grado di tenere separate le connessioni da diversi client. Per quanto riguarda il modo in cui sono implementati, la risposta è a.) Non importa, lo scopo dell’API socket è precisamente che l’implementazione non dovrebbe importare o b.) Basta dare un’occhiata. Oltre ai libri di Stevens altamente raccomandati che forniscono una descrizione dettagliata di un’implementazione, controlla la fonte in Linux o Solaris o in uno dei BSD.

Come ha detto l’altro utente, un socket è identificato in modo univoco da una tupla di 4 elementi (Client IP, Client Port, Server IP, Server Port).

Il processo del server in esecuzione sull’IP del server gestisce un database (ovvero non mi interessa quale tipo di tabella / lista / struttura / matrice / struttura dei dati magica che utilizza) dei socket attivi ed è in ascolto sulla porta del server. Quando riceve un messaggio (tramite lo stack TCP / IP del server), controlla l’IP e la porta del client rispetto al database. Se l’IP del client e la porta del client si trovano in una voce del database, il messaggio viene trasferito a un gestore esistente, altrimenti viene creata una nuova voce del database e viene generato un nuovo gestore per gestire quel socket.

Nei primi giorni di ARPAnet, alcuni protocolli (FTP per uno) ascoltavano una porta specifica per le richieste di connessione e rispondevano con una porta handoff. Ulteriori comunicazioni per quella connessione andrebbero oltre la porta di handoff. Ciò è stato fatto per migliorare le prestazioni per pacchetto: a quei tempi i computer erano di diversi ordini di grandezza più lenti.