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



Documenti analoghi
P3-05: Socket Options

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

Sono definiti per vari protocolli

P3-04: I/O multiplexing

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

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

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

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

Comunicazione Connectionless o Datagram

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

Funzioni bloccanti e soluzioni

Laboratorio di Reti di Calcolatori

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

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

Laboratorio di Reti di Calcolatori

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

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

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

Esercitazione [7] Client/Server con Socket

LABORATORIO di Reti di Calcolatori

Reti di Calcolatori - Laboratorio. Lezione 5. Gennaro Oliva

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

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

Reti (già Reti di Calcolatori )

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

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

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

Socket TCP. prima parte

Esercitazione [6] Client/Server con Socket

Laboratorio di Reti di Calcolatori

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

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

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

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

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

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

Introduzione ai socket

Reti di Calcolatori - Laboratorio. Lezione 7. Gennaro Oliva

LABORATORIO di Reti di Calcolatori

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

Programmazione di applicazioni di rete con socket - parte 1

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

Programmazione di Rete

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

Scrittura dei programmi applicativi di rete

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

*HVWLRQHDYDQ]DWDGHOOH6RFNHWLQ& ODSULPLWLYDVHOHFW

Laboratorio di Reti di Calcolatori

5 Esercitazione (svolta):

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

I Socket di Berkeley

Sistemi di Elaborazione. Introduzione alla Programmazione distribuita

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

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

L interfaccia socket

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

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

Socket per TCP: Fondamenti

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

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

Interazione con il DNS Conversioni di Nomi ed Indirizzi

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

Laboratorio di Programmazione in Rete

Socket per TCP: Fondamenti

Programmazione di applicazioni di rete

Corso di Reti di Calcolatori T

Laboratorio di Reti di Calcolatori

Esempi di Client e Server

Socket per TCP: Fondamenti

API Socket di Berkeley

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

Le Opzioni per i Socket

Laboratorio di Programmazione in Rete

Cenni di programmazione distribuita in C++ Mauro Piccolo

Interazione (TCP) Client-Server con le socket

Interazione (TCP) Client-Server con le socket

IPC: InterProcess Communication

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

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

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

Laboratorio di Sistemi Operativi Cognome Nome Mat.

L uso di Socket UDP. TCP vs. UDP UDP

Socket TCP. seconda parte

Scrittura dei programmi applicativi di rete

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

I Socket. Laboratorio Software M. Grotto R. Farina

(VHUFLWD]LRQLGLEDVHVXOOH6RFNHWLQ&

Sistemi Operativi Teledidattico

SISTEMI OPERATIVI. Processi in Linux. Giorgio Giacinto Sistemi Operativi

INTERNET DOMAIN SOCKETS (Cap.59)

Il sistema operativo LINUX Inter Process Communication. Sommario. popen ( ) PIPE. pipe ( ) popen ( ) Sistemi operativi Modulo II

Internetworking with TCP/IP (Douglas E. Comer) Vol. I and Vol III.

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

Opzioni per le Socket

Esercitazione Laboratorio di Sistemi Operativi Cognome Nome Mat.

IPC Inter Process Communication

Transcript:

Corso di laurea in Informatica Reti di Calcolatori A.A. 2007-2008 Prof. Roberto De Prisco Lezione 1 Obiettivo 2 Saper scrivere semplici programmi per la comunicazione su una rete di calcolatori Assunzione Sapete già programmare Ambiente di sviluppo LINUX Compilatore C Socket per la comunicazione in rete 1

Desktop grafico 3 Desktop grafico Usare solo per scaricare gli esempi Terminale testuale CTRL-ALT-F1 CTRL-ALT-F6 Linea di comando 4 Terminale testuale Line di comando Prompt robdep@zircone:~/corsi/reti/c> 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:~/corsi/reti/c> Comandi ls, cd, pwd, cp, rm, mv, cat, mkdir man <comando> 2

Shell 5 Shell È il programma che interpreta i comandi BASH È la shell standard di Linux Echo $SHELL Sezione risorse del Sito web Link a pagine su Bash e altro (es. editor vi) Sito Web del corso LSO File 6 Editor di file vi emacs Occorre imparare ad usare uno di questi due editor Basta il minimo indispensabile Manuali Una ricerca su Internet vi fornirà numerosi fonti manuale editor vi 3

Programmi in C #include <stdio.h> int main(int argc, char **argv) { union { short s; char c[sizeof(short)]; un; un.s = 0x0102; printf("cpu = %s - byte ordering: ",getenv("cpu")); if (sizeof(short) == 2) { if ( un.c[0] == 1 && un.c[1] == 2 ) printf ("big-endian endian\n"); n"); else if ( un.c[0] == 2 && un.c[1] == 1 ) printf ("little-endian endian\n"); n"); else printf("unknown\n"); n"); else printf("size of short: %d.\n",sizeof(short)); exit(0); 7 Compilazione e Makefile 8 Il sorgente C va compilato Compilare un programma consiste nel 1.trasformare il sorgente C in codice oggetto 2.unire tale codice oggetto con le librerie (link) gcc (GNU C Compiler) gcc ls1.c gcc o ls1 ls1.c gcc Lmylibpath lmylib Imyincpath O DDEBUG... Makefile make... fa tutto 4

