Reti di Calcolatori. Saper scrivere semplici programmi per la comunicazione su una rete di calcolatori. Assunzione. Ambiente di sviluppo LABORATORIO

Documenti analoghi
P3-04: I/O multiplexing

PRATICA - Lezione 1. Autunno PRATICA (Lez. 1)

Sono definiti per vari protocolli

SERVER CLIENT. Letteralmente significa presa (di corrente) È l astrazione di un canale di comunicazione fra due computer connessi da una rete

SERVER CLIENT. Struttura di un Applicazione UDP. Socket UDP. Parametri di sendto. Funzioni di Input/Output. Prof. Vincenzo Auletta

P3-05: Socket Options

Laboratorio Reti di Calcolatori (A.A ) Delfina Malandrino.

Comunicazione Connectionless o Datagram

Problema. I/O Multiplexing. Struttura delle Operazioni di Lettura. Modelli di I/O. Prof. Vincenzo Auletta

Server Iterativi. Server TCP Ricorsivi. Fork. Server Ricorsivi. un server iterativo gestisce una connessione alla volta. Prof.

Laboratorio di Reti di Calcolatori

Laboratorio di Reti di Calcolatori

Laboratorio di Reti di Calcolatori

Funzioni bloccanti e soluzioni

Funzioni bloccanti e soluzioni. Funzioni bloccanti e soluzioni (2) Parametri della funzione select() Funzione select()

funzione fork() La funzione fork è usata per duplicare un processo. #include <unistd.h> pid_t fork (void);

Timeout. Socket Avanzati. Funzione alarm() Client echo UDP con alarm

Laboratorio Reti di Calcolatori (A.A ) Delfina Malandrino.

Corso di laurea in Informatica. Reti di Calcolatori A.A Prof. Roberto De Prisco LABORATORIO. Lezione

LABORATORIO di Reti di Calcolatori

Opzioni del Socket. Socket Options. Opzioni di Livello Socket. Livello delle Opzioni

Laboratorio di. Reti Informatiche. Corso di Laurea Triennale in Ingegneria Informatica A.A. 2016/2017. Ing. Niccolò Iardella

Esercitazione [08] Server multi-process/multi-thread

Reti (già Reti di Calcolatori )

Esercitazione [7] Client/Server con Socket

Reti di Calcolatori - Laboratorio. Lezione 5. Gennaro Oliva

funzione close() La funzione close è utilizzata normalmente per chiudere un descrittore di file, è utilizzata per chiudere un socket e terminare una

Sviluppo di Applicazioni su Rete. Introduzione all API socket di Berkeley. Interazione tra Processi. Modello Client-Server

Introduzione. Prof. Roberto De Prisco. Telefonini. o almeno. e non rispondere. Università degli studi di Salerno Laurea e Diploma in Informatica

Esercitazione [7] Server multi-process/multi-thread

Progettazione di Applicazioni Robuste. Applicazione Echo. Schema Generale di un Server TCP Ricorsivo 1. Applicazione echo

Progettazione di un client TCP. Progettazione di un server TCP. Esempio: daytime TCP. Client TCP daytime

Socket TCP. prima parte

Scrittura dei programmi applicativi di rete

Programmazione socket. Queste slide sono distribuite con licenza Creative Commons Attribuzione-Non commerciale-condividi allo stesso modo 2.

request reply richiesta client processo di servizio processo server principale From - Valeria Cardellini, Corso Sist. Distr. A.A.

I.I.S. G.B. PENTASUGLIA MATERA ISTITUTO TECNICO SETTORE TECNOLOGICO LICEO SCIENTIFICO SCIENZE APPLICATE. Classe: 5Ci

Socket. Nei sistemi operativi moderni i servizi disponibili in rete si basano principalmente sul modello client/server.

Laboratorio reti AA 2006/2007. Dott. Matteo Roffilli Ricevimento in ufficio dopo la lezione

Programmazione di applicazioni di rete con socket - parte 1

L uso di Socket UDP. Usiamo le API Winsock incluse in <Winsock.h> A.A. 2005/06. Dott.ssa Valeria Carofiglio

Laboratorio reti AA 2008/2009. Dott. Matteo Roffilli Ricevimento in ufficio dopo la lezione

Esercitazione [6] Client/Server con Socket

Programmazione di Rete

Socket II MIDLAB. Sirio Scipioni. M I D L A B.

Laboratorio reti AA 2007/2008. Dott. Matteo Roffilli Ricevimento in ufficio dopo la lezione

*HVWLRQHDYDQ]DWDGHOOH6RFNHWLQ& ODSULPLWLYDVHOHFW

L interfaccia socket

LABORATORIO di Reti di Calcolatori

Socket. Nei sistemi operativi moderni i servizi disponibili in rete si basano principalmente sul modello client/server.

Laboratorio di Reti di Calcolatori

TECN.PROG.SIST.INF. UDP socket in Windows. Roberta Gerboni

LABORATORIO di Reti di Calcolatori

Introduzione ai socket

RETI DI CALCOLATORI. Prof. PIER LUCA MONTESSORO Ing. DAVIDE PIERATTONI. Facoltà di Ingegneria Università degli Studi di Udine

SISTEMI OPERATIVI. Processi in Linux. Giorgio Giacinto Sistemi Operativi

5 Esercitazione (svolta):

L uso di Socket UDP. TCP vs. UDP UDP

Interazione (TCP) Client-Server con le socket

Applicazione ECHO. Dott. Delfina Malandrino. p//ssd s / d

Sistemi Operativi (M. Cesati)

Laboratorio reti AA 2007/2008. Dott. Matteo Roffilli Ricevimento in ufficio dopo la lezione

Laboratorio reti AA 2008/2009. Dott. Matteo Roffilli Ricevimento in ufficio dopo la lezione

Sistemi di Elaborazione. Introduzione alla Programmazione distribuita

Esempi di Client e Server

Interazione (TCP) Client-Server con le socket

Cenni di programmazione distribuita in C++ Mauro Piccolo

Una semplice applicazione client/server 1

Socket per TCP: Fondamenti

Socket per TCP: Fondamenti

Acknowledgment: Prof Vincenzo Auletta, Università di Salerno. Approfondimento alla programmazione distribuita

Scrittura dei programmi applicativi di rete

Comunicazioni fra processi remoti: i socket nello UNIX di Berkeley

I Socket. Laboratorio Software M. Grotto R. Farina

Interazione con il DNS Conversioni di Nomi ed Indirizzi

TECN.PROG.SIST.INF. I Socket Roberta Gerboni

Corso di Reti di Calcolatori T

Esempio 1: stampa locale di file remoto

Laboratorio di Programmazione in Rete

Università di Roma Tor Vergata Corso di Laurea triennale in Informatica Sistemi operativi e reti A.A Pietro Frasca.

Sistemi Operativi. Marzo-Giugno 2011 matricole congrue 0 mod 3. Controllo dei processi - I

Sistemi Operativi (M. Cesati)

Socket per TCP: Fondamenti

Socket TCP. seconda parte

File binari e file di testo

Laboratorio di. Reti Informatiche. Corso di Laurea Triennale in Ingegneria Informatica A.A. 2016/2017. Ing. Niccolò Iardella

LABORATORIO di Reti di Calcolatori

IPC Inter Process Communication

Università degli Studi di Cagliari Corso di Laurea Specialistica in Ingegneria Elettronica. SISTEMI OPERATIVI A.A. 2004/2005 Docente: Giorgio Giacinto

Reti di Calcolatori - Laboratorio. Lezione 7. Gennaro Oliva

Sistemi Operativi Teledidattico

Corso di Reti di Calcolatori T

Laboratorio di Reti di Calcolatori

Socket Programming. Socket Programming. Università di Palermo

C UDP in Windows p53 - variante 1

Laboratorio di. Reti Informatiche. Corso di Laurea Triennale in Ingegneria Informatica A.A. 2016/2017. Ing. Niccolò Iardella

