WinSock: i socket in Windows

Cosa sono e come funzionanto i WinSock di Windows
Data: 02/01/2003 Autore: Iasparra Francesco 

Attualmente la suite di protocolli di comunicazione piu’ diffuso e’ il TCP/IP (Transmission Control Protocol/Internet Protocol) usato per la comunicazione sulla rete mondiale (Internet Protocol Suite).
Una qualsiasi applicazione sviluppata su piattaforma Windows o Unix che voglia utilizzare la suite TCP/IP per la comunicazione su rete, deve utilizzare le socket.
Gli sviluppatori di applicazioni Internet, basate sulla famiglia di protocolli TCP/IP, utilizzano le socket come una interfaccia standard di programmazione per sviluppare software capace di gestire una qualsiasi implementazione di TCP/IP.
La prima implementazione dell’interfaccia Socket nasce, in origine, all’interno del sistema operativo UNIX, attualmente viene implementata su molteplici sistemi operativi, tra cui Windows.
Le socket in Windows chiamate "WinSock" sono un "open transport-level network API standard", progettato per il sistema operativo Windows dal WinSock Group fondato nel 1991 e formato da produttori e sviluppatori di applicazioni per le reti, e di software per sistemi operativi.
WinSock Group si riunisce molte volte all’anno con lo scopo di raffinare ed estendere le specifiche dell’interfaccia sockets, e per testare nuovi stack di protocolli forniti dai vari produttori.
Per le versioni di Windows 95/NT sono state utilizzate Windows Sockets versione 1.1, pubblicate nel gennaio del 1993, derivate da Berkeley Software Distribuition (BSD) 4.3 per TCP/IP.

Le specifiche di Windows Sockets include le routine dei socket in stile Berkeley. E’ previsto un ulteriore supporto mediante estensioni ai socket di Berkeley sotto forma di funzioni che iniziano con il prefisso WSA (WinSock API per l’appunto).
Alcune di queste funzioni sono ad esempio WSAGetLastError(), WSAsyncSelect(), WSAStartup(). Nei successivi paragrafi vengono discusse le principali funzioni da utilizzare nello sviluppo di applicazioni client/server che utilizzano il protocollo TCP/IP.

La seguente tabella mostra le dipendenze tra la versione delle Winsock che si vuole utilizzare, il file header per sviluppo dell’applicazione in linguaggio C e le DLL a cui agganciarsi.

WinSock Version Platform Header file Dynamic Link Library
16-bit WinSock 1.1 16/32-bit Windows Winsock.h Winsock.dll
32-bit WinSock 1.1 32-bit Windows Winsock.h Wsock32.dll
16-bit WinSock 2.0 32-bit Windows Winsock2.h Ws2_32.dll

L'API delle Windows Sockets è detta Open nel senso che è stata creata, come altri sistemi aperti, nello spirito di collaborazione tra diversi produttori di software di rete ed istituzioni universitarie, ed è disponibile gratuitamente, così come lo sono i file di intestazione sviluppati in linguaggio C (Winsock.h) e le relative librerie (Winsock.dll, Wsock32.dll ed altre) di cui la tabella sopra ne specifica le dipendenze.
Quindi in pratica la specifica Windows Sockets API (WSA) definisce una collezione di chiamate di funzioni, procedure, strutture di dati, e le relative semantiche, che permettono, a qualsiasi applicazione Windows, un accesso standardizzato ai servizi di rete offerti da una suite di protocolli sottostante.

Un socket in Windows e’ individuato mediante il tipo SOCKET, che contrariamente a cio’ che avviene in Unix esso non è di tipo intero non negativo (come file descriptor), ma qualunque valore e’ un socket valido a meno del valore NVALID_SOCKET che verra’ utilizzato per controllare eventuali errori.

Stile BSD :

s = socket(...);
if (s == -1) 
{...}   

Stile WINDOWS:

s = socket(...);
if (s == INVALID_SOCKET) {...}

La realizzazione della funzione select() e’ stata modificata in Windows per gestire i WinSock, in cui la struttura fd_set non contiene piu’ una bitmask ma un array di SOCKET:

typedef struct fd_set {
u_int fd_count;
SOCKET fd_array[FD_SETSIZE];
} fd_set;    

