WinSock: i socket in Windows

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

La prima funzione che deve essere chiamata da qualsiasi programma Windows che voglia utilizzare le API Windows Sockets e’ WSAStartup().
Se questa funzione non e’ chiamata la funzione socket fallisce e restituisce il codice di errore INVALID_SOCKET.
Questa richiede due parametri: una word contenente la versione più alta delle Winsock API supportata dalla nostra applicazione, codificata in modo tale che nel byte meno significativo sia contenuta la versione principale e nell' altro il numero di revisione (ad esempio 0101h per l'API 1.1 e 0002h per la 2.0). Viene allora utilizzata la macro MAKEWORD(int x,int y) la quale crea una word a partire da due interi.
La struttura di tipo WSAData, verrà riempita dalla funzione WSAStartup() con le caratteristiche della Winsock DLL che andremo ad utilizzare:

struct WSAData {
WORD wVersion;
WORD wHighVersion;
char szDescription[WSADESCRIPTION_LEN+1];
char szSystemStatus[WSASYSSTATUS_LEN+1];
unsigned short iMaxSockets;
unsigned short iMaxUdpDg;
char FAR * lpVendorInfo;
};    

La principale funzione di WSAStartup() è quella di inizializzare la Winsock DLL e per questo motivo deve essere la prima funzione Winsock ad essere chiamata da un'applicazione.
Se il valore di ritorno è non nullo, significa che si è verificato un errore fatale e che l'applicazione deve essere chiusa (in quanto questa risulterà impossibilitata a lavorare con le Winsock). In caso contrario (ritorno nullo), analizzando la struttura WSAData, otterremo interessanti infAormazioni circa la DLL utilizzata (ad esempio quale versione delle Winsock dovremo effettivamente usare).
La sintassi e’ la seguente:

int retvalue = WSAStartup(WORD rvers,WSADATA wsimpl);   

La funzione restituisce zero in caso di successo oppure in caso di errore:
- WSASYSNOTREADY: il sistema non e’ pronto per la comunicazione in rete
- WSAVERNOTSUPPORTED: versione richiesta delle WinSock non disponibile
- WSAEINVAL: la DLL non supporta la versione richiesta

Creare un Windows Socket. Per fare questo bisogna passare:
- La famiglia di indirizzi (AF_INET o PF_INET indistintamente per specificare la famiglia di protocolli TCP/IP).
- Il tipo di socket (stream indicando SOCK_STREAM oppure datagram indicando SOCK_DGRAM).
- Il tipo di protocollo (IPPROTO_TCP o IP_PROTOUDP o il valore nullo per non specificarne alcuno).

La sua sintassi e’ la seguente:

SOCKET retvalue = socket (int afam,int type,int protocol);    

Questa funzione ritorna direttamente l'handle della nuova socket, in caso di successo, oppure la costante INVALID_SOCKET in caso di errore. In quest'ultimo caso, il codice dell'errore può essere reperito utilizzando la funzione WSAGetLastError().

 In questo esempio mostreremo come sia possibile creare un server TCP, che aspetta un richiesta di connessione, riceve un carattere dal client e chiude la connessione. Il client viene eseguito mendiante il comando telnet, aprendo una console e al prompt dei comandi digitando:

C:\ telnet 127.0.0.1

Questo comando va lanciato dopo aver avviato il server TCP. A questo punto, viene visualizzato la finestra dell’applicazione Telnet ed e’ possibile digitare un carattere, fatto ciò viene visualizza nella finestra del server il carattere che e’ stato digitato nell’applicazione telnet e immediatamente dopo la connessione viene chiusa dal server.

int main(void){
	SOCKET sock21, asock;
	sockaddr_in sock_in21, asock_in;
	WORD wVersionRequested;
	int err, addrlen;
	char *send_buff="SERVER TELNET \n";
	char recv_buff[256];
	wVersionRequested=MAKEWORD(2,0);
	printf("MINISERVER TCP/IP su porta Telnet\n");
	err=WSAStartup(wVersionRequested,&wsaData);
	if(err!=0){
		printf("Libreria winsock non presente\n");
		exit(1);
	} 
	sock21=socket(PF_INET,SOCK_STREAM,0);
	if(sock21==INVALID_SOCKET){
		printf("Errore socket():%d\n",WSAGetLastError());
		exit(1);
	}
	sock_in21.sin_family=PF_INET;
	sock_in21.sin_port=htons(IPPORT_TELNET);
	sock_in21.sin_addr.s_addr=INADDR_ANY;
	err=bind(sock21,(struct sockaddr *) &sock_in21, sizeof(struct sockaddr_in));
	if(err==SOCKET_ERROR){
		printf("Errore bind():%d\n",WSAGetLastError());
		exit(1);
	}
	err=listen(sock21,1);
	if(err==SOCKET_ERROR){
		printf("Errore listen():%d\n",WSAGetLastError());
		exit(1);
	} else {
		printf("Aspettimo la connessione..\n");
	}
	addrlen=sizeof(struct sockaddr);
	asock=accept(sock21,(struct sockaddr *) &asock_in,(LPINT)&addrlen);
	if(asock==INVALID_SOCKET){
		printf("Errore accept():%d\n",WSAGetLastError());
		exit(1);
	} else {
		printf("Client: %s:%d\n",inet_ntoa(asock_in.sin_addr),ntohs(asock_in.sin_port));
	}
	send(asock,send_buff,strlen(send_buff),0);
	memset(recv_buff,0,sizeof(recv_buff));
	recv(asock,recv_buff,sizeof(recv_buff),0);
	printf("CLIENT>%s\n",recv_buff);
	closesocket(asock);
	closesocket(sock21);
	WSACleanup();
	return 0;
}    

Con questo ho concluso. Avete visto come la sintassi di programmazione delle Winsock sia identica a quella dei socket in Linux, a meno che' non si voglia utilizzare la modalità asincrona.