Windows Socket Programming Corso di Sistemi di Elaborazione delle Informazioni a.a 2007/2008 Autori: Alberto Colombo Fulvio Frati 1 Sommario Richiami di TCP Classificazione dei socket Socket client-server UDP Socket client-server TCP Esempio server multiporta gethostbyname & gethostbyaddr Java Socket 2 Richiami di TCP: porte I protocolli TCP e UDP usano le porte per mappare i dati in ingresso con un particolare processo attivo su un computer. Ogni socket è legato a un numero di porta così che il livello TCP può identificare l’applicazione a cui i dati devono essere inviati. App App App App App Porta Porta Porta Porta Porta UDP / TCP #porta Dati 3 Socket: definizione A livello programmatico, un Socket è definito come un “identificativo univoco che rappresenta un canale di comunicazione attraverso cui l’informazione è trasmessa” [RFC 147] La comunicazione basata su socket è indipendente dal linguaggio di programmazione. Client e server devono concordare solo su protocollo (TCP o UDP) e numero di porta 4 Richiami di TCP: well-known ports Le porte sono rappresentate da valori interi positivi (16 bit). Rappresentano punto di collegamento fra strato fisico e applicazioni; rappresentano un canale di comunicazione Alcune porte sono state riservate per il supporto di servizi wellknown: ftp 21/tcp; telnet 23/tcp; smtp 25/tcp; login 513/tcp. I servizi e i processi a livello utente generalmente usano un numero di porta >=1024. 5 Socket: Comunicazione server client Il server riceve una richiesta da un client server client Crea un nuovo socket che dialogherà col client e torna in ascolto di altre richieste richiesta comm richiesta client’ p fork p server new socket comm client 6 Socket: Classificazione (1) Datagram Socket: Utilizzano un protocollo senza connessione UDP Non è necessaria una procedura iniziale di connessione e riconoscimento fra client e server Non dà garanzie di ricezione e ordine dei pacchetti Stream Socket: Utilizzano un protocollo con connessione TCP La comunicazione avviene solo fra nodi tra cui è stato stabilito un canale di comunicazione Garantisce ordine e ricezione dei pacchetti Raw socket: Dà completo accesso allo strato fisico del sistema per poter sfruttare funzionalità non implementate dalle interfaccie Solo per utenti esperti 7 Socket: Classificazione (2) Sincroni: il client ed il server si sincronizzano ad ogni messaggio: sia send() (per inviare) che receive() (per ricevere) sono bloccanti send() ritorna dopo che è stata fatta l’issue della receive receive() ritorna solo dopo che il messaggio arriva in coda Asincrona: send() non è bloccante, significa che l’operazione ritorna subito dopo che il messaggio è stato copiato su un buffer locale receive() puo’ essere sia bloccante che non. Non bloccante significa che ritorna subito il controllo all’applicazione, poi tramite polling o interrupt notificherà l’accodamento del messaggio nella coda 8 Differenze ambiente Unix e Windows Include files, tutte le librerie raccolte in una: #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <winsock.h> WSAGetLastError() al posto della variabile errno closeSocket() al posto di close() Non supporta Raw Socket Fork implicito 9 UDP/TCP client: Header 10 UDP/TCP Client: Inizializzazione librerie WSAStartup(version, WSAData): • WORD version, versione delle librerie da caricare • LPWSADATA WSAData, struttura per ricevere i dettagli della Windows Socket Implementation • Necessaria solo in ambienti Windows 11 UDP/TCP Client: Creazione del socket socket(af, type, protocol): • int af, famiglia di indirizzi da utilizzare (AF_INET, PF_INET, AF_UNIX); • int type, tipo di socket (SOCK_STREAM, SOCK_DGRAM); • int protocol, tipo di protocolo utilizzato (IPPROTO_UDP, IPPROTO_TCP). • Restituisce un valore negativo se non a buon fine 12 UDP Client: sendTo() sendTo(socket, echoString, bufferLength, flag, to, toLength): • int socket, descrittore del socket; • char* echoString, messaggio da spedire; • int bufferLength, dimensione in bytes; • int flags, indica la modalità con cui viene effettuata la chiamata (default 0); • sockaddr* to, indirizzo destinatario; • int toLength, dimensione indirizzo in byte. • Non è necessario una procedura di connessione fra client e server 13 UDP Client: recvfrom() recvfrom(socket, buffer, bufferLength, flags, from, fromLength): • SOCKET socket, descrittore del socket; • char* buffer, messaggio spedito; • int bufferLength, dimensione in bytes; • int flags, indica la modalità con cui viene effettuata la chiamata (default 0). • sockaddr* from, indirizzo mittente; • int fromLength, dimensione indirizzo in byte. 14 UDP Client: Chiusura socket closesocket(socket): chiude il socket specificato e ritorna 0 se la chiusura è andata a buon fine, altrimenti un codice d’errore, recuperabile con WSAGetLastError(). WSACleanup(): rilascia la libreria. 15 UDP Server: Port Binding bind(socket, address, addressLength): • SOCKET socket,descrittore del socket; • sockaddr* address, indirizzo del socket; • int addressLength, dimensione dell’indirizzo in byte. • Lega ogni comunicazione su quel protocollo e su quella porta all’applicazione 16 UDP Server: Ricezione messaggi • Il server rimane in attesa della ricezione di un messaggio sulla recvfrom() • Nella struct echoclntAddr sono salvate tutte le informazioni relative al chiamante (in sin_addr l’indirizzo IP) 17 UDP Socket Server: INADDR_ANY Client: servIP Client Server 18 TCP Client: Creazione del socket • Crea un socket basato su TCP di tipo STREAM 19 TCP Client: Connessione • Stabilisce una connessione verso uno specifico server connect(socket, address, addressLength): • SOCKET socket, descrittore del socket; • sockaddr* address, indirizzo del server; • int addressLength, dimensione dell’indirizzo in byte. 20 TCP Client: Invio dati • Invia i dati specificati verso un socket già connesso • Non è necessario indicare ogni volta l’indirizzo del mittente send(socket, buffer, bufferLength, flags): • SOCKET socket, descrittore del socket; • char* buffer, messaggio da inviare; • int bufferLength, dimensione del messaggio in byte; • int flag, indica la modalità con cui viene effettuata la chiamata (default 0).21 TCP Client: Ricezione dati • Riceve dati da un socket connesso e restituisce il numero di byte ricevuti • Quando restituisce 0 la comunicazione è interrotta recv(socket, buffer, bufferLength, flag): • SOCKET socket, descrittore del socket; • char* buffer, messaggio da inviare; • int bufferLength, dimensione del messaggio in byte; • int flag, indica la modalità con cui viene effettuata la chiamata (default 0). 22 TCP Server: Connessione • Dopo il bind, il server rimane in ascolto (listen) sul socket; listen(socket, backlog): • SOCKET socket, descrittore del socket; • int backlog, dimensione massima della coda delle connessioni in attesa. 23 TCP Server: Accettazione • Quando un client richiede la connessione, viene un creato un nuovo socket per gestire la comunicazione • Il socket iniziale rimane in attesa di nuove richieste • accept(socket, clientAddress, clientAddressLength): • SOCKET socket, descrittore del socket; • sockaddr* clientAddress, indirizzo del client che ha richiesto l’accesso; • int clientAddressLength, dimensione di clientAddress in byte. 24 TCP Server: Select (1) • Permette di gestire simultaneamente più socket che gesticono porte diverse • Primo passo: creazione di un array di socket ognuno su una porta differente; nell’esempio la lista delle porte è passata a riga di comando • Per ogni porta è creato un socket server distinto 25 TCP Server: Select (2) • La select() ad intervalli specificati, controlla quali socket hanno fatto richiesta in lettura, scrittura o hanno riscontrato errori di comunicazione e restituisce il numero di socket interessati • I socket richiedenti sono marcati ed inseriti in specifici array per le successive operazioni select(0,readfds, writefds, errorfds, timeout): • fd_set* readfs, puntatore all’array di socket in lettura; • fd_set* writefs, puntatore all’array di socket in scrittura; • fd_set* errorfs, puntatore all’array di socket controllati per errori; • timeval* timeout, tempo di attesa per la select 26 TCP Server: Select (3) • Per capire quali socket hanno richiesto l’accesso, si scorre l’array dei socket e si applica FD_ISSET() • FD_ISSET restituisce un valore non-zero se il socket è stato marcato FD_ISSET(socket, *set): • SOCKET socket, il descrittore del socket da controllare; • fd_set *set, puntatore all’insieme dei socket marcati. 27 TCP Server: Select (4) • Prima di ogni chiamata a select() è necessario inizializzare l’insieme dei socket marcabili. Occorre invocare: 1. FD_ZERO() svuota l’insieme dei socket marcati 2. FD_SET(), prepara la lista dei socket attivi, aggiungendo all’insieme il socket che potrà poi essere marcato dalla select() 28 Esempio Socket paralleli: fork (1) Duplica il processo Restituisce: Al processo padre: pid (Process Id) processo figlio Al processo figlio: pid = 0 In caso di errore: -1 I descrittori aperti dal padre saranno condivisi anche dal figlio Un descrittore chiuso in un processo rimane aperto nell’altro processo Crea repliche del processo server per gestire comunicazioni parallele L’esecuzione ricomincia nel processo figlio dall’istruzione sucessiva alla fork Chiamata di sistema tipica di Linux, non utilizzabile sotto Windows 29 Esempio Socket paralleli: fork (2) Sono nel processo padre, chiudo socket connesso e ritorno all’accept Sono nel processo figlio, chiudo socket principale in ascolto e gestisco la comunicazione con il socket connesso 30 gethostbyname() & gethostbyaddr() struct hostent *gethostbyname(const char *name); struct hostent *gethostbyaddr(const void *addr, int len, int type); Restituiscono un puntatore ad un oggetto di tipo hostent che descrive un indirizzo internet a partire da un nome o da un indirizzo Parametri: name: nome dell’host da ricercare, es. “www.dti.crema.unimi.it” addr, puntatore alla struttura in_addr che contiene l’indirizzo len, lunghezza in byte della variabile addr type, famiglia di indirizzi, es. AF_INET 31 Riassumendo UDP TCP 32 Java socket Server: Creare un oggetto ServerSocket, specificando il numero di porta a cui legarlo Chiamare accept() per restare in attesa di richieste di connessione Quando accept() termina la connessione col client è stabilita accept() restituisce un socket per comunicare col client Client: Creare un oggetto di tipo Socket, specificando indirizzo e porta Quando l’oggetto è costruito la connessione col server è stabilita La comunicazione avviene direttamente attraverso i canali messi a disposizione dall’oggetto Socket: getInputStream(), getOutputStream(), ottengono rispettivamente un canale di input e di output 33 Java socket: Serializzazione Il meccanismo della serializzazione permette di scrivere e ricevere direttamente oggetti (istanze di classi) sui flussi in output e input Gli oggetti scambiati devono implemetare l’interfaccia Serializable La maggior parte delle classi standard implementa Serializable 34 Java Socket: Server (1) //SimpleServer.java: un semplice programma server import java.net.*; import java.io.*; public class SimpleServer { public static void main(String args[]) throws IOException { // Registra il servizio sulla porta 1234 ServerSocket s = new ServerSocket(1234); // Aspetta e accetta una connessione Socket s1=s.accept(); 35 Java Socket: Server (2) // Ottiene un flusso di comunicazione associato al socket OutputStream s1out = s1.getOutputStream(); DataOutputStream dos = new DataOutputStream(s1out); // Invia una stringa! dos.writeUTF(“Hello world"); // Chiude la connessione, ma non il socket server dos.close(); s1out.close(); s1.close(); } } 36 Java Socket: Client (1) // SimpleClient.java: un semplice programma client import java.net.*; import java.io.*; public class SimpleClient { public static void main(String args[]) throws IOException { // Apre la connessione a un server, alla porta 1234 Socket s1 = new Socket(“myserver.dti.unimi.it”,1234); 37 Java Socket: Client (2) // Ottiene un file handle dal socket e legge l’input InputStream s1In = s1.getInputStream(); DataInputStream dis = new DataInputStream(s1In); String st = new String (dis.readUTF()); System.out.println(st); // Alla fine, chiude la connessione e esce dis.close(); s1In.close(); s1.close(); } } 38 Esempio socket paralleli: thread Server Client 39 Annotazioni finali Le slide saranno pubblicate su http://ra.crema.unimi.it References: MSDN – Getting started with Winsock 40