Le Socket
Inter-process communication: socket Abbiamo visti alcune tipologie di Inter-process communication: Anonymous pipes FIFOs o named pipes Le socket di comunicazione si pongono nell'ipc per realizzare: Comunicazioni tra processi sulla stessa macchina Comunicazioni tra processi sparsi nella rete Caratteristiche Si basano su file speciali Garantiscono connessioni affidabili e non Più complesse da realizzare, es. open() sfrutta più system call 2
Socket Originariamente sviluppate da BSD ma adottate in tutte le famiglie Unix. Lo scopo creare IPC trasparenti capaci di sfruttare diversi protocolli. Paradigma client-server Il server crea un socket il cui nome è noto. Il client comunica con il server tramite una connessione al suo socket. Se la connessione ha successo si hanno due descrittori Uno per il client per inviare/ricevere le informazioni Uno per il server per ricevere/inviare le informazioni Ad ogni connessione, il server può reagire: In modo sequenziale (è lui che comunica direttamente) In maniera concorrente (crea un figlio che la gestisca) 3
Socket La creazione di una socket non è un operazione atomica. Server socket() /* istanzia la socket */ bind() /* setta il nome (sarà noto) della socket */ listen() /* ascolta la coda delle connessioni */ accept() /* gestisce la prossima connessione */ Client socket() /* istanzia la socket */ connect() /* connette ad un server per mezzo del nome */ Istanziare una socket il kernel alloca il file speciale per lo scambio dei dati. Si definisce il tipo di connessione: affidabile o non affidabile 4
Socket: schema generale 5
Socket: come collegare due processi - Server Passo 1: creare un file ricevitore (socket) ottenendo il file descriptor Il sistema crea la socket a cui è possibile riferirsi con il descrittore sdf 6
Socket: come collegare due processi - Server Passo 1: creare un file ricevitore (socket) ottenendo il file descriptor Passo 2: associare al socket un indirizzo di tipo: AF_UNIX, AF_INET mysocket rappresenta il nome che dovrà essere noto per connettersi Dipende dal dominio di connessione (ce ne sono altri): AF_UNIX: per connessioni tra processi sulla stessa macchina AF_INET: per connessioni tra processi sparsi in rete 7
Socket: come collegare due processi - Server Passo 1: creare un file ricevitore (socket) ottenendo il file descriptor Passo 2: associare al socket un indirizzo di tipo: AF_UNIX, AF_INET Passo 3: specificare che siamo disposti ad accettare connessioni 8
Socket: come collegare due processi - Server Passo 1: creare un file ricevitore (socket) ottenendo il file descriptor Passo 2: associare al socket un indirizzo di tipo: AF_UNIX, AF_INET Passo 3: specificare che siamo disposti ad accettare connessioni Passo 4. bloccarsi in attesa di connessioni da parte di altri processi Da questo momento il server si mette in ascolto delle connessioni in entrata (il processo viene sospeso) 9
Socket: come collegare due processi - Client Passo 1: creare un file ricevitore (socket) ottenendo il file descriptor 10
Socket: come collegare due processi - Client Passo 1: creare un file ricevitore (socket) ottenendo il file descriptor Passo 2: collegarsi al socket del server Il nome deve essere quello utilizzato nella chiamata bind() del server 11
Socket: come collegare due processi - Client Passo 1: creare un file ricevitore (socket) ottenendo il file descriptor Passo 2: collegarsi al socket del server Il nome deve essere quello utilizzato nella chiamata bind() del server Avviene il match fra accept/connect ed un nuovo socket viene creato 12
Socket: come collegare due processi - Server accept ritorna al server il descrittore del nuovo socket usabile per il resto della comunicazione con il client 13
Socket: come collegare due processi - Server accept ritorna al server il descrittore del nuovo socket usabile per il resto della comunicazione con il client il server si può rimettere in attesa di connessioni da altri client con un altra accept 14
Socket - creazione Dominio - indica dove risiedono il server ed il client. Alcuni: AF_UNIX: client e server sono sulla stessa macchina AF_INET: client e server sono ovunque in Internet Tipo - determina il tipo di comunicazione server/client. Due principali: SOCK_STREAM: affidabile, preserva l'ordine di invio dei dati, basata su stream di byte di lunghezza variabile SOCK_DGRAM: inaffidabile, senza una connessione fissa, basata su messaggi di lunghezza variabile Protocollo - specifica le regole di scambio messaggi. Caratteristiche: protocolli datagram, come UDP, che non sono affidabili protocolli stream, come TCP/IP, che assicurano la ricezione dei dati preservandone l'ordine 15
Socket system call sys/types.h sys/socket.h /* per socket() */ sys/un.h /* per dominio AF_UNIX */ netinet/in.h arpa/inet.h netdb.h /* per dominio AF_INET */ int socket (int domain, int type, int protocol) domain specifica il dominio (AF_UNIX, AF_INET, etc.) type specifica il tipo di comunicazione(sock_stream, SOCK_DGRAM) protocol specifica il protocollo da utilizzare Tipicamente 0 per indicare che si vuole utilizzare il protocollo di default per la coppia domain/type in questione Ritorno restituisce un descrittore di file associato al nuovo socket -1 se si è verificato un errore 16
Socket - esempio Comunicazione via socket AF_UNIX (tralasciamo gestione errori) #include <sys/un.h> /* ind AF_UNIX */ #define SOCKNAME./mysock int main (void){ int fd_skt, fd_c; struct sockaddr_un sa; /* indirizzo AF_UNIX */ // creazione indirizzo strcpy(sa.sun_path, SOCKNAME); sa.sun_family = AF_UNIX; /* segue*/ } sockaddr_un è la struttura utilizzata in fase di bind. La sua definizione è: struct sockaddr_un { unsigned short sun_family; /* AF_UNIX */ char sun_path[108]; } 17
Socket esempio di creazione server/client E' possibile creare nello stesso file sia il client che il server int main (void){ int fd_skt, fd_c; struct sockaddr_un sa; /* ind AF_UNIX */ strcpy(sa.sun_path, SOCKNAME); sa.sun_family = AF_UNIX; if (fork()!=0) { /* padre, server */ fd_skt = socket(af_unix,sock_stream,0); /* segue */ }else { /* figlio, client */ fd_skt = socket(af_unix,sock_stream,0); /* segue */ } } 18
Socket system call bind() int bind (int sock_fd, const struct sockaddr *sa, socklen_t sa_len) Caratteristiche associa alla socket con descrittore sock_fd uno specifico indirizzo, puntato da sa, di lunghezza sa_len Tipicamente con AF_UNIX si deve indicare un file che non esiste sa_len è passato come sizeof (struct sockaddr) Ritorno 0 in caso di successo -1 errore con errno settato 19
Socket esempio di creazione server/client Aggiungiamo la chiamata a bind int main (void){ int fd_skt, fd_c; struct sockaddr_un sa; /* ind AF_UNIX */ strcpy(sa.sun_path, SOCKNAME); sa.sun_family = AF_UNIX; if (fork()!=0) { /* padre, server */ fd_skt = socket(af_unix,sock_stream,0); bind(fd_skt, (struct sockaddr *) &sa, sizeof(sa)); /* segue */ }else{ /* figlio, client */ fd_skt = socket(af_unix,sock_stream,0); /* segue */ } } 20
Socket system call listen() int listen(int sockfd, int backlog) Caratteristiche usata da un server connection-oriented per indicare che desidera ricevere connessioni sulla socket locale sockfd backlog indica il numero massimo di richieste di connessioni che possono essere messe in coda. In genere si utilizza SOMAXCONN definito in sys/socket.h Ritorno 0 in caso di successo -1 errore con errno settato 21
Socket esempio di creazione server/client Aggiungiamo la chiamata a listen int main (void){ int fd_skt, fd_c; struct sockaddr_un sa; /* ind AF_UNIX */ strcpy(sa.sun_path, SOCKNAME); sa.sun_family = AF_UNIX; if (fork()!=0) { /* padre, server */ fd_skt = socket(af_unix,sock_stream,0); bind(fd_skt, (struct sockaddr *) &sa, sizeof(sa)); listen(fd_skt,somaxconn) /* segue */ }else{ /* figlio, client */ /* segue */ } } 22
Socket system call accept() int accept (int sock_fd, const struct sockaddr * sa, socklen_t *sa_len); Caratteristiche usata per attendere una richiesta di connessione sul socket sock_fd rimuove la richiesta dalla coda e carica l'indirizzo del client in sa e la sua lunghezza in sa_len se non esistono richieste di connessione pendenti, il processo è sospeso Ritorno un'altra socket descriptor con le stesse proprietà di sock_fd, connessa con il client -1 errore con errno settato 23
Socket esempio di creazione server/client Aggiungiamo la chiamata a accept int main (void){ int fd_skt, fd_c; struct sockaddr_un sa; /* ind AF_UNIX */ strcpy(sa.sun_path, SOCKNAME); sa.sun_family = AF_UNIX; if (fork()!=0) { /* padre, server */ fd_skt = socket(af_unix,sock_stream,0); bind(fd_skt, (struct sockaddr *) &sa, sizeof(sa)); listen(fd_skt,somaxconn) fd_c = accept(fd_skt,null,0); /* segue */ }else{ /* figlio, client */... } 24
Socket system call connect() int connect(int sock_fd, const struct sockaddr *sa, socklen_t sa_len); Caratteristiche usata da un client per connettere a sock_fd alla raggiungibile da sa invia al server l'indirizzo locale a cui inviare la risposta, assegnando una porta libera locale Sospende il processo fino a che la connessione non è stata stabilita Ritorno 0 in caso di connessione stabilita -1 se la socket non esiste o la coda è piena. errno viene settato 25
Socket esempio di creazione server/client Aggiungiamo la chiamata a connect #define N 100... }else{ /* figlio, client */ fd_skt=socket(af_unix,sock_stream,0); while (connect(fd_skt,(struct sockaddr*)&sa, sizeof(sa)) == -1 ) { if ( errno == ENOENT ) sleep(1); /* la socket non esiste ancora */ else exit(exit_failure); } write(fd_skt, Hallo!, 7); read(fd_skt, buf, N); printf( Client got: %s\n, buf) ; close(fd_skt); exit(0);... 26
Socket esempio di creazione server/client #define N 100... if (fork()!=0) { /* padre, server */ fd_skt = socket(af_unix,sock_stream,0); bind(fd_skt,(struct sockaddr *)&sa, sizeof(sa)); listen(fd_skt,somaxconn); fd_c = accept(fd_skt, NULL,0); read(fd_c, buf, N); printf( Server got: %s\n,buf) ; write(fd_c, Bye!, 5); close(fd_skt); close(fd_c); exit(0); }... 27
Socket esempio finale Compilazione bash:~$ gcc -Wall -pedantic clserv.c -o clserv Esecuzione bash:~$./clserv -- un po di attesa Server got: Hallo! Client got: Bye! bash:~$ 28
Socket - commenti Nell esempio visto manca completamente la gestione degli errori (deve essere effettuata al solito modo) Una volta creata la connessione si possono leggere e scrivere i dati come con una pipe Si può fare molto di più dell esempio: gestire più client contemporaneamente indirizzi AF_INET: comunicazione con macchine diverse usare comunicazioni connectionless (datagram) senza creare una connessione permanente gestire macchine con diversi endians gestire dettagli del comportamento dei socket, accedere al database dei nomi (eg. www.google.it) 29
Socket: gestire più client Soluzioni tipiche per soddisfare più richieste: Utilizzare un server multithreaded: Un thread dispatcher che si mette in attesa ripetutamente su accept() per una nuova connessione ogni volta che la connessione è stata stabilita viene creato un nuovo thread worker Il thread worker si mette in attesa con read() dei messaggi in arrivo solo dal client relativo ad una particolare connessione Utilizzare un server sequenziale e select(): Permette di capire quale file descriptor è pronto 30
Socket Server Iterativo Vs Concorrente int sockfd, newsockfd;... if ( (sockfd = socket(...) ) < 0) /* err */ if ( (bind(sockfd, my_addr, my_ll) < 0) /* err */ if ( (listen(sockfd, 5) < 0) /* err */ int sockfd, newsockfd;... if ( (sockfd = socket(...) ) < 0) /* err */ if ( (bind(sockfd, my_addr, my_ll) < 0) /* err */ if ( (listen(sockfd, 5) < 0) /* err */ for( ; ; ) { newsockfd=accept(sockfd,cli_addr,cli_ll); if (newsockfd < 0) /* accept error */ read(newsockfd,...); write(newsockfd,...); close (newsockfd); } for( ; ; ) { newsockfd=accept(sockfd,cliaddr,clill); if (newsockfd < 0) /* accept error */ if (fork() == 0) { /* child */ close(sockfd); read(newsockfd,...); write(newsockfd,...); exit (0); } close (newsockfd); /* parent */ } 31
Socket Server & select() System call select() definita in sys/select.h permette di aspettare contemporaneamente file descriptor multipli (con timeout) int select(int nfds, /* # of file descriptors */ fd_set *rdset, /* Read descriptor set */ fd_set *wrset, /* Write descriptor set */ fd_set *errset, /* Exception descriptor set */ struct timeval *timeout); /* Timeout value */ Parametri di input nfsd è la dimensione massima dei sottoinsiemi *...set sono i sets dei file descriptors realizzati tramite mappe di bit timeout è il timeout di attesa (opzionale) Parametri di output 0 timeout; -1 errore (see errno); k < 0 numero di descriptors pronti per l'i/o Modifica fd_set ponendo a 1 i bit relativi ai file descriptors pronti per l'i/o 32
Socket Server & select() Semantica Si blocca finchè uno dei descrittori specificati ha dati rilevanti. In particolare: un descrittore fd atteso per la lettura (rdset) è considerato pronto quando una read su fd non si blocca, quindi anche se è stato chiuso con EOF la read non si blocca un descrittore fd atteso per la scrittura (wrset) è considerato pronto quando una write su fd non si blocca un descrittore fd atteso per eccezioni (errset) è considerato pronto quando si è verificato un errore all uscita della select() i set sono modificati per indicare chi è pronto. 33
Socket Server & select() Come specificare i set rdset, wrset, errset sono maschere di bit e vengono manipolate usando delle macro: FD_ZERO(&fdset) azzera la maschera fdset FD_SET(fd,&fdset) mette a 1 il bit corrispondente al file descriptor fd nella maschera fdset FD_CLR(fd,&fdset) mette a 0 il bit corrispondente al file descriptor fd nella maschera fdset FD_ISSET(fd,&fdset) vale 1 se il bit corrispondente al file descriptor fd nella maschera fdset era a 1 e zero altrimenti 34
Socket Server & select() Come specificare i set: esempio 1. tipicamente si azzera un set e poi si mettono a 1 i bit che interessano: 2. creare la maschera di attesa per il primo fra fd1 e fd2 pronto in lettura fd_set rdset; FD_ZERO(&rdset); FD_SET(fd1,&rdset); FD_SET(fd2,&rdset); Come controllare chi è pronto? non c è modo di avere la lista, si devono testare ciascuno con FD_ISSET if (FD_ISSET(fd1,&rdset)) read(fd1,buf,n); else if (FD_ISSET(fd2,&rdset)) read(fd2,buf,n);... 35
Socket Server & select(): il server static void run_server(struct sockaddr * psa) { int fd_sk, /* socket di connessione */ fd_c, /* socket di I/O con un client */ fd_num=0, /* max fd attivo */ fd; /* indice per verificare risultati select */ char buf[n]; /* buffer messaggio */ fd_set set, /* l insieme dei file descriptor attivi */ rdset; /* insieme fd attesi in lettura */ int nreaded; /* numero caratteri letti */... } 36
Socket Server & select(): il server static void run_server(struct sockaddr * psa) { int fd_sk, fd_c, fd_num=0, fd; char buf[n]; fd_set set, rdset; int nreaded; fd_sk = socket(af_unix, SOCK_STREAM, 0); bind(fd_sk,(struct sockaddr *)psa, sizeof(*psa)); listen(fd_sk, SOMAXCONN); /* manteniamo il massimo indice di descrittore attivo in fd_num */ if (fd_sk > fd_num) fd_num = fd_sk; FD_ZERO(&set); FD_SET(fd_sk,&set);... } 37
Socket Server & select(): il server static void run_server(struct sockaddr * psa) { int fd_sk, fd_c, fd_num=0, fd; char buf[n]; fd_set set, rdset; int nreaded; fd_sk = socket(af_unix, SOCK_STREAM, 0); bind(fd_sk,(struct sockaddr *)psa, sizeof(*psa); listen(fd_sk, SOMAXCONN); if (fd_sk > fd_num) fd_num = fd_sk; /* max num descrittore attivo*/ FD_ZERO(&set); FD_SET(fd_sk,&set); while (TRUE) { rdset = set; /* re-init x ogni ciclo dato che select lo modifica */ /* nota fd_num + 1: vogliamo il N max di descrittori attivi, non l indice massimo */ if (select(fd_num + 1, &rdset, NULL, NULL, NULL) == -1)) /* gestione errore */... } 38
Socket Server & select(): il server static void run_server(struct sockaddr * psa) {... /* continua dal while */ else { /* select OK */ for (fd = 0; fd < fd_num; fd++) { if (FD_ISSET(fd, &rdset)) { if (fd == fd_sk) { /* sock connect pronto */ fd_c = accept(fd_sk, NULL,0); FD_SET(fd_c, &set); if (fd_c > fd_num) fd_num = fd_c; } else { /* sock I/0 pronto */ nreaded = read(fd, buf, N); if (nreaded == 0 ) { /* EOF client finito */ FD_CLR(fd, &set); fd_num = aggiorna(&set); close(fd);... } 39
Socket Server & select(): il server static void run_server(struct sockaddr * psa) {... /* continua dal while */ else { /* nread!=0 */ printf( Server got: %s\n, buf) ; write(fd, Bye!, 5); } }... } /* chiude fino al while() */ } La parte del server termina qui. Vediamo il client... 40
Socket Server & select(): il client static void run_client(struct sockaddr * psa) { /* I figli sono generati da fork() */ if (fork() ==0 ) { /* figlio, client */ int fd_skt; char buf[n]; fd_skt=socket(af_unix, SOCK_STREAM, 0); while (connect(fd_skt,(struct sockaddr*)&sa, sizeof(sa)) == -1 ) { if ( errno == ENOENT ) sleep(1); else exit(exit_failure); } write(fd_skt, Hallo!,7); read(fd_skt,buf,n); printf( Client got: %s\n, vbuf) ; close(fd_skt); exit(exit_success); } } 41
Socket Server & select(): main di esecuzione... #include <sys/select.h> #include <sys/un.h> /* ind AF_UNIX */ #define SOCKNAME./mysock" #define N 100 int main (void){ int i; struct sockaddr_un sa; /* ind AF_UNIX */ strcpy(sa.sun_path, SOCKNAME); sa.sun_family=af_unix; for(i=0; i< 4; i++) } run_client(&sa); /* attiva un client*/ run_server(&sa); /* attiva il server */ return 0; 42
Socket esempio finale Compilazione bash:~$ gcc -Wall -pedantic clserv.c -o clserv Esecuzione bash:~$./clserv Server got: Hallo! Server got: Hallo! Client got: Bye! Client got: Bye! Server got: Hallo! Server got: Hallo! Client got: Bye! Client got: Bye! Tuttavia il server rimane attivo... 43
Socket Server & select(): considerazioni Ancora qualcosa un modo per terminare il server è utilizzando i segnali Es.: SIGINT CTRL-C È possibile personalizzare la gestione del segnale in modo da farlo terminare gentilmente Rimandiamo quindi ai segnali... Sono disponibili altre SC per il controllo dei descrittori pronti pselect poll... 44
Socket & internet AF_INET I socket con indirizzi AF_UNIX possono essere usati solo sulla stessa macchina Ci sono molte altre famiglie di indirizzi Accenneremo ad AF_INET famiglia di indirizzi che permette di far comunicare i socket su Internet vedremo come usarli (assumeremo noti i dettagli su come funziona Internet) Prima vedremo quanto sia importante tenere in considerazione come i dati viaggiano tra i sistemi 45
Big and Little endian Comunicazione tra macchine diverse Se comunichiamo una sequenza di byte fra macchine diverse: La comunicazione preserva l ordine dei byte (quindi non ci sono problemi con le stringhe) Ci sono però due modi di rappresentare i numeri binari; Es.: D04C esadecimale (2 byte) 46
Big and Little endian Comunicazione tra macchine diverse Quindi se comunico D04C esadecimale da little endian a big endian Verrà inviato prima 4C e poi D0 La macchina ricevente interpreta il primo come byte più significativo e il secondo come byte meno significativo Quindi il numero ricevuto sarò 4CD0 47
Big and Little endian Comunicazione tra macchine diverse È necessario avere un ordine dei byte indipendente dalla macchina e usare sempre quello per gli scambi È il network byte order Network byte order La macchina mittente converte dalla sua Rappresentazione da host byte order al network byte order Il ricevente fa l inverso È necessario farlo solo per dati binari grezzi non per dati già in formato standard (es. JPEG) 48
Network byte order Definite in arpa/inet.h uint16_t htons (uint16_t hostnum); /* TO NETWORK BYTE ORDER */ uint32_t htonl (uint32_t hostnuml); /* TO NETWORK BYTE ORDER */ uint16_t ntohs (uint16_t netnum); /* TO HOST BYTE ORDER */ uint32_t ntohl (uint32_t netnuml); /* TO HOST BYTE ORDER */ Esempio stampare i 2 byte che rappresentano D04C in ordine crescente in host byte order in network byte order 49
Network byte order: esempio #include <arpa/inet.h> #include <stdio.h> int main (void) { uint16_t n = 0xD04C, nnet; unsigned char* p; p = (unsigned char *) &n; printf( %x %x \n, *p, *(p+1)); /* host order */ nnet = htons(n); p = (unsigned char *) &nnet; printf( %x %x \n, *p, *(p+1)); /* net order */ return 0; } 50
Network byte order: esempio Compilazione bash:~$ gcc -Wall -pedantic endian.c -o endian Esecuzione bash:~$./endian 4c d0 d0 4c bash:~$ Compilato su AMD athlon XP /proc/cpuinfo AMD, x86 sono little endian Il network byte order è big endian (dai vecchi VAX) 51
Indirizzi AF_INET Sono indirizzi fomati da: un identificativo della macchina (indirizzo IPv4) numero a 32 bit assegnato ad un interfaccia di rete sulla macchina vengono scritti in dotted notation a gruppi di 4 byte convertiti in decimale separati da punti (es. 216.109.125.70) ognuno ha anche un nome simbolico (es. www.yahoo.com) un identificativo della porta all interno della macchina è un numero a 16 bit identifica un servizio. Es: 80 porta server HTTP 21 porta server ftp etc... 52
Indirizzi AF_INET Strutture definita in netinet/in.h struct sockaddr_in { sa_family_t sin_family; /* AF_INET, etc */ in_port_t sin_port; /* numero di porta (uint16_t) */ struct in_addr sin_addr; /* indirizzo IPv4 */ }; struct in_addr { }; in_addr_t s_addr; /* indirizzo (uint32_t) */ sin_addr Identifica l'indirizzo dell'host sin_port Identifica la porta di ascolto Le strutture dati verranno utilizzate nella chiamata a bind() 53
Indirizzi AF_INET: esempio /* vogliamo rappresentare 216.119.125.70 porta 80*/ struct sockaddr_in sa; sa.sin_family = AF_INET; /* numero di porta (uint16_t) deve essere in network byte order */ sa.sin_port = htons(80); /* indirizzo IPv4 (uint32_t) deve essere in network byte order 216.119.125.70 */ sa.sin_addr.s_addr = htonl(216<<24) + htonl(119<<16) + htonl(125<<8) + htonl(70); /* Variante dell'ultimo punto */ sa.sin_addr.s_addr = inet_addr( 216.109.125.70"); 54
Indirizzi AF_INET: funzioni utili Alcune funzioni utili: In arpa/inet.h sono dichiarate alcune funzioni utili per manipolare indirizzi Internet. unsigned long int inet addr (const char *name) converte name (es. 10.0.0.1) in network byte order Restituisce -1 se l'indirizzo non è valido. char *inet ntoa (struct in_addr addr) converte addr (è in network byte order) in numeri e punti. inet_ntop, inet_pton sono la loro generalizzazione e funzionano anche con IPv6 55
Indirizzi AF_INET: esempio HTTP /* vogliamo ottenere la pagina HTTP che corrisponde a www.yahoo.com chiedendola direttamente al web server di 216.119.125.70 porta 80*/ #define REQUEST GET / HTTP/1.0\r\n\r\n" /* DEFAULT HTTP GET REQUEST */ int main (void) { struct sockaddr_in sa; int fd_skt, nread; char buf[1024]; sa.sin_family = AF_INET; sa.sin_port = htons(80); /* DEFAULT HTTP SERVER PORT */ sa.sin_addr.s_addr = inet_addr( 216.109.125.70"); /* CONNECT TO YAHOO WEB SERVER*/ fd_skt =socket(af_inet, SOCK_STREAM, 0); connect(fd_skt, (struct sockaddr*)&sa, sizeof(sa)); write(fd_skt, REQUEST, strlen(request)); /* SEND HTTP REQUEST */ nread = read(fd_skt, buf, sizeof(buf)); write(1, buf, nread); /* WRITE TO STDOUT */ close(fd_skt); return 0; } // Nota: la gestione degli errori è stata omessa 56
Socket esempio finale Compilazione bash:~$ gcc -Wall -pedantic getpage.c -o getp Esecuzione bash:~$./getp HTTP/1.1 301 Moved Permanently Date: Mon 20 Mar 2006 15:52:34 GMT Location:http://smallbusiness.yahoo.com/domains/ Connection: close Content-Type: text/html The document has moved... bash:~$ 57
Indirizzi AF_INET: nomi mnemonici Di solito non si usano i numeri ma dei nomi mnemonici: www.cs.unicam.it, www.google.com, etc. È possibile convertire i nomi mnemonici in dotted notation usando un database distribuito (DNS) accessibile da C con getaddrinfo È possibile recuperare in formato stringa gli eventuali errori generati dalla chiamata a getaddrinfo con la chiamata a gai_strerror int gethostname(char *name, size_t namelen) È possibile ottenere il nome mnemonico pubblico della macchina su cui stiamo girando con gethostname namelen è la lunghezza massima del vettore name 58