Parametri valore-risultatorisultato

Transcript:

Obiettivo 2 orso di laurea in Informatica Reti di alcolatori A.A. 2007-2008 Prof. Roberto De Prisco aper scrivere semplici programmi per la comunicazione su una rete di calcolatori Assunzione apete già programmare Lezione 1 Ambiente di sviluppo LINUX ompilatore ocket per la comunicazione in rete Desktop grafico 3 Linea di comando 4 Desktop grafico Usare solo per scaricare gli esempi Terminale testuale Line di comando Prompt Terminale testuale TRL-ALT-F1 TRL-ALT-F6 robdep@zircone:~/orsi/reti/> gmake gcc -g -O0 -Werror -c lib-errori.c gcc -g -O0 -Werror -c lib-corso-reti.c compiling daytimesrv.c with rule 1 robdep@zircone:~/orsi/reti/> omandi ls, cd, pwd, cp, rm, mv, cat, mkdir man <comando> hell 5 File 6 hell È il programma che erpreta i comandi Editor di file vi emacs BAH È la shell standard di Linux Echo $HELL Occorre imparare ad usare uno di questi due editor Basta il minimo indispensabile ezione risorse del ito web Link a pagine su Bash e altro (es. editor vi) ito Web del corso LO Manuali Una ricerca su Internet vi fornirà numerosi fonti manuale editor vi 1

Programmi in #include <stdio.h> main( argc, char **argv) { union { short s; char c[sizeof(short)]; un; un.s = 0x0102; prf("pu = %s - byte ordering: ",getenv("pu")); if (sizeof(short) == 2) { if ( un.c[0] == 1 && un.c[1] == 2 ) prf ("big-endian endian\n"); n"); else if ( un.c[0] == 2 && un.c[1] == 1 ) prf ("little-endian endian\n"); n"); else prf("unknown\n"); n"); else prf("size of short: %d.\n",sizeof(short)); exit(0); 7 ompilazione e Makefile Il sorgente va compilato ompilare un programma consiste nel 1.trasformare il sorgente in codice oggetto 2.unire tale codice oggetto con le librerie (link) 8 gcc (GNU ompiler) gcc ls1.c gcc o ls1 ls1.c gcc Lmylibpath lmylib Imyincpath O DDEBUG... Makefile make... fa tutto Makefile 9 Esempio 10 #Makefile ALL = lib-errori lib-corso-reti \ daytimesrv daytimecli daytimesrv-ric \ echosrv echocli echosrv-sigh all: $(ALL).c: lib-errori.o lib-corso-reti.o @echo compiling $< with rule 1 gcc $< -g -O0 Werror -o $@ lib-errori.o lib-corso-reti.o lib-errori: lib-errori.c gcc -g -O0 -Werror -c lib-errori.c lib-corso-reti: lib-corso-reti.c gcc -g -O0 -Werror -c lib-corso-reti.c clean: rm -f $(ALL) rm -f *~ rm -f *.o robdep@zircone:~/orsi/reti/> gmake gcc -g -O0 -Werror -c lib-errori.c gcc -g -O0 -Werror -c lib-corso-reti.c compiling daytimesrv.c with rule 1 gcc daytimesrv.c -g -O0 -Werror -o daytimesrv lib-errori.o lib-corso-reti.o compiling daytimecli.c with rule 1 gcc daytimecli.c -g -O0 -Werror -o daytimecli lib-errori.o lib-corso-reti.o compiling daytimesrv-ric.c with rule 1 gcc daytimesrv-ric.c -g -O0 -Werror -o daytimesrv-ric liberrori.o lib-corso-reti.o compiling echosrv.c with rule 1 gcc echosrv.c -g -O0 -Werror -o echosrv lib-errori.o libcorso-reti.o Librerie e include file #include <nome.h> erca il file da includere nelle directory di ricerca standard del compilatore /usr/include, /usr/lib/include,... # include nome.h erca il file da includere nella cwd 11 Basic.h #ifndef BAI #define BAI #include <sys/types.h> /* basic system data types */ #include <sys/socket.h> /* basic socket definitions */ #include <sys/time.h> /* timeval{ for select() */ #include <time.h> /* timespec{ for pselect() */ #include <netinet/in.h> /* sockaddr_in{ and other Internet defns */ #include <arpa/inet.h> /* inet(3) functions */ #include <errno.h> 12 Al momento di eseguire il link il compilatore cerca il codice necessario nelle librerie librerie specificate con l nel comando di compilazione la ricerca di tali librerie è fatta in posti standard (/usr/lib, /usr/local/lib,...) e nelle directory specificate con L libreria di default (contiene prf) #include <unistd.h> #include <sys/wait.h> #include <sys/un.h> /* for Unix domain sockets */ #define MAXLINE 256 #define PORT 12345 #define BAKLOG 5 #define MAX(a, b) ((a) > (b)? (a) : (b)) #endif 2

