Un socket rappresenta un punto di connessione di una rete TCP/IP; è un punto a cui collegarsi per comunicare con altre applicazioni sulla rete (è analogo a una presa elettrica con cui ci si collega alla rete elettrica).
In pratica un socket identifica un computer e un processo sul computer; è formato da un indirizzo IP che identifica un computer sulla rete e da un numero di porta che identifica un processo o un’applicazione sul computer.
A ogni processo o applicazione che usa TCP (o UDP) come protocollo di trasporto viene assegnato un numero identificativo unico chiamato porta. Le porte sono numerate da 0 a 65.535. Su uno stesso computer possono essere attive contemporaneamente più comunicazioni; ognuna è individuata da una porta diversa.
Il socket rappresenta solo uno dei capi di una comunicazione; due computer per comunicare usano ciascuno un socket; tra i due socket si crea una connessione per il trasferimento dei dati in due direzioni.
Dato che le connessioni sono identificate da entrambi i socket, un socket può essere usato da più di una connessione contemporaneamente, cioè due o più connessioni possono terminare nello stesso socket; per esempio due client possono collegarsi allo stesso server sul suo socket; i client potrebbero usare anche lo stesso numero di porta senza problemi perché comunque la coppia di socket che identifica la connessione è diversa.
In una applicazione client/server il server apre un socket e resta in attesa di connessioni (apertura passiva; la porta si dice in stato di ascolto); il client chiama il socket del server per iniziare la connessione (apertura attiva; permette di stabilire la connessione); per potersi connettere il client deve conoscere l’indirizzo e il numero di porta del server di destinazione.
Le applicazioni server più diffuse (come i server Web, FTP ecc.) hanno dei numeri di porta predefiniti compresi tra 0 e 1.023 assegnati dalla Internet Assigned Numbers Authority (IANA), noti come Well Known Port Number.
Per le applicazioni client i numeri di porta di solito sono assegnati dinamicamente dal sistema operativo alla richiesta di connessione al server; può essere utilizzato il primo numero libero a partire da 1.024.
Per creare un’applicazione con i socket basta utilizzare le primitive messe a disposizione dal TCP/IP per richiedere i servizi del protocollo TCP o del protocollo UDP. Il programmatore non si deve preoccupare di nessuna delle problematiche che riguardano il trasporto dei dati; deve solo scegliere quale protocollo di trasporto usare.
Le primitive per l’utilizzo dei socket possono variare leggermente in implementazioni diverse del TCP/IP. Le più note sono le primitive dello Unix di Berkeley.
Le primitive da usare per aprire una connessione in una applicazione server o in una applicazione client sono un po’ diverse.
In una applicazione server che usa TCP come protocollo di trasporto bisogna utilizzare le primitive:
-
Socket crea un punto di accesso specificando il tipo di servizio desiderato e quindi il protocollo da usare (TCP);
-
Bind associa un socket a una porta (i socket appena creati non hanno indirizzi, bisogna specificare una porta in modo che i client possano connettersi);
-
Listen alloca lo spazio per la coda delle chiamate in arrivo;
-
Accept blocca il server in attesa di una richiesta di connessione; quando arriva una richiesta il server crea un nuovo socket; in tal modo può creare un processo o un thread che gestisca la connessione del nuovo socket e tornare ad aspettare altre richieste di connessione sul socket originario.
In una applicazione client bisogna utilizzare le primitive:
-
Socket crea un punto di accesso (la primitiva Bind non serve perché il numero di porta utilizzato dal client non è importante);
-
Connect richiede una connessione al server specificando indirizzo e porta del server; il processo client rimane bloccato finché non viene stabilita la connessione.
Dopo aver stabilito la connessione entrambe le applicazioni client e server possono usare le primitive per lo scambio di dati (la connessione è full duplex):
-
Send permette di inviare informazioni sulla connessione;
-
Receive blocca l’applicazione in attesa di ricevere informazioni dalla connessione.
Al termine entrambe le applicazioni devono chiudere la connessione (nei socket la chiusura delle connessioni è simmetrica) con la primitiva:
-
Close chiude un lato della connessione; la connessione viene chiusa quando entrambe le applicazioni la richiamano.