Makefile 9 #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 Esempio 10 robdep@zircone:~/corsi/reti/c> 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 5

Librerie e include file #include <nome.h> Cerca il file da includere nelle directory di ricerca standard del compilatore /usr/include, /usr/lib/include,... 11 # include nome.h Cerca il file da includere nella cwd 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 printf) Basic.h 12 #ifndef BASIC #define BASIC #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> #include <unistd.h> #include <sys/wait.h> #include <sys/un.h> /* for Unix domain sockets */ #define MAXLINE 256 #define PORT 12345 #define BACKLOG 5 #define MAX(a, b) ((a) > (b)? (a) : (b)) #endif 6

Libreria gestioni errori 13 Definisce varie funzioni per la gestione degli errori Facciamo il link con questa libreria per usare tali funzioni Sono 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 Libreria corso reti 14 Definisce varie funzioni per la lettura e scrittura dei socket Convezione sul nome reti_nomefunzione Esempi di funzioni della libreria reti_readn Legge esattamente n byte reti_writen Scrive esattamente n byte reti_readline Legge una riga Dettagli nel file lib-corso-reti.c 7

Corso di laurea in Informatica Reti di Calcolatori A.A. 2007-2008 Prof. Roberto De Prisco Socket TCP Lezione 2 Socket 16 Letteralmente significa presa (di corrente) È l astrazione di un canale di comunicazione fra due computer connessi da una rete Sono definiti per vari protocolli Per TCP/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 8