Libreria gestioni errori 13 Libreria corso reti 14 Definisce varie funzioni per la gestione degli errori Facciamo il link con questa libreria per usare tali funzioni ono funzioni che stampano un messaggio di errore Alcune terminano l esecuzione del programma File lib-errori.c err_msg err_quit, err_sys stampa solo l errore chiamano exit Definisce varie funzioni per la lettura e scrittura dei socket onvezione sul nome reti_nomefunzione Esempi di funzioni della libreria reti_readn Legge esattamente n byte reti_writen crive esattamente n byte reti_readline Legge una riga Dettagli nel file lib-corso-reti.c ocket 16 orso di laurea in Informatica Reti di alcolatori A.A. 2007-2008 Prof. Roberto De Prisco Letteralmente significa presa (di corrente) È l astrazione di un canale di comunicazione fra due computer connessi da una rete ono definiti per vari protocolli ocket TP Lezione 2 Per TP/IP un socket identifica i due punti della connessione Un indirizzo IP ed una porta su un host Un indirizzo IP ed una porta sull altro host Funzioni per i socket 17 ockaddr_in 18 Tipica erazione in una connessione TP LIENT socket() connect() write() read() close() Dati (richiesta) tabilisce una connessione Notificazione di fine comunicazione Dati (risposta) socket() bind() listen() accept() read() write() read() close() Aspetta una connessione ERVER struct in_addr { in_addr_t s_addr; /* 32-bit, network byte ordered */ struct sockaddr_in { u8_t sin_len; sa_family_t sin_family; /* tipo di protocollo, AF_INET */ in_port_t sin_port; /* 16-bit, network byte ordered */ struct in_addr sin_addr; /* struttura indirizzo IP */ char sin_zero[8]; struct sockaddr { u8_t sin_len; sa_family_t sin_family; /* tipo di protocollo: AF_XXX */ char sa_data[14]; /* indirizzo specifico del protocollo */ sin_zero Utilizzata per far si che la grandezza della struttura sia almeno 16 byte sin_len Non è richiesta dallo standard Posix Esistono diverse strutture con grandezze differenti 3

Lunghezze strutture socket 19 Funzione socket 20 #include <sys/socket.h> socket( family, type, protocol ); Valore di ritorno: se errore un socket descriptor se OK ocket descriptor è come un file descriptor ono presi dallo stesso insieme e un ero è usato come file descriptor non può essere usato come socket descriptor e viceversa ocket e file sono visti più o meno allo stesso modo read, write, close sono le stesse funzioni dei file Funzione socket family Un ero che specifica quale famiglia di protocolli si ende usare: AF_INET IPv4 AF_INET6 IPv6 AF_LOAL prot. locale (client e server sullo stesso host) AF_ROUTE ockets per routing altri type Un ero che dice il tipo di socket OK_TREAM OK_DGRAM OK_RAW protocol 0, tranne che per OK_RAW per uno stream di dati (TP) per datagrammi (UDP) per applicazioni dirette su IP 21 Funzione connect #include <sys/socket.h> connect( sd, struct sockaddr *servaddr, socklen_t addrlen); 22 Valore di ritorno: se errore, 0 se OK Permette ad un client di aprire una connessione con il server Il kernel sceglie una porta effimera (e l indirizzo IP) Nel caso di una connessione TP viene fatto l handshaking, in caso di errore ritorna (errno) ETIMEDOUT EONNREFUED EHOTUNREAH Funzione bind #include <sys/socket.h> bind( sd, struct sockaddr*myaddr, socklen_t addrlen); 23 Valore di ritorno: se errore, 0 se OK Permette ad un server di assegnare un indirizzo per il server al socket on TP l indirizzo può essere indirizzo IP (deve essere una delle erfacce) porta entrambi nessuno e la porta non è specificata (valore 0) ne viene scelta una effimera e l indirizzo IP è quello wildcard (INADDR_ANY, 0) viene usato quello designato come IP destinazione nel YN del client Funzione listen #include <sys/socket.h> listen( sd, backlog); Usata solo da un server TP, serve a 24 Valore di ritorno: se errore, 0 se OK 1. onvertire il socket da attivo a passivo, per far sì che il kernel accetti connessioni sul socket Per default un socket è creato attivo, e il kernel si aspetta che sia il socket di un client Nel diagramma a stati TP fa muovere da LOED a LITEN 2. Backlog specifica quante connessioni accettare e mettere in attesa per essere servite 4

Backlog 25 Funzione accept 26 #include <sys/socket.h> accept( sd, struct sockaddr*cliaddr, socklen_t addrlen); erver accept ODA connessioni completate (stato ETABLIHED) Valore di ritorno: se errore, socked descriptor se OK apertura conn. completata connect dal client YN apertura connessione ODA connessioni incomplete (stato YN_RVD) La somma degli elementi in entrambe le code non può superare il backlog Permette ad un server di prendere la prima connessione completata dalla coda e non ce ne sono si blocca cliaddr è un parametro valore-risultato In chiamata contiene il listening socket Al ritorno contiene il socket connesso al particolare client Daytime server 27 erver iterativo 28 #include "basic.h #include <time.h> daytimesrv.c main( argc, char **argv) { pid_t pid; listenfd, connfd; struct sockaddr_in servaddr; char buff[maxline]; time_t ticks; if (argc!= 2) err_quit("usage: daytimesrv <porta>"); if( (listenfd = socket(af_inet, OK_TREAM, 0)) < 0) err_sys("socket error"); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(inaddr_any); servaddr.sin_port = htons(atoi(argv[1])); if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0) err_sys("bind error"); if( listen(listenfd, 5) < 0 ) err_sys("listen error"); for ( ; ; ) { if( (connfd = accept(listenfd, (struct sockaddr *) NULL, NULL)) < 0) err_sys("accept error"); ticks = time(null); snprf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); write(connfd, buff, strlen(buff)); close(connfd); erver iterativo, serve i client uno alla volta Quando un client è connesso il seguente client deve aspettare Accettabile per server semplici come il daytime Funzione inet_pton #include <sys/socket.h> inet_pton( af, const char* stringa, void* dest); 29 Valore di ritorno: 0 se errore, > 0 se OK Trasforma un indirizzo IP da formato presentazione a formato network Presentazione: stringa 192.41.218.1 Network: sequenza di bit 11000000.00101001.110011010.00000001 Daytime client #include "basic.h" main( argc, char **argv) { sockfd, n; char recvline[maxline + 1]; struct sockaddr_in servaddr; if (argc!= 3) err_quit("usage: daytimecli <inidirrizzoip> <porta>"); if( (sockfd = socket(af_inet, OK_TREAM, 0)) < 0 ) err_sys("socket error"); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(atoi(argv[2])); if (inet_pton(af_inet, argv[1], &servaddr.sin_addr) <= 0) err_quit("inet_pton error for %s", argv[1]); daytimecli.c 30 if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) err_quit("connect error"); while ( (n = read(sockfd, recvline, MAXLINE)) > 0) { recvline[n] = 0; /* 0 finale richiesto dal per le stringhe */ fputs(recvline, stdout); exit(0); 5

erver ricorsivi 31 erver iterativi 32 Un server ricorsivo usa una copia di se stesso per servire una richiesta pid_t pid; listenfd, connfd; lient connect() Richiesta di connessione erver listensd listenfd = socket(.); /* riempi la struttura sockaddr_in (es. numero di porta) */ bind(listenfd,.) listen(listenfd, LITENQ) for ( ; ; ) { connfd = accept(listenfd, ); if ( (pid = fork()) == 0) { close(listenfd); /* figlio chiude il socket di ascolto */ DOIT(connfd); /* serve la richiesta */ close(connfd); /* chiude il socket */ exit(0); /* il figlio termina */ close(connfd); /* il padre chiude il socket della connessione */ Il server chiama accept() Viene creato un nuovo socket descriptor nel server per la connessione con questo particolare client lient connect() onnessione stabilita listensd connsd erver erver ricorsivi 33 erver ricorsivi 34 lient erver lient erver connect() onnessione stabilita listensd connect() listensd connsd padre connsd padre erver listensd onnessione stabilita erver listensd connsd figlio connsd Il padre chiude il socket della connessione Può accettare nuove connessioni figlio Il server chiama fork() Padre e figlio nel server condividono il socket Il figlio chiude il socket per l accettazione di nuove connessioni Può gestire la connessione con il client Getsockname e getpeername #include <sys/socket.h> getsockname( sd, struct sockaddr*localaddr, socklen_t addrlen); getpeername( sd, struct sockaddr*remoteaddr, socklen_t addrlen); 35 Valore di ritorno: se errore, socked descriptor se OK Ritornano l indirizzo locale associato al socket L indirizzo dell altro lato della connessione associata al socket erve perché Un client che non chiama bind non sa quale porta è stata usata Un client non sa l indirizzo IP usato se ci sono più erfaccie Una chiamata a bind con porta=0 assegna una porta effimera tessa cosa per l indirizzo IP (INADDR_ANY) Dopo una exec si può risalire agli indirizzi della connessione NB: un file descriptor rimane aperto quando si chiama exec #include #include Echo server (1) "basic.h" "echo.h" main( argc, char **argv) { pid_t childpid; listenfd, connfd; struct sockaddr_in servaddr, cliaddr; socklen_t cliaddr_len; if( (listenfd = socket(af_inet, OK_TREAM, 0)) < 0) err_sys("socket error"); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(inaddr_any); servaddr.sin_port = htons(port); /* daytime server */ if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0) err_sys("bind error"); if( listen(listenfd, LITENQ) < 0 ) err_sys("listen error"); 36 echosrv.c 6

Echo server (2) 37 Echo client (1) 38 for ( ; ; ) { cliaddr_len = sizeof(cliaddr); if( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddr_len)) < 0) err_sys("accept error"); if( (childpid = fork()) == 0 ) { close(listenfd); str_echo(connfd); exit(0); close(connfd); void str_echo( sockfd) { ssize_t n; char line[maxline]; for ( ; ; ) { if ( (n = read(sockfd, line, MAXLINE)) == 0) return; /* connection closed by other end */ write(sockfd, line, n); #include "basic.h" #include "echo.h" main( argc, char **argv) { sockfd, n; struct sockaddr_in servaddr; if (argc!= 2) err_quit("usage: echotcpcli <IPaddress>"); if ( (sockfd = socket(af_inet, OK_TREAM, 0)) < 0) err_sys("socket error"); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(port); /* echo server */ if (inet_pton(af_inet, argv[1], &servaddr.sin_addr) <= 0) err_quit("inet_pton error for %s", argv[1]); if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) err_sys("connect error"); str_cli(stdin, sockfd); /* svolge tutto il lavoro del client */ exit(0); echocli.c Echo client (2) 39 Echo server 40 void str_cli(file *fp, sockfd) { char sendline[maxline], recvline[maxline]; while (fgets(sendline, MAXLINE, fp)!= NULL) { reti_writen(sockfd, sendline, strlen(sendline)); if (reti_readline(sockfd, recvline, MAXLINE) == 0) err_quit("str_cli: server terminated prematurely"); fputs(recvline, stdout); Per semplicità facciamo girare server e client sulla stessa macchina prompt > echoserver & [1] 21130 prompt > netstat a Proto Recv-Q Q end-q Q Local address Foreign address (state) Tcp 0 0 *.9877 *.* LITEN prompt > echoclient 127.0.0.1 In un altra finestra prompt > netstat a Proto Recv-Q Q end-q Q Local address Foreign address (state) Tcp 0 0 localhost.9877 localhost.1052 ETABLIHED Tcp 0 0 localhost.1052 localhost.9877 ETABLIHED Tcp 0 0 *.9877 *.* LITEN A questo punto la connessione è stabilita Echo server 41 Echo server 42 prompt > echoclient 127.0.0.1 Digitata al terminale iao server Risposta del server iao server Digitata al terminale Arrivederci Risposta del server Arrivederci ^D Digitata al terminale prompt > prompt > netstat a a grep 9877 Tcp 0 0 localhost.1052 localhost.9877 TIME_WAIT Tcp 0 0 *.9877 *.* LITEN Digitando ^D, il client termina chiamando exit Il kernel chiude tutti i file descriptor, quindi anche i socket descriptor Quindi il socket del client viene chiuso La chiusura implica la spedizione di FIN al server La ricezione dell AK al FIN A questo punto il server è nello stato LOE_WAIT mentre il client è nello stato FIN_WAIT_2 La prima parte della chiusura di una connessione TP è conclusa Il server ha chiuso il socket Il client è nello stato di TIME_WAIT Il lato che chiude la connessione rimane in questo stato per un certo periodo (2ML) per 1. Mantenere informazioni nel caso l ultimo AK viene perso e l altro lato rispedisce l ultimo FIN 2. Permettere a vecchi pacchetti di essere eliminati dalla rete in modo da non farli erferire con successive connessioni Quando il server riceve il FIN è nella readline che ritorna EOF e quindi chiama exit I file descriptor vengono chiusi, quindi anche il socket ed un FIN viene spedito al client A questo punto la conessione è completamente terminata ed il client va nello stato TIME_WAIT mentre il server ha chiuso la connessione Dopo un certo periodo (2 Maximum egment Lifetime) il client chiude la connessione 7

egnale IGHLD In un server ricorsivo, il server crea un figlio per gestire la connessione quando la connessione viene chiusa il figlio termina Il sistema operativo manda un segnale di IGHLD al padre e il figlio diventa zombie Zombie sono dei processi terminati per i quali vengono mantenuti dei dati nel sistema operativo Zombie sono necessari per permettere al padre di controllare il valore di uscita del processo e utilizzo delle risorse del figlio (memoria, PU, etc.) Ovviamente non vogliamo lasciare zombie Occorre scrivere un signal handler che chiama wait 43 zombie Ognli client che termina lascia uno zombie <defunct> indica uno zombie robdep@zaffiro:~/orsi/reti/> echocli 127.0.0.1 ciao ciao ^D robdep@zaffiro:~/orsi/reti/> echocli 127.0.0.1 pippo pippo ^D robdep@zaffiro:~/orsi/reti/> ps PID TTY TIME MD 1077 pts/0 00:00:00 cat 22084 pts/2 00:00:00 bash 27162 pts/3 00:00:00 ssh 30007 pts/6 00:00:00 bash 30331 pts/11 00:00:00 bash 30761 pts/11 00:00:00 echosrv 30765 pts/11 00:00:00 echosrv <defunct> 30767 pts/11 00:00:00 echosrv <defunct> 30768 pts/6 00:00:00 ps Il client viene ucciso Il client viene ucciso 44 ignal handler 45 Interruzione delle system call 46 void sig_child( signo) { pid_t pid; stat; while ( pid = waitpid(,&stat,wnohang)) > 0) { prf( hild %d terminated\n,pid); n,pid); Utilizzando il gestore di segnali si evitano i processi zombie Appena il figlio finisce viene chiamata waitpid Prompt > echoserver & [2] 19287 prompt > echoclient 127.0.0.1 iao server iao server ^D hild 19293 terminated accept error: errupted system call Il segnale è stato catturato dal padre durante l esecuzione di accept Il gestore del segnale viene eseguito Poiché è stata errotta la funzione accept ritorna con il codice di errore EINTR Poiché la gestione di tale errore non è prevista il server termina l esecuzione Occorre tener presente questo problema In alcuni sistemi le system call sono automaticamente richiamate in altri no Una possibile soluzione 47 Reset connessione e accept 48 for ( ; ; ) { clilen = sizeof(cliaddr); if ( (connfd = accept(listenfd, &cliaddr, &clilen)) < 0) { if (errno = EINTR) continue; else { perror( accept error ); exit(1); e la chiamata ad accept ritorna EINTR accept viene richiamata e l errore è diverso da EINTR i gestisce l errore (nell esempio si chiama exit) Un altro errore tipico da gestire con accept è il reset della connessione prima della chiamata ad accept La connessione diventa ETABLIHED Il client spedisce un RT Il server chiama accept Accept ritorna un codice di errore EONNABORTED Il server può richiamare accept per la prossima connessione 8

Terminazione del server 49 IGPIPE 50 osa succede se il server termina prematuramente? Prompt > echoclient 127.0.0.1 iao iao Arrivederci Il server non risponde (dipende dal codice) Il server viene ucciso Al kill i socket descriptor vengono chiusi Un FIN viene spedito al client Il client spedisce Arrivederci al server È permesso perché il client non ha chiuso il socket Il client chiama readline che ritorna EOF Non si aspetta di ricevere EOF quindi stampa il messaggio di errore e termina osa succede se il client ignora l errore su readline e scrive nel socket? Questo può capitare se il codice ha due write consecutive La prima fa sì che il server spedisca RT La seconda crea il problema Viene generato un segnale di IGPIPE Il processo termina se il segnale non viene catturato o ignorato e IGPIPE è ignorato l operazione di write genera l errore di EPIPE oluzione semplice, quando non si deve reagire all errore 1. Ignorare (IG_IGN) il segnale di IGPIPE Assume che non occorre fare niente di speciale in tale circostanza 2. ontrollare l errore di EPIPE sulle write e nel caso di errore terminare (non scrivere più) Macchina server non raggiungibile 51 Un altra possibile causa di errore è se la macchina server non risponde proprio Diverso da uccidere il processo server (in quel caso vengono spediti FIN, RT) Può dipendere dalla rete O dalla macchina server Il client è bloccato in readline TP ritrasmetterà i dati per ricevere l AK fino ad un certo timeout La funzione di lettura dal socket ritorna un errore ETIMEOUT EHOTUNREAH, ENETUNREAH erver shutdown and reboot La connessione viene stabilita Il server va giù e fa il reboot senza che il client se ne accorga Non c è comunicazione durante lo shutdown (server scollegato dalla rete altrimenti spedisce FIN) Il client spedisce nuovi dati al server dopo il reboot Il server non ha più il socket aperto TP risponde ai dati con un RT lient è in readline quando riceve RT Readline ritorna EONNREET 52 erver somma olo la funziona che gestisce il client void server_somma( sockfd) { i, arg1, arg2; ssize_t n; char sendline[maxline], rcvline[maxline]; char c; for ( ; ; ) { if ( (n = reti_readline(sockfd, rcvline, MAXLINE)) == 0) return; /* connection closed by other end */ /* legge dalla stringa passata dal client i due eri da sommare */ if( sscanf(rcvline, "%d %d", &arg1, &arg2) == 2 ) /* converte il risultato in stringa e lo scrive nel buffer */ sprf(sendline, "%d\n", arg1 + arg2); else sprf(sendline, "input error\n"); n = strlen(sendline); reti_writen(sockfd, sendline, n); 53 sommasrv.c lient somma Il codice del client somma è un pò più complesso Deve gestire due input I dati in arrivo dal socket I dati digitati dall utente alla tastiera Questo problema verrà affrontato in seguito IO multiplexing elect Il codice è disponibile sulla pagina Web sommacli.c 54 9

Problema 55 lient e server, stesso tipo di macchina sunos5 > sommacli 206.62.226.33 11 22 33 1-44 -55 lient e server, macchine di tipo diverso Una parc l altra Intel bsdi > sommacli 206.62.226.33 11 22 33 1-44 6542537 orso di laurea in Informatica Reti di alcolatori A.A. 2007-2008 Prof. Roberto De Prisco ocket UDP parc: big-endian, Intel: little-endian Lezione 3 UDP 57 Funzioni per i socket 58 TP Trasporto orientato alla connessione, affidabile Tipica erazione per il protocollo UDP socket() bind() UDP enza connessione, inaffidabile i sono situazione in cui è sensato usare UDP Esempi DN NF NMP LIENT Aspetta un datagram socket() sendto() recvfrom() close() Dati (richiesta) Dati (risposta) recvfrom() sendto() close() Aspetta un datagram ERVER pedire e ricevere datagrammi 59 erver echo con UDP 60 #include <sys/socket.h> recvfrom( sd, void* buf, nbytes, flags, struct sockaddr* from, socklen_t *len); sendto( sd, const void* buf, nbytes, flags, struct sockaddr* to, socklen_t len); const Valore di ritorno: se errore, byte letti o scritti se OK sd, buf e nbytes Il socket descriptor, i dati da scrivere o il buffer in cui leggere e la lunghezza dei dati/buffer flags = 0, per ora (vedremo a che serve con le funzioni di I/O avanzato) from o to, len pecificano la struttura che descrive il socket, il from e len verranno scritti dalla funzione e sono nulli inizialmente significa che non siamo eressati a saperli e non verranno scritti imili agli ultimi due parametri di accept main( argc, char **argv) { sockfd; struct sockaddr_in servaddr, cliaddr; if( (sockfd = socket(af_inet, OK_DGRAM, 0)) < 0 ) err_sys("socket error"); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(inaddr_any); servaddr.sin_port = htons(port); if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) err_sys("bind error"); server_echo_udp(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr)); void server_echo_udp( sockfd, struct sockaddr *p_cliaddr, socklen_t clilen) { n; socklen_t len; char mesg[maxline]; for ( ; ; ) { len = clilen; if( (n = recvfrom(sockfd, mesg, MAXLINE, 0, p_cliaddr, &len)) < 0) err_sys("recvfrom error"); if( sendto(sockfd, mesg, n, 0, p_cliaddr, len)!= n ) err_sys("sendto error"); echoudpsrv.c 10

lient echo con UDP (1) 61 lient echo con UDP (2) 62 #include "basic.h" #include "echo.h" main( argc, char **argv) { sockfd; echoudpcli.c void client_echo_udp(file *fp, sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) { n; char sendline[maxline], recvline[maxline + 1]; char buff[maxline]; socklen_t len; struct sockaddr *p_replyaddr; struct sockaddr_in servaddr; p_replyaddr = malloc(servlen); if (argc!= 2) err_quit("usage: udpclient <IPaddress>"); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(port); inet_pton(af_inet, argv[1], &servaddr.sin_addr); if( (sockfd = socket(af_inet, OK_DGRAM, 0)) < 0 ) err_sys("socket error"); client_echo_udp(stdin, sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); exit(0); while (fgets(sendline, MAXLINE, fp)!= NULL) { sendto(sockfd, sendline, strlen(sendline), 0, p_servaddr, servlen); len = servlen; if( (n = recvfrom(sockfd, recvline, MAXLINE, 0, p_replyaddr, &len)) < 0 ) err_sys("recvfrom error"); if( (len!= servlen) memcmp(p_servaddr, p_replyaddr, len)!= 0 ) { struct sockaddr_in *sin = (struct sockaddr_in *) p_replyaddr; err_msg("risposta da %s ignorata\n", n", inet_ntop(af_inet, &sin->sin_addr, buff, sizeof(buff))); continue; recvline[n] = 0; fputs(recvline, stdout); ontrollo sul mittente 63 Datagrammi perduti 64 Il client controlla che il datagram di risposta venga dal server Infatti potrebbe ricevere un qualsiasi altro datagram Tale datagram sarebbe erpratato come la risposta del server Esercizio Provare a creare una situazione del genere ul sito c è il codice di spedisce_dg.c Permette di spedire un datagram verso una porta UDP osa succede se un datagram si perde? Per esempio un router lo butta via hi lo sta aspettando (server o client) rimane bloccato in attesa Per evitare questo problema si può usare un timeout In alcuni casi non basta Non sappiamo se il messaggio del client non è mai arrivato al server oppure se la risposta del server non è arrivata al client In alcuni casi (es. transazioni bancarie) fa molta differenza onnect e UDP 65 ebbene UPD sia senza connessione è possibile chiamare la funzione connect su un socket UDP Non si crea una connessione (handshake TP) emplicemente il kernel memorizza l indirizzo IP e la porta con cui si vuole comunicare Quindi dobbiamo distinguire tra ocket UDP connesso ocket UDP non connesso ocket UDP connesso 66 Non si può specificare il destinatario: è quello specificato in connect Non si usa sendto ma write o send I pacchetti verrano automaticamente spediti all indirizzo specificato nella chiamata a connect I datagram letti sono quelli che arrivano dall indirizzo connesso Non si usa recvfrom, ma si usa read o readv iò limita un server UDP a comunicare con un solo client Errori asincroni possono essere controllati Un socket UDP non connesso non può controllare errori asincroni 11

connect 67 UDP client versione connect 68 È possibili chiamare connect più di una volta Può essere usato per cambiare l indirizzo con cui si vuol comunicare Disconnettere il socket (specificando AF_UNPE come famiglia di protocolli nel campo sin_family) Potrebbe ritornare l errore EAFNOUPPORT, ma non è un problema void client_echo_udp_conn(file *fp, sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) { n; char sendline[maxline], recvline[maxline + 1]; if( connect(sockfd, (struct sockaddr *) p_servaddr, servlen) < 0 ) err_sys("connect error"); while (fgets(sendline, MAXLINE, fp)!= NULL) { write(sockfd, sendline, strlen(sendline)); n = read(sockfd, recvline, MAXLINE); recvline[n] = 0; /* null terminate */ fputs(recvline, stdout); echoudpcli-connect.c connect.c Inaffidabilità di UDP UDP non dà alcuna garanzia sulla consegna dei datagram 69 onsideriamo la seguente applicazione client server UDP Il server riceve datagram e semplicemente li conta Può essere errotto con TRL-, c e un gestore di segnale che semplicemente stampa quanti datagram sono stati ricevuti Il client spedisce un serie di pacchetti, senza aspettare alcuna risposta UDP echo server count (1) #include "basic.h" #include "echo.h" void server_echo_udp_count( sockfd, struct sockaddr *p_cliaddr, socklen_t clilen); static void gestisci_errupt( signo); count = 0; main( argc, char **argv) { sockfd; struct sockaddr_in servaddr, cliaddr; if( (sockfd = socket(af_inet, OK_DGRAM, 0)) < 0 ) err_sys("socket error"); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(inaddr_any); servaddr.sin_port = htons(port); if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) err_sys("bind error"); signal(igint, gestisci_errupt); server_echo_udp_count(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr)); 70 echoudpcli-count.c count.c UDP echo server count (2) 71 UDP echo client count 72 void server_echo_udp_count( sockfd, struct sockaddr *pcliaddr, socklen_t clilen) { n; socklen_t len; char mesg[maxline]; n = 240 * 1024; setsockopt(sockfd, OL_OKET, O_RVBUF, &n, sizeof(n)); for ( ; ; ) { len = clilen; recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len); count++; sleep(1); /* rallentiamo il server */ static void gestisci_errupt( signo) { prf("\ndatagrams ndatagrams ricevuti: %d\n", count); exit(0); #define NDG 2000 /* #datagrams to send */ #define DGLEN 1400 /* length of each datagram */ void client_echo_udp_count(file *fp, sockfd, const struct sockaddr *pservaddr, socklen_t servlen) { i; char sendline[maxline]; for (i = 0; i < NDG; i++) { sendto(sockfd, sendline, DGLEN, 0, pservaddr, servlen); Il client è come gli altri ambia solo la funzione che spedisce i datagram echoudpcli-count.c count.c osa succede se usiamo questo client-server? 12

Errori non segnalati 73 robdep@zircone:~/orsi/reti/> netstat -s s grep -4 "Udp" tail -5; Udp: 6686 packets received 2012 packets to unknown port received. 9674 packet receive errors 18634 packets sent orso di laurea in Informatica Reti di alcolatori robdep@zircone:~/orsi/reti/> echoudpsrv-count count Datagrams ricevuti: 11 lient in un altra shell; dopo un pòtrl- A.A. 2007-2008 Prof. Roberto De Prisco robdep@zircone:~/orsi/reti/> netstat -s s grep -4 "Udp" tail -5; Udp: 7206 packets received 2012 packets to unknown port received. 11154 packet receive errors 20634 packets sent I/O Multiplexing robdep@zircone:~/orsi/reti/> echoudpcli-count count 127.0.0.1 robdep@zircone:~/orsi/reti/> Lezione 4 Problema Un programma deve gestire due input simultaneamente tandard input (leggere da tastiera) Un socket (leggere dal socket) Abbiamo visto un esempio in cui il client era bloccato a leggere da standard input Non poteva leggere il FIN sul socket Normalmente una funzione di I/O si blocca se non ci sono dati da leggere erve un modo per poter aspettare da più canali di input Il primo che produce dati viene letto 75 Modelli di I/O Vari modelli di Input/Output 1. Blocking 2. Nonblocking 3. I/O multiplexing 4. Guidato dai segnali 5. Asincrono sincroni incrono: il processo si blocca (quando chiama l operazione di lettura) fino alla conclusione dell operazione In una operazione di lettura da un canale di I/O possiamo distinguere due fasi 1. Attesa per i dati da parte del kernel 2. opia dei dati dal kernel al processo che deve usarli 76 Blocking I/O 77 Nonblocking I/O 78 applicazione kernel applicazione kernel Recvfrom ystem call Non ci sono datagram pronti Recvfrom ystem call Non ci sono datagram pronti BLOATA datagram pronto opia datagram FAE 1: attesa Recvfrom Recvfrom EWOULDBLOK EWOULDBLOK ystem call ystem call datagram pronto opia datagram FAE 1: attesa FAE 2: copia BLOATA FAE 2: copia Processo continua (elabora il datagram) Ritorna OK opia completata Processo continua (elabora il datagram) Ritorna OK opia completata 13

I/O multiplexing 79 I/O guidato dai segnali 80 applicazione kernel applicazione kernel select ystem call Non ci sono datagram pronti signal ystem call Non ci sono datagram pronti BLOATA FAE 1: attesa FAE 1: attesa Recvfrom Ritorna pronto ystem call datagram pronto opia datagram GETORE EGNALE Recvfrom EGNALE ystem call datagram pronto opia datagram BLOATA FAE 2: copia BLOATA FAE 2: copia Processo continua (elabora il datagram) Ritorna OK opia completata Processo continua (elabora il datagram) Ritorna OK opia completata I/O asincrono 81 Funzione select 82 applicazione aio_read ystem call kernel Non ci sono datagram pronti #include <sys/select.h> #include <sys/time.h> select( maxfd, fd_set readset, fd_set writeset, fd_set exceptionset, const struct timeval *timeout); datagram pronto opia datagram FAE 1: attesa Valore di ritorno: se errore, 0 se timeout, numero di descrittori pronti Permette di aspettare che uno o più file descriptor siano pronti per essere letti GETORE EGNALE Processo continua (elabora il datagram) EGNALE opia completata FAE 2: copia Il timeout è dato dalla struttura struct timeval { long tv_sec; long tv_usec; Parametri select 83 Parametri select 84 Timeout 1. Puntatore nullo: aspetta senza timeout (fino a che un descrittore è pronto) 2. truttura con un timeout non zero: aspetta fino al timeout, poi ritorna anche se non ci sono descrittori pronti Anche se possiamo specificare i microsecondi alcuni kernel arrotondano a multipli di 10 microsecondi Alcuni sistemi Linux modificano la struttura timeout (vale il tempo rimanente al momento del ritorno) 3. truttura con un timeout pari a 0: non aspettare, ritorna immediatamente (polling) File descriptor da controllare Readset: pronti per la lettura Writeset: pronti per la scrittura Exceptionset: condizioni particolari Arrivo di dati fuori banda su un socket Informazioni di controllo da uno pseudo terminale Per descriveri gli insiemi si usa la struttura fd_set che è un insieme di bit void FD_ZERO(fd_set *fdset); Azzera la struttura fdset void FD_ET( fd, fd_set *fdset); Mette a 1 il bit relativo al file descriptor fd void FD_LR( fd, fd_set *fdset); Mette a 0 il bit relativo al file descriptor fd FD_IET( fd, fd_set *fdset); ontrolla se il bit relativo al file descriptor fd è a 1 La costante FD_ETIZE (select.h) è il numero di descrittori in fd_set (solitamente 1024) maxfd: è il numero massimo di descrittori effetivamente usati Usato per efficienza dal kernel Es. se siamo eressati ai descrittori 1,4,7,9 maxfd deve valere 10 (i file descriptor iniziano da 0) 14

Descrittori pronti 85 Descrittori pronti 86 Quando un socket descriptor è pronto per essere usato? ocket in lettura Quando c è almeno un byte da leggere La soglia si può cambiare con le opzioni dei socket Il socket è stato chiuso in lettura Es. è stato ricevuto il FIN L operazione di lettura ritorna EOF Il socket è un listening socket e ci sono delle connessioni completate è un errore L operazione di lettura ritornerà e errno specificherà l errore ocket in scrittura Il numero di byte di spazio disponibile nel buffer del kernel è maggiore di 2048 La soglia si può cambiare con le opzioni dei socket L operazione di scrittura ritorna il numero di byte effettivamente passati al livello di trasporto Il socket è stato chiuso in scrittura Un operazione di scrittura genera IGPIPE è un errore L operazione di scrittura ritornerà e errno specificherà l errore Eccezioni per socket Arrivo di dati fuori banda echoclient versione select (1) 87 echoclient versione select (2) 88 void client_echo_select(file *fp, sockfd) { maxfdl; fd_set rset; char sendline[maxline], recvline[maxline]; n; FD_ZERO(&rset); for( ; ; ) { FD_ET(fileno(fp), &rset); FD_ET(sockfd, &rset); maxfdl = MAX(fileno(fp), sockfd) + 1; if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 ) err_sys("select error"); echocli-slct.c if (n == 0) err_quit( %s [%d]: server disconnesso, FILE, LINE ); fputs(recvline, stdout); if( FD_IET(fileno(fp), &rset) ) { if( fgets(sendline, MAXLINE, fp) == NULL) return; if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0) err_sys("write error"); if( FD_IET(sockfd, &rset) ) { if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) { if( errno == EPIPE ) { err_msg( %s [%d]: server disconnesso, FILE, LINE ); break; else err_sys("readline error"); Il client riesce a gestire sia l input da tastiera che l input dal socket e il server termina viene spedito un EOF sul socket Il client lo riceve e termina la connessione enza select il client se ne sarebbe accorto dopo echoclient e select 89 top-and-wait 90 ondizioni gestite da select in lettura su stdin ed un socket Data o EOF stdin socket lient Il client opera in modalità stop-and-wait: pedisce una linea di input e si blocca in attesa della risposta del server echo Tempo 0 dati Tempo 4 echo Tempo 1 dati Tempo 5 echo TP Tempo 2 dati Tempo 6 echo RT data FIN Tempo 3 dati Tempo 7 echo 15

Batch input i spediscono le richieste consecutivamente senza aspettare le risposte, che arriveranno dopo Tempo 0 d1 Tempo 1 d2 Tempo 2 d3 Tempo 3 d4 d1 d2 d3 d1 d2 d1 Tempo 4 d5 Tempo 5 d6 Tempo 6 d7 Tempo 7 d8 r1 d4 d5 d6 r1 d7 r2 d3 d4 r1 d5 r2 d6 r3 d2 r1 d3 r2 d4 r3 d5 r4 91 hutdown della connessione Quando il client finisce di spedire non può chiudere il socket i possono esser ancora dati in arrivo i deve chiudere il socket solo in scrittura e lasciarlo aperto in lettura pedire il FIN solo in una direzione #include <sys/socket.h> shutdown( sockfd, howto); 92 Valore di ritorno: se errore, 0 se OK howto = HUT_RD, HUT_WR, HUT_RDWR echoclient versione shutdown 93 echoclient versione shutdown 94 void client_echo_shutdown(file *fp, sockfd) { maxfdl, stdineof; fd_set rset; char sendline[maxline], recvline[maxline]; n; FD_ZERO(&rset); for( ; ; ) { FD_ET(fileno(fp), &rset); FD_ET(sockfd, &rset); maxfdl = MAX(fileno(fp), sockfd) + 1; if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 ) err_sys("select error"); if( FD_IET(sockfd, &rset) ) { if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) { if( errno == EPIPE ) { err_msg("%s [%d]: server disconnesso", FILE, LINE ); break; else err_sys("readline error"); echocli-shtd.c if (n == 0) { if( stdineof == 1 ) return; else { err_msg("%s [%d]: server disconnesso", FILE, LINE ); exit(); fputs(recvline, stdout); if( FD_IET(fileno(fp), &rset) ) { if( fgets(sendline, MAXLINE, fp) == NULL) { stdineof = 1; shutdown(sockfd, HUT_WR); FD_LR(fileno(fp), &rset); continue; if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0) err_sys("write error"); elect per il server Possiamo usare select anche nel server Al posto di creare un figlio per ogni connessione elect può leggere da tutti i client connessi trutture dati utilizzate Array rset, contiene file descriptor dei socket utilizzati dal server (sia listening che connessi) Array client, contiene eri che indicano fd 95 trutture dati per il server 96 Il server crea il listening socket chiamando listen upponiamo che gli standard file siano aperti e che il fd ritornato da listen sia 3 Tale informazione verrà memorizzata in rset rset fd0 fd1 fd2 fd3 fd4 fd5 0 0 0 1 0 0 client 1 2 3 rset fd0 fd1 fd2 fd3 fd4 fd5 0 0 0 0 0 0 Il server chiama select per leggere da tutti i socket (file descriptor) aperti All inizio c è solo il il listening socket, su fd 3 Quindi il parametro maxfd di select deve essere 4 FD_ETIZE 16

trutture dati per il server 97 Quando un client stabilisce una connessione verrà creato un socket per la connessione con la funzione accept upponiamo che il fd ritornato è 4 trutture dati per il server upponiamo che un altro client si connette, verrà creato un nuovo socket upponiamo che il fd ritornato è 5 98 client 1 2 3 4 rset fd0 fd1 fd2 fd3 fd4 fd5 0 0 0 1 1 0 client 1 2 3 4 5 rset fd0 fd1 fd2 fd3 fd4 fd5 0 0 0 1 1 1 FD_ETIZE FD_ETIZE Di nuovo il server chiama select per leggere da tutti i socket (file descriptor) aperti Ora ci sono due socket, su fd 3 (listening) e fd 4 (connessione) Quindi il parametro maxfd di select deve essere 5 Di nuovo il server chiama select per leggere da tutti i socket (file descriptor) aperti Ora ci sono tre socket, su fd 3 (listening), e fd 4 e fd 5 per le due connessioni Quindi il parametro maxfd di select deve essere 6 trutture dati per il server 99 Echo server versione select (1) 100 upponiamo ora che la prima connessione (quella che usa fd 4) venga chiusa client 1 2 3 FD_ETIZE 5 rset fd0 fd1 fd2 fd3 fd4 fd5 0 0 0 1 0 1 Il server chiama select per leggere da tutti i socket (file descriptor) aperti Ora ci sono due socket, su fd 3 (listening), e fd 5 (connessione) Quindi il parametro maxfd di select deve essere 6 main( argc, char **argv) { listenfd, connfd, sockfd; i, maxi, maxfd; ready, client[fd_etize]; char buff[maxline]; fd_set rset, allset; ssize_t n; struct sockaddr_in servaddr, cliaddr; socklen_t cliaddr_len; if( (listenfd = socket(af_inet, OK_TREAM, 0)) < 0) err_sys("socket error"); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(inaddr_any); servaddr.sin_port = htons(port); if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0) err_sys("bind error"); if( listen(listenfd, BAKLOG) < 0 ) err_sys("listen error"); echosrv-slct.c slct.c Echo server versione select (2) 101 Echo server versione select (3) 102 maxfd = listenfd; /* inzializza il numero di descrittori */ maxi = ; for ( i = 0; i < FD_ETIZE; i++) client[i] = ; /* inizializza l'array client a 1 */ FD_ZERO(&allset); /* inizializza a zero tutti i descrittori */ FD_ET(listenfd, &allset); /* setta il descrittore di ascolto */ for ( ; ; ) { rset = allset; /* insieme descrittori da controllare per la lettura */ if( (ready = select(maxfd+1, &rset, NULL, NULL, NULL)) < 0 ) err_sys("select error"); if( FD_IET(listenfd, &rset) ) { /* richiesta ricevuta dal listening socket */ cliaddr_len = sizeof(cliaddr); if( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddr_len)) < 0) err_sys("accept error"); for(i = 0; i < FD_ETIZE; i++) if( client[i] < 0 ) { /* cerca il primo posto libero per il nuovo il descrittore */ client[i] = connfd; break; if( i == FD_ETIZE ) err_quit("troppi client"); FD_ET(connfd, &allset); /* setta connfd per la select */ if( connfd > maxfd ) maxfd = connfd; /* aggiorna maxfd */ if( i > maxi ) maxi = i; /* aggiorna maxi */ if( --ready <= 0 ) continue; /* se non ci sono altri socket pronti riprendi da select */ for( i = 0; i <= maxi; i++ ) { /* controlla tutti i socket attivi per controllare se sono leggibili */ if( (sockfd = client[i]) < 0 ) continue; if ( FD_IET(sockfd, &rset) ) { /* se sockfd è leggibile invoca la readline */ if ( (n = reti_readline(sockfd, buff, MAXLINE)) == 0) { /* connessione chiusa dall'altro endpo */ close(sockfd); /* rimuovi sockfd dalla lista di socket che la select deve controllare */ FD_LR(sockfd, &allset); client[i] = ; /* cancella sockfd da client */ else reti_writen(sockfd, buff, n); if ( --ready <= 0 ) break; 17

Denial of ervice Attack 103 onsideriamo la seguente situazione che può accadere con il server visto poco fa Un client si connette, spedisce un solo byte (che non sia un newline) e non fa più nulla Il server chiama readline che leggerà il singolo byte ma si bloccherà nella prossima chiamata a read in attesa di un newline Il server è bloccato e nessun altro client riceverà il servizio Denial of ervice Attack Un client riesce a far i modo che il server non risponda più ad altri client Denial of ervice Attack 104 Il problema deriva dal fatto che il server si blocca in una funzione relativa ad un client mentre ci sono anche altri client Potrebbe andare bene se il server deve gestire un solo client Possibili soluzioni Usare un singolo processo per ogni client Utilizzare un timeout sulle operazioni di I/O Usare I/O non-blocking Esercizi 105 Modificare echosrv-slct in modo che sia un server somma Modificare echosrv-slct in modo che stampi l indirizzo del client quando il client si connette Il client si disconnette orso di laurea in Informatica Reti di alcolatori A.A. 2007-2008 Prof. Roberto De Prisco Opzioni per i socket Lezione 5 ocket options Ogni socket aperto ha delle proprietà che ne determinano alcuni comportamenti Possono essere cambiate: opzioni L anologo per i file sono le proprietà che vengono gestite tramite la funzione fcntl La funzione fcntl può essere usata con i socket Ogni opzione ha un valore di default Alcune opzioni sono binarie (on o off) Altre hanno un valore ( o anche strutture più complesse) 107 Livelli ono divise in vari livelli OL_OKET livello socket IPPROTO_IP livello IP IPPROTO_IPV6 livello IP per la versione 6 IPPROTO_IMPV6 livello messaggi di controllo IPPROTO_TP livello TP Ogni livello ha varie opzioni Non è detto che tutte siano supportate Il programma sockopts-check stampa i valori di default delle opzioni supportate 108 18

Livello: OL_OKET Livello: IPPROTO_IP Livello: IPPROTO_TP Opzioni O_BROADAT O_DEBUG O_DONTROUTE O_ERROR O_KEEPALIVE O_LINGER O_RVBUF O_NDBUF O_RVLOWAT O_NDLOWAT O_RVTIMEO O_NDTIMEO O_REUEADDR O_REUEPORT O_TYPE O_UELOOPBAK IP_HDRINL IP_OPTION IP_TTL TP_KEEPALIVE TP_MAXRT TP_MAXEG TP_NODELAY 109 permette il broadcast abilita le informazioni di debug salta il lookup nella tavola di routing legge l errore corrente controlla che la connessione sia attiva controlla la chiusura della connessione grandezza del buffer in ricezione grandezza buffer in spedizione soglia per il buffer in ricezione soglia per il buffer in spedizione timeout per la ricezione timeout per la spedizione permette riutilizzo indirizzi locali permette riutilizzo porte locali il tipo di socket per i socket di routing (copia i pacchetti) header incluso con i dati opzioni nell header IP Time-To-Live tempo per la ritrasmissione tempo massimo per la ritrasmissione M (Maximum egment ize) disabilita algoritmo di Nagle getsockopt e setsockopt #include <sys/socket.h> getsockopt( sd, level, name, void *value, socklen_t *len); setsockopt( sd, level, name, void *value, socklen_t len); Parametri: 110 Valore di ritorno: se errore, 0 se OK setsockopt(3,ol_oket,o_keepalive,1,sizeof()); attiva l opzione di keepalive per mantenere la connessione attiva void* flag; socklen_t* size; getsockopt(3,ol_oket,o_keepalive,*flag,*size); ritorna la grandezza di un ero in size e flag sarà 0 se l opzione è disabilitata, oppure diverso da 0 se l opzione è abilitata O_BROADAT 111 O_ERROR 112 Rende possibile la trasmissione broadcast Un applicazione che ende spedire in broadcast deve abilitare questa opzione erve a prevenire broadcast accidentali Es. un programma prende in input l indirizzo di destinazione L utente potrebbe inserire un indirizzo di broadcast Il programma dovrebbe controllare e l opzione è disabilitata il broadcast non viene fatto comunque Viene ritornato l errore EAE Quando si verifica un errore, un codice di errore viene memorizzato in una variabile chiamata so_error La notifica dell errore avviene in 2 modi: e il processo è in una chiamata a select, sia per la lettura che per la scrittura, ritorna con il descrittore pronto ad essere letto e si sta usando I/O guidato dai segnali, IGIO viene generato Il processo può leggere il valore di so_error tramite questa opzione L ero ritornato è il valore di so_error Può essere solo letta, non scritta O_KEEPALIVE 113 O_LINGER 114 Quando è abilitata per un socket TP spedisce un messaggio di controllo se non c è scambio di dati per un periodo di 2 ore erve a mantenere attiva la connessione 3 possibili casi AK di risposta: la connessione è ancora attiva RT di risposta: la connessione era stata chiusa, viene generato l errore EONNREET Non c è risposta, vengono spediti 8 ulteriori messaggi ogni 75 secondi Nessuna riposta dopo 11 minuti e 15 secondi: viene generato l errore ETIMEDOUT Messaggi IMP di errore sulla rete, viene generato l errore EHOTUNREAH pecifica il funzionamento della close per protocolli orientati alla connessione (TP) struct linger { l_onoff; /* 0=off, nonzero=on */ l_linger; /* tempo attesa in secondi */ l_onoff = 0, close ritorna immediatamente ed il kernel spedirà eventuali dati residui l_onoff 0, l_linger = 0 La connessione viene resettata, i dati residui non spediti Evita il TIME_WAIT, ma rischia la reincarnazione della connessione l_onoff 0, l_linger 0 lose non ritorna subito ma aspetta l_linger secondi affinchè i dati residui vengano spediti È importante controllare il valore di ritorno perché dopo l_linger secondi i dati vengono cancellati 19

O_RVBUF e O_NDBUF Ogni socket ha un buffer di ricezione ed uno di spedizione 115 O_RVLOWAT e O_NDLOWAT ono le soglie usate dalla funzione select 116 Il buffer di ricezione è usato per mantenere i dati nel kernel prima di passarli all applicazione on TP, lo spazio disponibile è quello pubblicizzato nella finestra di TP quindi non dovrebbe esserci overflow on UDP, eventuali pacchetti in overflow vengono cancellati Il buffer in spedizione è usato per far scriver i dati dall applicazione prima di spedirli O_RVBUF e O_NDBUF ci permettono di cambiare la grandezza dei buffer Quando in ricezione sono presenti almeno un numero di byte pari al low-watermark allora select ritorna socket pronto in lettura Per TP e UDP il default è 1 In modo simile per poter scrivere (socket pronto in scrittura) i byte disponibili nel buffer di spedizione deve essere almeno pari al low-watermark Il default è 2048 O_RVTIMEO e O_NDTIMEO i permettono di usare un timeout per la ricezione e la spedizione sul socket struct timeval { long tv_sec; /* secondi */ long tv_usec; /* microsecondi */ Il timeout in ricezione è per le funzioni Read, readv, recv, recvfrom, recvmsg 117 O_TYPE i dà il tipo di socket OK_TREAM OK_DGRAM Viene usata da un processo che eredita un socket 118 Il timeout in spedizione è per le funzioni Write, writev, send, sendto Per default il timeout è 0 Timeout disabilitato, quindi la funzione si blocca Può essere solo letta, non scritta Opzioni di IPv4 119 Opzioni di TP 120 IP_HDRINL e è settata per un raw socket di IP allora l applicazione deve costruire anche l IP header che normalmente è scritto dal kernel IP_OPTION Permette di inserire le opzioni dell IP header IP_TTL Permette di specificare il Time-To-Live (in hops) del datagram Il default è 64 TP_KEEPALIVE Permette di moficare le 2 ore di attesa usate da O_KEEPALIVE TP_MAXRT, ritrasmetti per sempre 0, usa il default di sistema n > 0, ritrasmetti per n secondi prima di abbandonare la connessione TP_MAXEG Permette di modificare il valore dell M della connessione TP_NODELAY Diabilita l algoritmo di Nagle che diminuisce i dati spediti in caso di congestione 20