Una socket al momento della sua creazione non contiene informazioni dettagliate circa il modo in cui viene usata tranne le informazioni sulla famiglia di protocolli su cui si lavora ed il tipo di servizio, oltre chiaramente l’handle che la identifica. In particolare la socket non contiene i numeri di porta di protocollo o gli indirizzi IP, ne della macchina locale ne di quella remota, senza i quali, però, essa non può essere utilizzata da un'applicazione.
I protocolli TCP/IP definiscono un terminale di comunicazione endpoint consistente di un indirizzo IP e di un numero di porta. Altre famiglie di protocolli definiscono i propri indirizzi terminali in altri modi. Siccome le socket supportano più famiglie di protocolli, esse non definiscono il modo in cui specificare un indirizzo, né definiscono un particolare formato di protocollo di indirizzo, lasciano invece ad ogni famiglia di protocolli il compito di specificare gli indirizzi nella maniera voluta. E per permettere ciò le socket definiscono una famiglia di indirizzo per ogni tipo di indirizzo usato.
Una famiglia di protocolli può avere una o più famiglie di indirizzo. I protocolli TCP/IP usano tutti la stessa rappresentazione di indirizzo, denotando la propria famiglia di indirizzo con la costante simbolica AF_INET (definita in winsock.h )
Per mantenere inoltre la generalità dei servizi offerti rispetto alle diverse famiglie di protocolli, il sistema delle socket definisce un formato generalizzato che tutti i tipi di indirizzi terminali usano. Tale formato consiste della coppia : famiglia d'indirizzo, indirizzo, terminale nella famiglia.
In pratica nell'implementazione software si hanno delle dichiarazioni di strutture predefinite in C per gli indirizzi terminali. La struttura più generale è nota come struttura sockaddr e contiene un campo identificatore della famiglia di indirizzi formato da due byte e da un array a quattordici byte che contiene l'indirizzo:

struct sockaddr { /* struttura che contiene un indirizzo */
u_short sa_family; /* tipo o famiglia di indirizzo */
char sa_data[14]; /* valore dell'indirizzo */
};    

Ogni famiglie di indirizzo ha una propria lunghezza per indicarne il valore, ad esempio un' indirizzo TCP/IP è composto da otto byte mentre quello dello stack di protocolli IPX/SPX è lungo quattordici byte (nel BSD UNIX può essere ancora più lungo), per tali motivi quindi la struttura appena vista ha un valore di puro riferimento. Quindi per ogni famiglia che ha definito un suo formato di indirizzo ben preciso il software delle socket fornisce la relativa dichiarazione della corrispondente struttura.
Per la famiglia TCP/IP un indirizzo consiste di un campo di due byte che identifica la famiglia AF_INET (si noti che tale campo è comune a tutte le strutture di indirizzo), un campo a due byte che contiene il numero di porta, uno a quattro byte contenente l'indirizzo IP ed un ultimo campo ad otto byte inutilizzato e presente solo per conservare i limiti di ampiezza con la struttura. La struttura così risultante è:

struct sockaddr { /* struttura che contiene un indirizzo */
u_short sa_family; /* tipo o famiglia di indirizzo */
char sa_data[14]; /* valore dell'indirizzo */
}; 

Nell' ambito della programmazione con le Winsock, risulta fondamentale assegnare i ruoli di client e server al fine di semplificare la stesura del codice e non creare situazioni di ambiguità. Il server è quello che si preoccupa di stare in ascolto, in attesa di eventuali richieste di connessione entranti, mentre il client è quello che richiede la connessione e che di solito è il primo a trasmettere dati. Inizialmente sia il client che il server creano una propria socket indipendente. Queste vengono associate successivamente durante la fase di connessione.
Affinché due sockets possano comunicare come client e server, bisogna che queste siano dello stesso tipo, cioè devono essere entrambe di tipo stream (TCP) o datagram (UDP).
Operazioni fondamentali per una connessione TCP

SERVER CLIENT NOTE
WSAStartup WSAStartup Inizializzazione delle risorse
Socket Socket Creazione della socket
Bind   Assegna posta e indirizzo alla socket
Listen   Ascolta le richieste di connessione
  Connect Si connette all’ endpoint remoto
Accept   Accetta la richiesta di connessione
Recv Send Scambio dati
Send Recv Scambio dati
CloseSokect CloseSokect Chiusura dei socket
WSACleanup WSACleanup Rilascio delle risorse

Operazioni fondamentali per una connessione UDP

SERVER CLIENT NOTE
WSAStartup WSAStartup Inizializzazione delle risorse
Socket Socket Creazione della socket
Bind Bind Assegna posta e indirizzo alla socket
Recvform Sendto Sendto Scambio dei dati
Sendto Recvform Scambio dei dati
CloseSokect CloseSokect Chiusura dei socket
WSACleanup WSACleanup Rilascio delle risorse

Sono stati effettuati molti aggiornamenti a questo schema di base di funzionamento; sono stati aggiunti API, protocolli, tipi di trasmissione; Winsock 2 e' un esempio di standard utilizzabile per un vasto numero di protocolli e tipi di connessione.