Funzioni per i socket 17 Tipica interazione in una connessione TCP socket() bind() listen() socket() accept() CLIENT connect() write() Stabilisce una connessione Dati (richiesta) read() Aspetta una connessione SERVER read() Dati (risposta) write() close() Notificazione di fine comunicazione read() close() Sockaddr_in 18 struct in_addr { in_addr_t s_addr; /* 32-bit, network byte ordered */ struct sockaddr_in { uint8_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 { uint8_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 9

Lunghezze strutture socket 19 Funzione socket 20 #include <sys/socket.h> int socket(int family, int type, int protocol ); Valore di ritorno: -1 se errore un socket descriptor se OK Socket descriptor è come un file descriptor Sono presi dallo stesso insieme Se un intero è usato come file descriptor non può essere usato come socket descriptor e viceversa Socket e file sono visti più o meno allo stesso modo read, write, close sono le stesse funzioni dei file 10

Funzione socket int family Un intero che specifica quale famiglia di protocolli si intende usare: int type AF_INET AF_INET6 AF_LOCAL AF_ROUTE altri IPv4 IPv6 prot. locale (client e server sullo stesso host) Sockets per routing Un intero che dice il tipo di socket SOCK_STREAM SOCK_DGRAM SOCK_RAW int protocol 0, tranne che per SOCK_RAW per uno stream di dati (TCP) per datagrammi (UDP) per applicazioni dirette su IP 21 Funzione connect 22 #include <sys/socket.h> int connect(int sd, struct sockaddr *servaddr, socklen_t addrlen); Valore di ritorno: -1 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 TCP viene fatto l handshaking, in caso di errore ritorna (errno) ETIMEDOUT ECONNREFUSED EHOSTUNREACH 11

Funzione bind 23 #include <sys/socket.h> int bind(int sd, struct sockaddr*myaddr, socklen_t addrlen); Valore di ritorno: -1 se errore, 0 se OK Permette ad un server di assegnare un indirizzo per il server al socket Con TCP l indirizzo può essere indirizzo IP (deve essere una delle interfacce) porta entrambi nessuno Se la porta non è specificata (valore 0) ne viene scelta una effimera Se l indirizzo IP è quello wildcard (INADDR_ANY, 0) viene usato quello designato come IP destinazione nel SYN del client Funzione listen #include <sys/socket.h> int listen(int sd, int backlog); 24 Usata solo da un server TCP, serve a Valore di ritorno: -1 se errore, 0 se OK 1. Convertire 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 TCP fa muovere da CLOSED a LISTEN 2. Backlog specifica quante connessioni accettare e mettere in attesa per essere servite 12

Backlog 25 Server accept CODA connessioni completate (stato ESTABLISHED) apertura conn. completata CODA connessioni incomplete (stato SYN_RCVD) connect dal client SYN apertura connessione La somma degli elementi in entrambe le code non può superare il backlog Funzione accept 26 #include <sys/socket.h> int accept(int sd, struct sockaddr*cliaddr, socklen_t addrlen); Valore di ritorno: -1 se errore, socked descriptor se OK Permette ad un server di prendere la prima connessione completata dalla coda Se 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 13

Daytime server 27 #include "basic.h daytimesrv.c #include <time.h> int main(int argc, char **argv) { pid_t pid; int 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, SOCK_STREAM, 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); snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); write(connfd, buff, strlen(buff)); close(connfd); Server iterativo 28 Server iterativo, serve i client uno alla volta Quando un client è connesso il seguente client deve aspettare Accettabile per server semplici come il daytime 14

Funzione inet_pton 29 #include <sys/socket.h> int inet_pton(int af, const char* stringa, void* dest); 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 30 #include "basic.h" int main(int argc, char **argv) { int sockfd, n; char recvline[maxline + 1]; struct sockaddr_in servaddr; daytimecli.c if (argc!= 3) err_quit("usage: daytimecli <inidirrizzoip> <porta>"); if( (sockfd = socket(af_inet, SOCK_STREAM, 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]); 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 C per le stringhe */ fputs(recvline, stdout); exit(0); 15

Server ricorsivi 31 Un server ricorsivo usa una copia di se stesso per servire una richiesta pid_t int pid; listenfd, connfd; listenfd = socket(.); /* riempi la struttura sockaddr_in (es. numero di porta) */ bind(listenfd,.) listen(listenfd, LISTENQ) 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 */ Server iterativi 32 Client connect() Richiesta di connessione listensd Server Il server chiama accept() Viene creato un nuovo socket descriptor nel server per la connessione con questo particolare client Client connect() Connessione stabilita Server listensd connsd 16

Server ricorsivi 33 Client connect() Connessione stabilita listensd Server connsd padre listensd Server connsd figlio Il server chiama fork() Padre e figlio nel server condividono il socket Server ricorsivi 34 Client connect() listensd Server connsd padre Connessione stabilita listensd Server Il padre chiude il socket della connessione Può accettare nuove connessioni connsd figlio Il figlio chiude il socket per l accettazione di nuove connessioni Può gestire la connessione con il client 17

Getsockname e getpeername 35 #include <sys/socket.h> int getsockname(int sd, struct sockaddr*localaddr, socklen_t addrlen); int getpeername(int sd, struct sockaddr*remoteaddr, socklen_t addrlen); Valore di ritorno: -1 se errore, socked descriptor se OK Ritornano l indirizzo locale associato al socket L indirizzo dell altro lato della connessione associata al socket Serve 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ù interfaccie Una chiamata a bind con porta=0 assegna una porta effimera Stessa 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 Echo server (1) 36 #include #include "basic.h" "echo.h" echosrv.c int main(int argc, char **argv) { pid_t childpid; int listenfd, connfd; struct sockaddr_in servaddr, cliaddr; socklen_t cliaddr_len; if( (listenfd = socket(af_inet, SOCK_STREAM, 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, LISTENQ) < 0 ) err_sys("listen error"); 18

Echo server (2) 37 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(int 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); Echo client (1) #include "basic.h" #include "echo.h" int main(int argc, char **argv) { int sockfd, n; struct sockaddr_in servaddr; echocli.c 38 if (argc!= 2) err_quit("usage: echotcpcli <IPaddress>"); if ( (sockfd = socket(af_inet, SOCK_STREAM, 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); 19

Echo client (2) 39 void str_cli(file *fp, int 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); Echo server 40 Per semplicità facciamo girare server e client sulla stessa macchina prompt > echoserver & [1] 21130 prompt > netstat a Proto Recv-Q Q Send-Q Q Local address Foreign address (state) Tcp 0 0 *.9877 *.* LISTEN prompt > echoclient 127.0.0.1 In un altra finestra prompt > netstat a Proto Recv-Q Q Send-Q Q Local address Foreign address (state) Tcp 0 0 localhost.9877 localhost.1052 ESTABLISHED Tcp 0 0 localhost.1052 localhost.9877 ESTABLISHED Tcp 0 0 *.9877 *.* LISTEN A questo punto la connessione è stabilita 20

Echo server 41 prompt > echoclient 127.0.0.1 Ciao server Ciao server Arrivederci Arrivederci ^D prompt > Digitata al terminale Digitata al terminale Digitata al terminale Risposta del server Risposta del server prompt > netstat a a grep 9877 Tcp 0 0 localhost.1052 localhost.9877 TIME_WAIT Tcp 0 0 *.9877 *.* LISTEN 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 (2MSL) per 1. Mantenere informazioni nel caso l ultimo ACK 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 interferire con successive connessioni Echo server 42 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 ACK al FIN A questo punto il server è nello stato CLOSE_WAIT mentre il client è nello stato FIN_WAIT_2 La prima parte della chiusura di una connessione TCP è conclusa 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 Segment Lifetime) il client chiude la connessione 21

Segnale SIGCHLD 43 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 SIGCHLD 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, CPU, etc.) Ovviamente non vogliamo lasciare zombie Occorre scrivere un signal handler che chiama wait zombie Ognli client che termina lascia uno zombie <defunct> indica uno zombie robdep@zaffiro:~/corsi/reti/c> echocli 127.0.0.1 ciao ciao ^D robdep@zaffiro:~/corsi/reti/c> echocli 127.0.0.1 pippo pippo ^D robdep@zaffiro:~/corsi/reti/c> ps PID TTY TIME CMD 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 22

Signal handler 45 void sig_child(int signo) { pid_t pid; int stat; while ( pid = waitpid(-1,&stat,wnohang)) > 0) { printf( Child %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 Ciao server Ciao server ^D Child 19293 terminated accept error: interrupted system call Interruzione delle system call 46 Il segnale è stato catturato dal padre durante l esecuzione di accept Il gestore del segnale viene eseguito Poiché è stata interrotta 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 23

Una possibile soluzione 47 for ( ; ; ) { clilen = sizeof(cliaddr); if ( (connfd = accept(listenfd, &cliaddr, &clilen)) < 0) { if (errno = EINTR) continue; else { perror( accept error ); exit(1); Se la chiamata ad accept ritorna EINTR accept viene richiamata Se l errore è diverso da EINTR Si gestisce l errore (nell esempio si chiama exit) Reset connessione e accept 48 Un altro errore tipico da gestire con accept è il reset della connessione prima della chiamata ad accept La connessione diventa ESTABLISHED Il client spedisce un RST Il server chiama accept Accept ritorna un codice di errore ECONNABORTED Il server può richiamare accept per la prossima connessione 24

Terminazione del server 49 Cosa succede se il server termina prematuramente? Prompt > echoclient 127.0.0.1 Ciao Ciao 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 SIGPIPE 50 Cosa 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 RST La seconda crea il problema Viene generato un segnale di SIGPIPE Il processo termina se il segnale non viene catturato o ignorato Se SIGPIPE è ignorato l operazione di write genera l errore di EPIPE Soluzione semplice, quando non si deve reagire all errore 1. Ignorare (SIG_IGN) il segnale di SIGPIPE Assume che non occorre fare niente di speciale in tale circostanza 2. Controllare l errore di EPIPE sulle write e nel caso di errore terminare (non scrivere più) 25

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, RST) Può dipendere dalla rete O dalla macchina server Il client è bloccato in readline TCP ritrasmetterà i dati per ricevere l ACK fino ad un certo timeout La funzione di lettura dal socket ritorna un errore ETIMEOUT EHOSTUNREACH, ENETUNREACH Server shutdown and reboot 52 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 TCP risponde ai dati con un RST Client è in readline quando riceve RST Readline ritorna ECONNRESET 26

Server somma Solo la funziona che gestisce il client void server_somma(int sockfd) { int 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 interi da sommare */ if( sscanf(rcvline, "%d %d", &arg1, &arg2) == 2 ) /* converte il risultato in stringa e lo scrive nel buffer */ sprintf(sendline, "%d\n", arg1 + arg2); else sprintf(sendline, "input error\n"); n = strlen(sendline); reti_writen(sockfd, sendline, n); sommasrv.c 53 Client somma 54 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 Select Il codice è disponibile sulla pagina Web sommacli.c 27

Problema 55 Client e server, stesso tipo di macchina sunos5 > sommacli 206.62.226.33 11 22 33-11 -44-55 Client e server, macchine di tipo diverso Una Sparc l altra Intel bsdi > sommacli 206.62.226.33 11 22 33-11 -44-16542537 Sparc: big-endian, Intel: little-endian Corso di laurea in Informatica Reti di Calcolatori A.A. 2007-2008 Prof. Roberto De Prisco Socket UDP Lezione 3 28

UDP 57 TCP Trasporto orientato alla connessione, affidabile UDP Senza connessione, inaffidabile Ci sono situazione in cui è sensato usare UDP Esempi DNS NFS SNMP Funzioni per i socket 58 Tipica interazione per il protocollo UDP socket() bind() socket() recvfrom() CLIENT sendto() recvfrom() Dati (richiesta) Aspetta un datagram SERVER Aspetta un datagram Dati (risposta) sendto() close() close() 29

Spedire e ricevere datagrammi 59 #include <sys/socket.h> int recvfrom(int sd, void* buf, int nbytes, int flags, struct sockaddr* from, socklen_t *len); int sendto(int sd, const void* buf, int nbytes, int flags, struct sockaddr* to, socklen_t len); const Valore di ritorno: -1 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 Specificano la struttura che descrive il socket, il from e len verranno scritti dalla funzione Se sono nulli inizialmente significa che non siamo interessati a saperli e non verranno scritti Simili agli ultimi due parametri di accept Server echo con UDP int main(int argc, char **argv) { int sockfd; struct sockaddr_in servaddr, cliaddr; if( (sockfd = socket(af_inet, SOCK_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); echoudpsrv.c 60 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(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen) { int 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"); 30

Client echo con UDP (1) 61 #include #include "basic.h" "echo.h" echoudpcli.c int main(int argc, char **argv) { int struct sockaddr_in sockfd; servaddr; 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, SOCK_DGRAM, 0)) < 0 ) err_sys("socket error"); client_echo_udp(stdin, sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); exit(0); Client echo con UDP (2) 62 void client_echo_udp(file *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) { int n; char sendline[maxline], recvline[maxline + 1]; char buff[maxline]; socklen_t len; struct sockaddr *p_replyaddr; p_replyaddr = malloc(servlen); 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); 31

Controllo sul mittente 63 Il client controlla che il datagram di risposta venga dal server Infatti potrebbe ricevere un qualsiasi altro datagram Tale datagram sarebbe interpratato come la risposta del server Esercizio Provare a creare una situazione del genere Sul sito c è il codice di spedisce_dg.c Permette di spedire un datagram verso una porta UDP Datagrammi perduti 64 Cosa succede se un datagram si perde? Per esempio un router lo butta via Chi 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 32

Connect e UDP 65 Sebbene UPD sia senza connessione è possibile chiamare la funzione connect su un socket UDP Non si crea una connessione (handshake TCP) Semplicemente il kernel memorizza l indirizzo IP e la porta con cui si vuole comunicare Quindi dobbiamo distinguire tra Socket UDP connesso Socket UDP non connesso Socket 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 Ciò 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 33

connect 67 È possibili chiamare connect più di una volta Può essere usato per cambiare l indirizzo con cui si vuol comunicare Disconnettere il socket (specificando AF_UNSPEC come famiglia di protocolli nel campo sin_family) Potrebbe ritornare l errore EAFNOSUPPORT, ma non è un problema UDP client versione connect 68 void client_echo_udp_conn(file *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) { int 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 34

Inaffidabilità di UDP 69 UDP non dà alcuna garanzia sulla consegna dei datagram Consideriamo la seguente applicazione client server UDP Il server riceve datagram e semplicemente li conta Può essere interrotto con CTRL-C, 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 #include #include UDP echo server count (1) "basic.h" "echo.h" echoudpcli-count.c count.c 70 void server_echo_udp_count(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen); static void gestisci_interrupt(int signo); int count = 0; int main(int argc, char **argv) { int sockfd; struct sockaddr_in servaddr, cliaddr; if( (sockfd = socket(af_inet, SOCK_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(sigint, gestisci_interrupt); server_echo_udp_count(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr)); 35

UDP echo server count (2) 71 void server_echo_udp_count(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen) { int n; socklen_t len; char mesg[maxline]; n = 240 * 1024; setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)); for ( ; ; ) { len = clilen; recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len); count++; sleep(1); /* rallentiamo il server */ static void gestisci_interrupt(int signo) { printf("\ndatagrams ndatagrams ricevuti: %d\n", count); exit(0); UDP echo client count #define NDG 2000 /* #datagrams to send */ #define DGLEN 1400 /* length of each datagram */ echoudpcli-count.c count.c 72 void client_echo_udp_count(file *fp, int sockfd, const struct sockaddr *pservaddr, socklen_t servlen) { int i; char sendline[maxline]; for (i = 0; i < NDG; i++) { sendto(sockfd, sendline, DGLEN, 0, pservaddr, servlen); Il client è come gli altri Cambia solo la funzione che spedisce i datagram Cosa succede se usiamo questo client-server? 36

Errori non segnalati 73 robdep@zircone:~/corsi/reti/c> netstat -s s grep -C4 "Udp" tail -5; Udp: 6686 packets received 2012 packets to unknown port received. 9674 packet receive errors 18634 packets sent robdep@zircone:~/corsi/reti/c> echoudpsrv-count count Datagrams ricevuti: 11 Client in un altra shell; dopo un pòctrl-c robdep@zircone:~/corsi/reti/c> netstat -s s grep -C4 "Udp" tail -5; Udp: 7206 packets received 2012 packets to unknown port received. 11154 packet receive errors 20634 packets sent robdep@zircone:~/corsi/reti/c> echoudpcli-count count 127.0.0.1 robdep@zircone:~/corsi/reti/c> Corso di laurea in Informatica Reti di Calcolatori A.A. 2007-2008 Prof. Roberto De Prisco I/O Multiplexing Lezione 4 37

Problema Un programma deve gestire due input simultaneamente Standard input (leggere da tastiera) Un socket (leggere dal socket) 75 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 Serve un modo per poter aspettare da più canali di input Il primo che produce dati viene letto Modelli di I/O 76 Vari modelli di Input/Output 1. Blocking 2. Nonblocking 3. I/O multiplexing 4. Guidato dai segnali 5. Asincrono sincroni Sincrono: 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. Copia dei dati dal kernel al processo che deve usarli 38

Blocking I/O 77 applicazione kernel Recvfrom System call Non ci sono datagram pronti FASE 1: attesa BLOCCATA datagram pronto Copia datagram FASE 2: copia Processo continua (elabora il datagram) Ritorna OK Copia completata Nonblocking I/O 78 applicazione kernel Recvfrom System call Non ci sono datagram pronti Recvfrom Recvfrom EWOULDBLOCK EWOULDBLOCK System call System call datagram pronto FASE 1: attesa Copia datagram BLOCCATA FASE 2: copia Processo continua (elabora il datagram) Ritorna OK Copia completata 39

I/O multiplexing 79 applicazione select System call kernel Non ci sono datagram pronti BLOCCATA FASE 1: attesa Recvfrom Ritorna pronto System call datagram pronto Copia datagram BLOCCATA FASE 2: copia Processo continua (elabora il datagram) Ritorna OK Copia completata I/O guidato dai segnali 80 applicazione signal System call kernel Non ci sono datagram pronti FASE 1: attesa GESTORE SEGNALE Recvfrom SEGNALE System call datagram pronto Copia datagram BLOCCATA FASE 2: copia Processo continua (elabora il datagram) Ritorna OK Copia completata 40

I/O asincrono 81 applicazione aio_read System call kernel Non ci sono datagram pronti FASE 1: attesa datagram pronto Copia datagram FASE 2: copia GESTORE SEGNALE Processo continua (elabora il datagram) SEGNALE Copia completata Funzione select 82 #include <sys/select.h> #include <sys/time.h> int select(int maxfd, fd_set readset, fd_set writeset, fd_set exceptionset, const struct timeval *timeout); Valore di ritorno: -1 se errore, 0 se timeout, numero di descrittori pronti Permette di aspettare che uno o più file descriptor siano pronti per essere letti Il timeout è dato dalla struttura struct timeval { long tv_sec; long tv_usec; 41

Parametri select 83 Timeout 1. Puntatore nullo: aspetta senza timeout (fino a che un descrittore è pronto) 2. Struttura 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. Struttura 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 Parametri select 84 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_SET(int fd, fd_set *fdset); Mette a 1 il bit relativo al file descriptor fd void FD_CLR(int fd, fd_set *fdset); Mette a 0 il bit relativo al file descriptor fd int FD_ISSET(int fd, fd_set *fdset); Controlla se il bit relativo al file descriptor fd è a 1 La costante FD_SETSIZE (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 interessati ai descrittori 1,4,7,9 maxfd deve valere 10 (i file descriptor iniziano da 0) 42

Descrittori pronti 85 Quando un socket descriptor è pronto per essere usato? Socket 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 C è un errore L operazione di lettura ritornerà -1 e errno specificherà l errore Descrittori pronti 86 Socket 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 SIGPIPE C è un errore L operazione di scrittura ritornerà -1 e errno specificherà l errore Eccezioni per socket Arrivo di dati fuori banda 43

echoclient versione select (1) 87 void client_echo_select(file *fp, int sockfd) { int maxfdl; fd_set rset; char sendline[maxline], recvline[maxline]; int n; echocli-slct.c FD_ZERO(&rset); for( ; ; ) { FD_SET(fileno(fp), &rset); FD_SET(sockfd, &rset); maxfdl = MAX(fileno(fp), sockfd) + 1; if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 ) err_sys("select error"); if( FD_ISSET(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"); echoclient versione select (2) 88 if (n == 0) err_quit( %s [%d]: server disconnesso, FILE, LINE ); fputs(recvline, stdout); if( FD_ISSET(fileno(fp), &rset) ) { if( fgets(sendline, MAXLINE, fp) == NULL) return; if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0) err_sys("write error"); Il client riesce a gestire sia l input da tastiera che l input dal socket Se il server termina viene spedito un EOF sul socket Il client lo riceve e termina la connessione Senza select il client se ne sarebbe accorto dopo 44

echoclient e select 89 Condizioni gestite da select in lettura su stdin ed un socket Data o EOF stdin socket Client TCP RST data FIN Stop-and-wait 90 Il client opera in modalità stop-and-wait: Spedisce una linea di input e si blocca in attesa della risposta del server echo C Tempo 0 dati Tempo 4 echo S Tempo 1 dati Tempo 5 echo Tempo 2 dati Tempo 6 echo Tempo 3 dati S C Tempo 7 echo 45

Batch input 91 Si spediscono le richieste consecutivamente senza aspettare le risposte, che arriveranno dopo C Tempo 0 d1 C Tempo 4 d5 d4 d3 d2 r1 S S C Tempo 1 d2 d1 C Tempo 5 d6 d5 d4 r1 d3 r2 S S C Tempo 2 d3 d2 d1 C Tempo 6 d7 d6 r1 d5 r2 d4 r3 S S C Tempo 3 d4 d3 d2 d1 S C C Tempo 7 d8 r1 d7 r2 d6 r3 d5 r4 S S Shutdown della connessione 92 Quando il client finisce di spedire non può chiudere il socket Ci possono esser ancora dati in arrivo Si deve chiudere il socket solo in scrittura e lasciarlo aperto in lettura Spedire il FIN solo in una direzione #include <sys/socket.h> int shutdown(int sockfd, int howto); Valore di ritorno: -1 se errore, 0 se OK howto = SHUT_RD, SHUT_WR, SHUT_RDWR 46

echoclient versione shutdown 93 void client_echo_shutdown(file *fp, int sockfd) { int maxfdl, stdineof; fd_set rset; char sendline[maxline], recvline[maxline]; int n; echocli-shtd.c FD_ZERO(&rset); for( ; ; ) { FD_SET(fileno(fp), &rset); FD_SET(sockfd, &rset); maxfdl = MAX(fileno(fp), sockfd) + 1; if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 ) err_sys("select error"); if( FD_ISSET(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"); echoclient versione shutdown 94 if (n == 0) { if( stdineof == 1 ) return; else { err_msg("%s [%d]: server disconnesso", FILE, LINE ); exit(-1); fputs(recvline, stdout); if( FD_ISSET(fileno(fp), &rset) ) { if( fgets(sendline, MAXLINE, fp) == NULL) { stdineof = 1; shutdown(sockfd, SHUT_WR); FD_CLR(fileno(fp), &rset); continue; if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0) err_sys("write error"); 47

Select per il server Possiamo usare select anche nel server 95 Al posto di creare un figlio per ogni connessione Select può leggere da tutti i client connessi Strutture dati utilizzate Array rset, contiene file descriptor dei socket utilizzati dal server (sia listening che connessi) Array client, contiene interi che indicano fd client 1 2 3-1 -1-1 rset fd0 fd1 fd2 fd3 fd4 fd5 0 0 0 0 0 0 FD_SETSIZE-1-1 Strutture dati per il server 96 Il server crea il listening socket chiamando listen Supponiamo 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 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 48

Strutture dati per il server 97 Quando un client stabilisce una connessione verrà creato un socket per la connessione con la funzione accept Supponiamo che il fd ritornato è 4 client 1 2 3 4-1 -1 rset fd0 fd1 fd2 fd3 fd4 fd5 0 0 0 1 1 0 FD_SETSIZE-1-1 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 Strutture dati per il server Supponiamo che un altro client si connette, verrà creato un nuovo socket Supponiamo che il fd ritornato è 5 98 client 1 2 3 4 5-1 rset fd0 fd1 fd2 fd3 fd4 fd5 0 0 0 1 1 1 FD_SETSIZE-1-1 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 49

Strutture dati per il server 99 Supponiamo ora che la prima connessione (quella che usa fd 4) venga chiusa client 1 2 3-1 5-1 rset fd0 fd1 fd2 fd3 fd4 fd5 0 0 0 1 0 1 FD_SETSIZE-1-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 Echo server versione select (1) 100 int main(int argc, char **argv) { int listenfd, connfd, sockfd; int i, maxi, maxfd; int ready, client[fd_setsize]; char buff[maxline]; fd_set rset, allset; ssize_t n; struct sockaddr_in servaddr, cliaddr; socklen_t cliaddr_len; echosrv-slct.c slct.c if( (listenfd = socket(af_inet, SOCK_STREAM, 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, BACKLOG) < 0 ) err_sys("listen error"); 50

Echo server versione select (2) 101 maxfd = listenfd; /* inzializza il numero di descrittori */ maxi = -1; for ( i = 0; i < FD_SETSIZE; i++) client[i] = -1; /* inizializza l'array client a -1 1 */ FD_ZERO(&allset); /* inizializza a zero tutti i descrittori */ FD_SET(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_ISSET(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_SETSIZE; i++) if( client[i] < 0 ) { /* cerca il primo posto libero per il nuovo il descrittore */ client[i] = connfd; break; if( i == FD_SETSIZE ) err_quit("troppi client"); FD_SET(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 */ Echo server versione select (3) 102 for( i = 0; i <= maxi; i++ ) { /* controlla tutti i socket attivi per controllare se sono leggibili */ if( (sockfd = client[i]) < 0 ) continue; if ( FD_ISSET(sockfd, &rset) ) { /* se sockfd è leggibile invoca la readline */ if ( (n = reti_readline(sockfd, buff, MAXLINE)) == 0) { /* connessione chiusa dall'altro endpoint */ close(sockfd); /* rimuovi sockfd dalla lista di socket che la select deve controllare */ FD_CLR(sockfd, &allset); client[i] = -1; /* cancella sockfd da client */ else reti_writen(sockfd, buff, n); if ( --ready <= 0 ) break; 51

Denial of Service Attack 103 Consideriamo 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 Service Attack Un client riesce a far i modo che il server non risponda più ad altri client Denial of Service 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 52

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 Corso di laurea in Informatica Reti di Calcolatori A.A. 2007-2008 Prof. Roberto De Prisco Opzioni per i socket Lezione 5 53

Socket options 107 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 (int o anche strutture più complesse) Livelli 108 Sono divise in vari livelli SOL_SOCKET livello socket IPPROTO_IP livello IP IPPROTO_IPV6 livello IP per la versione 6 IPPROTO_ICMPV6 livello messaggi di controllo IPPROTO_TCP livello TCP Ogni livello ha varie opzioni Non è detto che tutte siano supportate Il programma sockopts-check stampa i valori di default delle opzioni supportate 54

Livello: SOL_SOCKET Livello: IPPROTO_IP Livello: IPPROTO_TCP Opzioni SO_BROADCAST SO_DEBUG SO_DONTROUTE SO_ERROR SO_KEEPALIVE SO_LINGER SO_RCVBUF SO_SNDBUF SO_RCVLOWAT SO_SNDLOWAT SO_RCVTIMEO SO_SNDTIMEO SO_REUSEADDR SO_REUSEPORT SO_TYPE SO_USELOOPBACK IP_HDRINCL IP_OPTIONS IP_TTL TCP_KEEPALIVE TCP_MAXRT TCP_MAXSEG TCP_NODELAY 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 MSS (Maximum Segment Size) disabilita algoritmo di Nagle 109 getsockopt e setsockopt 110 #include <sys/socket.h> int getsockopt(int sd, int level, int name, void *value, socklen_t *len); int setsockopt(int sd, int level, int name, void *value, socklen_t len); Parametri: Valore di ritorno: -1 se errore, 0 se OK setsockopt(3,sol_socket,so_keepalive,1,sizeof(int)); attiva l opzione di keepalive per mantenere la connessione attiva void* flag; socklen_t* size; getsockopt(3,sol_socket,so_keepalive,*flag,*size); ritorna la grandezza di un intero in size e flag sarà 0 se l opzione è disabilitata, oppure diverso da 0 se l opzione è abilitata 55

SO_BROADCAST 111 Rende possibile la trasmissione broadcast Un applicazione che intende spedire in broadcast deve abilitare questa opzione Serve 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 Se l opzione è disabilitata il broadcast non viene fatto comunque Viene ritornato l errore EACCES SO_ERROR 112 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: Se il processo è in una chiamata a select, sia per la lettura che per la scrittura, ritorna con il descrittore pronto ad essere letto Se si sta usando I/O guidato dai segnali, SIGIO viene generato Il processo può leggere il valore di so_error tramite questa opzione L intero ritornato è il valore di so_error Può essere solo letta, non scritta 56

SO_KEEPALIVE 113 Quando è abilitata per un socket TCP spedisce un messaggio di controllo se non c è scambio di dati per un periodo di 2 ore Serve a mantenere attiva la connessione 3 possibili casi ACK di risposta: la connessione è ancora attiva RST di risposta: la connessione era stata chiusa, viene generato l errore ECONNRESET 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 ICMP di errore sulla rete, viene generato l errore EHOSTUNREACH SO_LINGER 114 Specifica il funzionamento della close per protocolli orientati alla connessione (TCP) struct linger { int l_onoff; /* 0=off, nonzero=on */ int 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 Close 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 57

SO_RCVBUF e SO_SNDBUF Ogni socket ha un buffer di ricezione ed uno di spedizione 115 Il buffer di ricezione è usato per mantenere i dati nel kernel prima di passarli all applicazione Con TCP, lo spazio disponibile è quello pubblicizzato nella finestra di TCP quindi non dovrebbe esserci overflow Con UDP, eventuali pacchetti in overflow vengono cancellati Il buffer in spedizione è usato per far scriver i dati dall applicazione prima di spedirli SO_RCVBUF e SO_SNDBUF ci permettono di cambiare la grandezza dei buffer SO_RCVLOWAT e SO_SNDLOWAT 116 Sono le soglie usate dalla funzione select Quando in ricezione sono presenti almeno un numero di byte pari al low-watermark allora select ritorna socket pronto in lettura Per TCP 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 58