+ All Categories
Home > Documents > Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di...

Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di...

Date post: 21-Feb-2019
Category:
Upload: hadang
View: 228 times
Download: 0 times
Share this document with a friend
21
1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1 1 1 LABORATORIO LABORATORIO LABORATORIO LABORATORIO Autunno 2007 Autunno 2007 Autunno 2007 Autunno 2007 2 2 2 Obiettivo 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 LABORATORIO LABORATORIO LABORATORIO LABORATORIO Autunno 2007 Autunno 2007 Autunno 2007 Autunno 2007 3 3 3 Desktop grafico … Desktop grafico Usare solo per scaricare gli esempi Terminale testuale CTRL-ALT-F1 … CTRL-ALT-F6 LABORATORIO LABORATORIO LABORATORIO LABORATORIO Autunno 2007 Autunno 2007 Autunno 2007 Autunno 2007 4 4 4 Linea di comando Terminale testuale Line di comando Prompt Comandi ls, cd, pwd, cp, rm, mv, cat, mkdir man <comando> 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> LABORATORIO LABORATORIO LABORATORIO LABORATORIO Autunno 2007 Autunno 2007 Autunno 2007 Autunno 2007 5 5 5 Shell 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 LABORATORIO LABORATORIO LABORATORIO LABORATORIO Autunno 2007 Autunno 2007 Autunno 2007 Autunno 2007 6 6 6 File 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”
Transcript
Page 1: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

1

Prof. Roberto De Prisco

Reti di Calcolatori

Corso di laurea in Informatica

A.A. 2007-2008

Lezione

LABORATORIO

1111

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

2222Obiettivo

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

3333Desktop grafico …

Desktop grafico

Usare solo per scaricare gli esempi

Terminale testuale

CTRL-ALT-F1 …

CTRL-ALT-F6

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

4444Linea di comando

� Terminale testuale� Line di comando� Prompt

� Comandi� ls, cd, pwd, cp, rm, mv, cat, mkdir� man <comando>

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>

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

5555Shell

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

6666File

� 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”

Page 2: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

2

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

7777Programmi in C

#include <stdio.h>#include <stdio.h>#include <stdio.h>#include <stdio.h>int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {

union {union {union {union {short s;short s;short s;short s;char c[sizeof(short)];char c[sizeof(short)];char c[sizeof(short)];char c[sizeof(short)];

} un;} un;} un;} un;un.s = 0x0102;un.s = 0x0102;un.s = 0x0102;un.s = 0x0102;printf("CPU = %s printf("CPU = %s printf("CPU = %s printf("CPU = %s ---- byte ordering: ",getenv("CPU"));byte ordering: ",getenv("CPU"));byte ordering: ",getenv("CPU"));byte ordering: ",getenv("CPU"));if (sizeof(short) == 2) {if (sizeof(short) == 2) {if (sizeof(short) == 2) {if (sizeof(short) == 2) {

if ( un.c[0] == 1 && un.c[1] == 2 ) if ( un.c[0] == 1 && un.c[1] == 2 ) if ( un.c[0] == 1 && un.c[1] == 2 ) if ( un.c[0] == 1 && un.c[1] == 2 ) printf ("bigprintf ("bigprintf ("bigprintf ("big----endianendianendianendian\\\\n");n");n");n");

else if ( un.c[0] == 2 && un.c[1] == 1 ) else if ( un.c[0] == 2 && un.c[1] == 1 ) else if ( un.c[0] == 2 && un.c[1] == 1 ) else if ( un.c[0] == 2 && un.c[1] == 1 ) printf ("littleprintf ("littleprintf ("littleprintf ("little----endianendianendianendian\\\\n");n");n");n");

elseelseelseelseprintf("unknownprintf("unknownprintf("unknownprintf("unknown\\\\n");n");n");n");

}}}}elseelseelseelse

printf("size of short: %d.printf("size of short: %d.printf("size of short: %d.printf("size of short: %d.\\\\n",sizeof(short));n",sizeof(short));n",sizeof(short));n",sizeof(short));exit(0);exit(0);exit(0);exit(0);

}}}}

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

8888Compilazione e Makefile

� Il sorgente C va compilato

� Compilare un programma consiste nel1.trasformare il sorgente C in codice oggetto2.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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

9999Makefile

#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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

10101010Esempio

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 lib-

errori.o lib-corso-reti.o

compiling echosrv.c with rule 1

gcc echosrv.c -g -O0 -Werror -o echosrv lib-errori.o lib-

corso-reti.o

……

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

11111111Librerie e include file

� #include <nome.h>� Cerca il file da includere nelle directory di ricerca

standard del compilatore� /usr/include, /usr/lib/include, ...

� # 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)

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

12121212Basic.h

#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

Page 3: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

3

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

13131313Libreria gestioni errori

� 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 stampa solo l’errore� err_quit, err_sys chiamano exit

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

14141414Libreria corso reti

� 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

Prof. Roberto De Prisco

Reti di Calcolatori

Corso di laurea in Informatica

A.A. 2007-2008

Lezione

Socket TCP

2222

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

16161616Socket

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

17171717Funzioni per i socket

socket()

bind()

listen()

accept()socket()

connect()

write()

read()

read()

write()

read()

close()

close()

Aspetta una connessione

Stabilisce una connessione

Dati (richiesta)

Notificazione di fine comunicazione

Dati (risposta)

CLIENT

CLIENT

CLIENT

CLIENT

SERVER

SERVER

SERVER

SERVER

Tipica interazione in una connessione TCP

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

18181818Sockaddr_in

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

Page 4: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

4

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

19191919Lunghezze strutture socket LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

20202020Funzione socket

#include <sys/socket.h>int socket(int family, int type, int protocol );

Valore di ritorno: -1 se erroreun 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

21212121Funzione socket

� int family� Un intero che specifica quale famiglia di protocolli si

intende usare:� AF_INET IPv4� AF_INET6 IPv6� AF_LOCAL prot. locale (client e server sullo stesso host)� AF_ROUTE Sockets per routing� altri …

� int type� Un intero che dice il tipo di socket

� SOCK_STREAM per uno stream di dati (TCP)� SOCK_DGRAM per datagrammi (UDP)� SOCK_RAW per applicazioni dirette su IP

� int protocol� 0, tranne che per SOCK_RAW

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

22222222Funzione connect

#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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

23232323Funzione bind#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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

24242424Funzione listen

#include <sys/socket.h>int listen(int sd, int backlog);

Valore di ritorno: -1 se errore, 0 se OK

� Usata solo da un server TCP, serve a

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

Page 5: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

5

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

25252525Backlog

La somma degli elementi in entrambe le code non può superare il backlog

ServerServerServerServer acceptacceptacceptaccept

connect dal clientconnect dal clientconnect dal clientconnect dal client

SYN apertura connessione

apertura conn. completata

CODA connessioni completate

(stato ESTABLISHED)

CODA connessioni incomplete

(stato SYN_RCVD)

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

26262626Funzione accept

#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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

27272727Daytime server

#include "basic.h“#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);

}}

daytimesrv.c

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

28282828Server iterativo

� Server iterativo, serve i client uno alla volta

� Quando un client è connesso il seguente client deve aspettare

� Accettabile per server semplici come il daytime

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

29292929Funzione inet_pton

#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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

30303030Daytime client

#include "basic.h"int main(int argc, char **argv) {

int sockfd, n;char recvline[MAXLINE + 1];struct sockaddr_in servaddr;

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);

}

daytimecli.cdaytimecli.cdaytimecli.cdaytimecli.c

Page 6: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

6

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

31313131Server ricorsivi

� Un server ricorsivo usa una copia di se stesso per servire una richiesta

pid_t pid;int 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 */

}

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

32323232Server iterativi

connect()connect()connect()connect()

listensdlistensdlistensdlistensd

Richiesta di connessioneRichiesta di connessioneRichiesta di connessioneRichiesta di connessione

connect()connect()connect()connect()

listensdlistensdlistensdlistensd

Connessione stabilitaConnessione stabilitaConnessione stabilitaConnessione stabilitaconnsdconnsdconnsdconnsd

� Il server chiama accept()

� Viene creato un nuovo socket descriptor nel server per la connessione con questo particolare client

ClientClientClientClient ServerServerServerServer

ClientClientClientClient ServerServerServerServer

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

33333333Server ricorsivi

connect()connect()connect()connect()

listensdlistensdlistensdlistensd

Connessione stabilitaConnessione stabilitaConnessione stabilitaConnessione stabilita

connsdconnsdconnsdconnsd

� Il server chiama fork()

� Padre e figlio nel server condividono il socket

ClientClientClientClient ServerServerServerServer

listensdlistensdlistensdlistensd

connsdconnsdconnsdconnsd

ServerServerServerServer

padrepadrepadrepadre

figliofigliofigliofiglio

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

34343434Server ricorsivi

connect()connect()connect()connect()

listensdlistensdlistensdlistensd

Connessione stabilitaConnessione stabilitaConnessione stabilitaConnessione stabilita

connsdconnsdconnsdconnsd

� Il padre chiude il socket della connessione� Può accettare nuove connessioni

� Il figlio chiude il socket per l’accettazione di nuove connessioni� Può gestire la connessione con il client

ClientClientClientClient ServerServerServerServer

listensdlistensdlistensdlistensd

connsdconnsdconnsdconnsd

ServerServerServerServer

padrepadrepadrepadre

figliofigliofigliofiglio

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

35353535Getsockname e getpeername#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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

36363636Echo server (1)

#include "basic.h"#include "echo.h"

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");

echosrv.cechosrv.cechosrv.cechosrv.c

Page 7: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

7

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

37373737Echo server (2)

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);}

}

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

38383838Echo client (1)

#include "basic.h"#include "echo.h"int main(int argc, char **argv) {

int sockfd, n;struct sockaddr_in servaddr;

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);

}

echocli.cechocli.cechocli.cechocli.c

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

39393939Echo client (2)

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);

}}

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

40404040Echo server

prompt > echoserver &prompt > echoserver &prompt > echoserver &prompt > echoserver &

[1] 21130 [1] 21130 [1] 21130 [1] 21130

prompt > netstat prompt > netstat prompt > netstat prompt > netstat ––––aaaa

Proto RecvProto RecvProto RecvProto Recv----Q SendQ SendQ SendQ Send----Q Local address Foreign address (state)Q Local address Foreign address (state)Q Local address Foreign address (state)Q Local address Foreign address (state)

TcpTcpTcpTcp 0000 0 *.98770 *.98770 *.98770 *.9877 *.**.**.**.*

LISTENLISTENLISTENLISTEN

prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1

� Per semplicità facciamo girare server e client sulla stessa macchina

� A questo punto la connessione è stabilita

prompt > netstat prompt > netstat prompt > netstat prompt > netstat ––––aaaa

Proto RecvProto RecvProto RecvProto Recv----Q SendQ SendQ SendQ Send----Q Local address Foreign addressQ Local address Foreign addressQ Local address Foreign addressQ Local address Foreign address (state)(state)(state)(state)

TcpTcpTcpTcp 0000 0 localhost.9877 localhost.1052 0 localhost.9877 localhost.1052 0 localhost.9877 localhost.1052 0 localhost.9877 localhost.1052 ESTABLISHEDESTABLISHEDESTABLISHEDESTABLISHED

TcpTcpTcpTcp 0000 0 localhost.1052 localhost.9877 0 localhost.1052 localhost.9877 0 localhost.1052 localhost.9877 0 localhost.1052 localhost.9877 ESTABLISHEDESTABLISHEDESTABLISHEDESTABLISHED

TcpTcpTcpTcp 0000 0 *.98770 *.98770 *.98770 *.9877 *.**.**.**.* LISTENLISTENLISTENLISTEN

� In un’altra finestra

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

41414141Echo server

� 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) per1. 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

prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1

Ciao serverCiao serverCiao serverCiao server

Ciao serverCiao serverCiao serverCiao server

ArrivederciArrivederciArrivederciArrivederci

ArrivederciArrivederciArrivederciArrivederci

^D^D^D^D

prompt >prompt >prompt >prompt >

prompt > netstat prompt > netstat prompt > netstat prompt > netstat ––––a | grep 9877a | grep 9877a | grep 9877a | grep 9877

TcpTcpTcpTcp 0000 0 localhost.1052 localhost.9877 0 localhost.1052 localhost.9877 0 localhost.1052 localhost.9877 0 localhost.1052 localhost.9877 TIME_WAITTIME_WAITTIME_WAITTIME_WAIT

TcpTcpTcpTcp 0000 0 *.98770 *.98770 *.98770 *.9877 *.**.**.**.* LISTENLISTENLISTENLISTEN

Digitata al terminaleDigitata al terminaleDigitata al terminaleDigitata al terminaleRisposta del serverRisposta del serverRisposta del serverRisposta del server

Digitata al terminaleDigitata al terminaleDigitata al terminaleDigitata al terminale

Risposta del serverRisposta del serverRisposta del serverRisposta del server

Digitata al terminaleDigitata al terminaleDigitata al terminaleDigitata al terminale

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

42424242Echo server

� 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

Page 8: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

8

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

43434343Segnale SIGCHLD

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

44444444zombie

robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1

ciaociaociaociao

ciao ciao ciao ciao

^D^D^D^D

robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1

pippopippopippopippo

pippopippopippopippo

^D^D^D^D

robdep@zaffiro:~/Corsi/Reti/C> psrobdep@zaffiro:~/Corsi/Reti/C> psrobdep@zaffiro:~/Corsi/Reti/C> psrobdep@zaffiro:~/Corsi/Reti/C> ps

PID TTY PID TTY PID TTY PID TTY TIME CMDTIME CMDTIME CMDTIME CMD

1077 pts/0 1077 pts/0 1077 pts/0 1077 pts/0 00:00:00 cat00:00:00 cat00:00:00 cat00:00:00 cat

22084 pts/2 22084 pts/2 22084 pts/2 22084 pts/2 00:00:00 bash00:00:00 bash00:00:00 bash00:00:00 bash

27162 pts/3 27162 pts/3 27162 pts/3 27162 pts/3 00:00:00 ssh00:00:00 ssh00:00:00 ssh00:00:00 ssh

30007 pts/6 30007 pts/6 30007 pts/6 30007 pts/6 00:00:00 bash00:00:00 bash00:00:00 bash00:00:00 bash

30331 pts/11 30331 pts/11 30331 pts/11 30331 pts/11 00:00:00 bash00:00:00 bash00:00:00 bash00:00:00 bash

30761 pts/11 30761 pts/11 30761 pts/11 30761 pts/11 00:00:00 echosrv00:00:00 echosrv00:00:00 echosrv00:00:00 echosrv

30765 pts/11 30765 pts/11 30765 pts/11 30765 pts/11 00:00:00 echosrv <defunct>00:00:00 echosrv <defunct>00:00:00 echosrv <defunct>00:00:00 echosrv <defunct>

30767 pts/11 30767 pts/11 30767 pts/11 30767 pts/11 00:00:00 echosrv <defunct>00:00:00 echosrv <defunct>00:00:00 echosrv <defunct>00:00:00 echosrv <defunct>

30768 pts/6 30768 pts/6 30768 pts/6 30768 pts/6 00:00:00 ps00:00:00 ps00:00:00 ps00:00:00 ps

Il client viene uccisoIl client viene uccisoIl client viene uccisoIl client viene ucciso

Il client viene uccisoIl client viene uccisoIl client viene uccisoIl client viene ucciso

� Ognli client che termina lascia uno zombie� <defunct> indica uno zombie

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

45454545Signal handler

Prompt > echoserver &Prompt > echoserver &Prompt > echoserver &Prompt > echoserver &

[2] 19287[2] 19287[2] 19287[2] 19287

prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1

Ciao serverCiao serverCiao serverCiao server

Ciao serverCiao serverCiao serverCiao server

^D^D^D^D

Child 19293 terminatedChild 19293 terminatedChild 19293 terminatedChild 19293 terminated

accept error: interrupted system callaccept error: interrupted system callaccept error: interrupted system callaccept error: interrupted system call

void sig_child(int signo) {void sig_child(int signo) {void sig_child(int signo) {void sig_child(int signo) {

pid_t pid;pid_t pid;pid_t pid;pid_t pid;

int stat;int stat;int stat;int stat;

while ( pid = waitpid(while ( pid = waitpid(while ( pid = waitpid(while ( pid = waitpid(----1,&stat,WNOHANG)) > 0) {1,&stat,WNOHANG)) > 0) {1,&stat,WNOHANG)) > 0) {1,&stat,WNOHANG)) > 0) {

printf(“Child %d terminatedprintf(“Child %d terminatedprintf(“Child %d terminatedprintf(“Child %d terminated\\\\n”,pid);n”,pid);n”,pid);n”,pid);

}}}}

}}}}

� Utilizzando il gestore di segnali si evitano i processi zombie� Appena il figlio finisce viene chiamata waitpid

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

46464646Interruzione delle system call

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

47474747Una possibile soluzione

for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {

clilen = sizeof(cliaddr);clilen = sizeof(cliaddr);clilen = sizeof(cliaddr);clilen = sizeof(cliaddr);

if ( (connfd = accept(listenfd, &cliaddr, &clilen)) < 0) {if ( (connfd = accept(listenfd, &cliaddr, &clilen)) < 0) {if ( (connfd = accept(listenfd, &cliaddr, &clilen)) < 0) {if ( (connfd = accept(listenfd, &cliaddr, &clilen)) < 0) {

if (errno = EINTR)if (errno = EINTR)if (errno = EINTR)if (errno = EINTR)

continue;continue;continue;continue;

else {else {else {else {

perror(“accept error”);perror(“accept error”);perror(“accept error”);perror(“accept error”);

exit(1);exit(1);exit(1);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)

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

48484848Reset connessione e accept

� 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

Page 9: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

9

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

49494949Terminazione del server

� Cosa succede se il server termina prematuramente?

� 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

Prompt > echoclient 127.0.0.1Prompt > echoclient 127.0.0.1Prompt > echoclient 127.0.0.1Prompt > echoclient 127.0.0.1

CiaoCiaoCiaoCiao

CiaoCiaoCiaoCiao

ArrivederciArrivederciArrivederciArrivederci

Il server non risponde (dipende dal codice)Il server non risponde (dipende dal codice)Il server non risponde (dipende dal codice)Il server non risponde (dipende dal codice)

Il server viene uccisoIl server viene uccisoIl server viene uccisoIl server viene ucciso

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

50505050SIGPIPE

� 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’errore1. Ignorare (SIG_IGN) il segnale di SIGPIPE

� Assume che non occorre fare niente di speciale in tale circostanza2. Controllare l’errore di EPIPE sulle write e nel caso di errore

terminare (non scrivere più)

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

51515151Macchina server non raggiungibile

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

52525252Server 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� TCP risponde ai dati con un RST

� Client è in readline quando riceve RST� Readline ritorna ECONNRESET

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

53535353Server “somma”

� Solo la funziona che gestisce il clientvoid server_somma(int sockfd) {void server_somma(int sockfd) {void server_somma(int sockfd) {void server_somma(int sockfd) {

int i, arg1, arg2;int i, arg1, arg2;int i, arg1, arg2;int i, arg1, arg2;

ssize_t n;ssize_t n;ssize_t n;ssize_t n;

char sendline[MAXLINE], rcvline[MAXLINE];char sendline[MAXLINE], rcvline[MAXLINE];char sendline[MAXLINE], rcvline[MAXLINE];char sendline[MAXLINE], rcvline[MAXLINE];

char c;char c;char c;char c;

for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {

if ( (n = reti_readline(sockfd, rcvline, MAXLINE)) == 0)if ( (n = reti_readline(sockfd, rcvline, MAXLINE)) == 0)if ( (n = reti_readline(sockfd, rcvline, MAXLINE)) == 0)if ( (n = reti_readline(sockfd, rcvline, MAXLINE)) == 0)

return; /* connection closed by other end */return; /* connection closed by other end */return; /* connection closed by other end */return; /* connection closed by other end */

/* legge dalla stringa passata dal client i due interi /* legge dalla stringa passata dal client i due interi /* legge dalla stringa passata dal client i due interi /* legge dalla stringa passata dal client i due interi

da sommare */da sommare */da sommare */da sommare */

if( sscanf(rcvline, "%d %d", &arg1, &arg2) == 2 ) if( sscanf(rcvline, "%d %d", &arg1, &arg2) == 2 ) if( sscanf(rcvline, "%d %d", &arg1, &arg2) == 2 ) if( sscanf(rcvline, "%d %d", &arg1, &arg2) == 2 )

/* converte il risultato in stringa e lo scrive nel /* converte il risultato in stringa e lo scrive nel /* converte il risultato in stringa e lo scrive nel /* converte il risultato in stringa e lo scrive nel

buffer */buffer */buffer */buffer */

sprintf(sendline, "%dsprintf(sendline, "%dsprintf(sendline, "%dsprintf(sendline, "%d\\\\n", arg1 + arg2);n", arg1 + arg2);n", arg1 + arg2);n", arg1 + arg2);

else else else else

sprintf(sendline, "input errorsprintf(sendline, "input errorsprintf(sendline, "input errorsprintf(sendline, "input error\\\\n");n");n");n");

n = strlen(sendline);n = strlen(sendline);n = strlen(sendline);n = strlen(sendline);

reti_writen(sockfd, sendline, n);reti_writen(sockfd, sendline, n);reti_writen(sockfd, sendline, n);reti_writen(sockfd, sendline, n);

}}}}

}}}}

sommasrv.csommasrv.csommasrv.csommasrv.c

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

54545454Client “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� Select

� Il codice è disponibile sulla pagina Web� sommacli.c

Page 10: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

10

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

55555555Problema

sunos5 > sommacli 206.62.226.33sunos5 > sommacli 206.62.226.33sunos5 > sommacli 206.62.226.33sunos5 > sommacli 206.62.226.33

11 2211 2211 2211 22

33333333

----11 11 11 11 ----44444444

----55555555

bsdi > sommacli 206.62.226.33bsdi > sommacli 206.62.226.33bsdi > sommacli 206.62.226.33bsdi > sommacli 206.62.226.33

11 2211 2211 2211 22

33333333

----11 11 11 11 ----44444444

----16542537165425371654253716542537

� Client e server, stesso tipo di macchina

� Client e server, macchine di tipo diverso� Una Sparc l’altra Intel

� Sparc: big-endian, Intel: little-endian

Prof. Roberto De Prisco

Reti di Calcolatori

Corso di laurea in Informatica

A.A. 2007-2008

Lezione

Socket UDP

3333

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

57575757UDP

� TCP� Trasporto orientato alla connessione, affidabile

� UDP� Senza connessione, inaffidabile

� Ci sono situazione in cui è sensato usare UDP

� Esempi� DNS� NFS� SNMP

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

58585858Funzioni per i socket

socket()socket()socket()socket()

bind()bind()bind()bind()

recvfrom()recvfrom()recvfrom()recvfrom()socket()socket()socket()socket()

sendto()sendto()sendto()sendto()

recvfrom()recvfrom()recvfrom()recvfrom()

sendto()sendto()sendto()sendto()

close()close()close()close()close()close()close()close()

Aspetta un datagram

Dati (richiesta)

Dati (risposta)

CLIENT

CLIENT

CLIENT

CLIENT

SERVER

SERVER

SERVER

SERVER

Tipica interazione per il protocollo UDP

Aspetta un datagram

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

59595959Spedire e ricevere datagrammi

#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, const

struct sockaddr* to, socklen_t len);

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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

60606060Server echo con UDP

int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {int sockfd;int sockfd;int sockfd;int sockfd;struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )

err_sys("socket error");err_sys("socket error");err_sys("socket error");err_sys("socket error");bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);

if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )err_sys("bind error");err_sys("bind error");err_sys("bind error");err_sys("bind error");

server_echo_udp(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));server_echo_udp(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));server_echo_udp(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));server_echo_udp(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));}}}}

void server_echo_udp(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen) {void server_echo_udp(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen) {void server_echo_udp(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen) {void server_echo_udp(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen) {int int int int n;n;n;n;socklen_t socklen_t socklen_t socklen_t len;len;len;len;char char char char mesg[MAXLINE];mesg[MAXLINE];mesg[MAXLINE];mesg[MAXLINE];for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {

len = clilen;len = clilen;len = clilen;len = clilen;if( (n = recvfrom(sockfd, mesg, MAXLINE, 0, p_cliaddr, &len)) < 0)if( (n = recvfrom(sockfd, mesg, MAXLINE, 0, p_cliaddr, &len)) < 0)if( (n = recvfrom(sockfd, mesg, MAXLINE, 0, p_cliaddr, &len)) < 0)if( (n = recvfrom(sockfd, mesg, MAXLINE, 0, p_cliaddr, &len)) < 0)

err_sys("recvfrom error");err_sys("recvfrom error");err_sys("recvfrom error");err_sys("recvfrom error");if( sendto(sockfd, mesg, n, 0, p_cliaddr, len) != n )if( sendto(sockfd, mesg, n, 0, p_cliaddr, len) != n )if( sendto(sockfd, mesg, n, 0, p_cliaddr, len) != n )if( sendto(sockfd, mesg, n, 0, p_cliaddr, len) != n )

err_sys("sendto error");err_sys("sendto error");err_sys("sendto error");err_sys("sendto error");}}}}

}}}}

echoudpsrv.cechoudpsrv.cechoudpsrv.cechoudpsrv.c

Page 11: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

11

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

61616161Client echo con UDP (1)

#include "basic.h"#include "basic.h"#include "basic.h"#include "basic.h"#include "echo.h"#include "echo.h"#include "echo.h"#include "echo.h"

int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {int sockfd;int sockfd;int sockfd;int sockfd;

struct sockaddr_in servaddr;struct sockaddr_in servaddr;struct sockaddr_in servaddr;struct sockaddr_in servaddr;

if (argc != 2)if (argc != 2)if (argc != 2)if (argc != 2)err_quit("usage: udpclient <IPaddress>");err_quit("usage: udpclient <IPaddress>");err_quit("usage: udpclient <IPaddress>");err_quit("usage: udpclient <IPaddress>");

bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);inet_pton(AF_INET, argv[1], &servaddr.sin_addr);inet_pton(AF_INET, argv[1], &servaddr.sin_addr);inet_pton(AF_INET, argv[1], &servaddr.sin_addr);inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )err_sys("socket error");err_sys("socket error");err_sys("socket error");err_sys("socket error");

client_echo_udp(stdin, sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));client_echo_udp(stdin, sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));client_echo_udp(stdin, sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));client_echo_udp(stdin, sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));

exit(0);exit(0);exit(0);exit(0);}}}}

echoudpcli.cechoudpcli.cechoudpcli.cechoudpcli.c

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

62626262Client echo con UDP (2)

void client_echo_udp(FILE *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) {void client_echo_udp(FILE *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) {void client_echo_udp(FILE *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) {void client_echo_udp(FILE *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) {int int int int n;n;n;n;char char char char sendline[MAXLINE], recvline[MAXLINE + 1];sendline[MAXLINE], recvline[MAXLINE + 1];sendline[MAXLINE], recvline[MAXLINE + 1];sendline[MAXLINE], recvline[MAXLINE + 1];char char char char buff[MAXLINE];buff[MAXLINE];buff[MAXLINE];buff[MAXLINE];socklen_t socklen_t socklen_t socklen_t len;len;len;len;struct sockaddr *p_replyaddr;struct sockaddr *p_replyaddr;struct sockaddr *p_replyaddr;struct sockaddr *p_replyaddr;

p_replyaddr = malloc(servlen);p_replyaddr = malloc(servlen);p_replyaddr = malloc(servlen);p_replyaddr = malloc(servlen);

while (fgets(sendline, MAXLINE, fp) != NULL) {while (fgets(sendline, MAXLINE, fp) != NULL) {while (fgets(sendline, MAXLINE, fp) != NULL) {while (fgets(sendline, MAXLINE, fp) != NULL) {sendto(sockfd, sendline, strlen(sendline), 0, p_servaddr, servlen);sendto(sockfd, sendline, strlen(sendline), 0, p_servaddr, servlen);sendto(sockfd, sendline, strlen(sendline), 0, p_servaddr, servlen);sendto(sockfd, sendline, strlen(sendline), 0, p_servaddr, servlen);len = servlen;len = servlen;len = servlen;len = servlen;

if( (n = recvfrom(sockfd, recvline, MAXLINE, 0, p_replyaddr, &len)) < 0 )if( (n = recvfrom(sockfd, recvline, MAXLINE, 0, p_replyaddr, &len)) < 0 )if( (n = recvfrom(sockfd, recvline, MAXLINE, 0, p_replyaddr, &len)) < 0 )if( (n = recvfrom(sockfd, recvline, MAXLINE, 0, p_replyaddr, &len)) < 0 )err_sys("recvfrom error");err_sys("recvfrom error");err_sys("recvfrom error");err_sys("recvfrom error");

if( (len != servlen) || memcmp(p_servaddr, p_replyaddr, len) != 0 ) {if( (len != servlen) || memcmp(p_servaddr, p_replyaddr, len) != 0 ) {if( (len != servlen) || memcmp(p_servaddr, p_replyaddr, len) != 0 ) {if( (len != servlen) || memcmp(p_servaddr, p_replyaddr, len) != 0 ) {struct sockaddr_in *sin = (struct sockaddr_in *) p_replyaddr;struct sockaddr_in *sin = (struct sockaddr_in *) p_replyaddr;struct sockaddr_in *sin = (struct sockaddr_in *) p_replyaddr;struct sockaddr_in *sin = (struct sockaddr_in *) p_replyaddr;err_msg("risposta da %s ignorataerr_msg("risposta da %s ignorataerr_msg("risposta da %s ignorataerr_msg("risposta da %s ignorata\\\\n", n", n", n",

inet_ntop(AF_INET, &sininet_ntop(AF_INET, &sininet_ntop(AF_INET, &sininet_ntop(AF_INET, &sin---->sin_addr, buff, sizeof(buff)));>sin_addr, buff, sizeof(buff)));>sin_addr, buff, sizeof(buff)));>sin_addr, buff, sizeof(buff)));continue;continue;continue;continue;

}}}}

recvline[n] = 0;recvline[n] = 0;recvline[n] = 0;recvline[n] = 0;fputs(recvline, stdout);fputs(recvline, stdout);fputs(recvline, stdout);fputs(recvline, stdout);

}}}}}}}}

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

63636363Controllo sul mittente

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

64646464Datagrammi perduti

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

65656565Connect e UDP

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

66666666Socket UDP connesso

� 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

Page 12: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

12

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

67676767connect

� È 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

68686868UDP client – versione connect

void client_echo_udp_conn(FILE *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) {void client_echo_udp_conn(FILE *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) {void client_echo_udp_conn(FILE *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) {void client_echo_udp_conn(FILE *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) {

int n;int n;int n;int n;char sendline[MAXLINE], recvline[MAXLINE + 1];char sendline[MAXLINE], recvline[MAXLINE + 1];char sendline[MAXLINE], recvline[MAXLINE + 1];char sendline[MAXLINE], recvline[MAXLINE + 1];

if( connect(sockfd, (struct sockaddr *) p_servaddr, servlen) < 0 )if( connect(sockfd, (struct sockaddr *) p_servaddr, servlen) < 0 )if( connect(sockfd, (struct sockaddr *) p_servaddr, servlen) < 0 )if( connect(sockfd, (struct sockaddr *) p_servaddr, servlen) < 0 )err_sys("connect error");err_sys("connect error");err_sys("connect error");err_sys("connect error");

while (fgets(sendline, MAXLINE, fp) != NULL) {while (fgets(sendline, MAXLINE, fp) != NULL) {while (fgets(sendline, MAXLINE, fp) != NULL) {while (fgets(sendline, MAXLINE, fp) != NULL) {

write(sockfd, sendline, strlen(sendline));write(sockfd, sendline, strlen(sendline));write(sockfd, sendline, strlen(sendline));write(sockfd, sendline, strlen(sendline));

n = read(sockfd, recvline, MAXLINE);n = read(sockfd, recvline, MAXLINE);n = read(sockfd, recvline, MAXLINE);n = read(sockfd, recvline, MAXLINE);

recvline[n] = 0; /* null terminate */recvline[n] = 0; /* null terminate */recvline[n] = 0; /* null terminate */recvline[n] = 0; /* null terminate */fputs(recvline, stdout);fputs(recvline, stdout);fputs(recvline, stdout);fputs(recvline, stdout);

}}}}}}}}

echoudpcliechoudpcliechoudpcliechoudpcli----connect.cconnect.cconnect.cconnect.c

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

69696969Inaffidabilità di UDP

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

70707070UDP echo server count (1)

#include "basic.h"#include "basic.h"#include "basic.h"#include "basic.h"#include "echo.h"#include "echo.h"#include "echo.h"#include "echo.h"

void server_echo_udp_count(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen);void server_echo_udp_count(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen);void server_echo_udp_count(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen);void server_echo_udp_count(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen);static void gestisci_interrupt(int signo); static void gestisci_interrupt(int signo); static void gestisci_interrupt(int signo); static void gestisci_interrupt(int signo);

int count = 0;int count = 0;int count = 0;int count = 0;

int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {int sockfd;int sockfd;int sockfd;int sockfd;struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;

if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )err_sys("socket error");err_sys("socket error");err_sys("socket error");err_sys("socket error");

bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);

if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )err_sys("bind error");err_sys("bind error");err_sys("bind error");err_sys("bind error");

signal(SIGINT, gestisci_interrupt);signal(SIGINT, gestisci_interrupt);signal(SIGINT, gestisci_interrupt);signal(SIGINT, gestisci_interrupt);

server_echo_udp_count(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));server_echo_udp_count(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));server_echo_udp_count(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));server_echo_udp_count(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));}}}}

echoudpcliechoudpcliechoudpcliechoudpcli----count.ccount.ccount.ccount.c

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

71717171UDP echo server count (2)

void server_echo_udp_count(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen) {void server_echo_udp_count(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen) {void server_echo_udp_count(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen) {void server_echo_udp_count(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen) {int int int int n;n;n;n;socklen_t socklen_t socklen_t socklen_t len;len;len;len;char char char char mesg[MAXLINE];mesg[MAXLINE];mesg[MAXLINE];mesg[MAXLINE];

n = 240 * 1024;n = 240 * 1024;n = 240 * 1024;n = 240 * 1024;setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));

for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {len = clilen;len = clilen;len = clilen;len = clilen;recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);count++;count++;count++;count++;sleep(1); /* rallentiamo il server */sleep(1); /* rallentiamo il server */sleep(1); /* rallentiamo il server */sleep(1); /* rallentiamo il server */

}}}}}}}}

static void gestisci_interrupt(int signo) {static void gestisci_interrupt(int signo) {static void gestisci_interrupt(int signo) {static void gestisci_interrupt(int signo) {printf("printf("printf("printf("\\\\nDatagrams ricevuti: %dnDatagrams ricevuti: %dnDatagrams ricevuti: %dnDatagrams ricevuti: %d\\\\n", count);n", count);n", count);n", count);exit(0);exit(0);exit(0);exit(0);

}}}}

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

72727272UDP echo client count

� Il client è come gli altri� Cambia solo la funzione che spedisce i datagram

� Cosa succede se usiamo questo client-server?

#define NDG 2000 /* #datagrams to send */#define NDG 2000 /* #datagrams to send */#define NDG 2000 /* #datagrams to send */#define NDG 2000 /* #datagrams to send */#define DGLEN 1400 /* length of each datagram */#define DGLEN 1400 /* length of each datagram */#define DGLEN 1400 /* length of each datagram */#define DGLEN 1400 /* length of each datagram */

void client_echo_udp_count(FILE *fp, int sockfd, const struct sockaddr *pservaddr, socklen_t servlen) {void client_echo_udp_count(FILE *fp, int sockfd, const struct sockaddr *pservaddr, socklen_t servlen) {void client_echo_udp_count(FILE *fp, int sockfd, const struct sockaddr *pservaddr, socklen_t servlen) {void client_echo_udp_count(FILE *fp, int sockfd, const struct sockaddr *pservaddr, socklen_t servlen) {int i;int i;int i;int i;char sendline[MAXLINE];char sendline[MAXLINE];char sendline[MAXLINE];char sendline[MAXLINE];

for (i = 0; i < NDG; i++) {for (i = 0; i < NDG; i++) {for (i = 0; i < NDG; i++) {for (i = 0; i < NDG; i++) {sendto(sockfd, sendline, DGLEN, 0, pservaddr, servlen);sendto(sockfd, sendline, DGLEN, 0, pservaddr, servlen);sendto(sockfd, sendline, DGLEN, 0, pservaddr, servlen);sendto(sockfd, sendline, DGLEN, 0, pservaddr, servlen);

}}}}}}}}

echoudpcliechoudpcliechoudpcliechoudpcli----count.ccount.ccount.ccount.c

Page 13: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

13

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

73737373Errori non segnalati

robdep@zircone:~/Corsi/Reti/C> netstat robdep@zircone:~/Corsi/Reti/C> netstat robdep@zircone:~/Corsi/Reti/C> netstat robdep@zircone:~/Corsi/Reti/C> netstat ----s | grep s | grep s | grep s | grep ----C4 "Udp" | tail C4 "Udp" | tail C4 "Udp" | tail C4 "Udp" | tail ----5; 5; 5; 5;

Udp:Udp:Udp:Udp:

6686 packets received6686 packets received6686 packets received6686 packets received

2012 packets to unknown port received.2012 packets to unknown port received.2012 packets to unknown port received.2012 packets to unknown port received.

9674 packet receive errors9674 packet receive errors9674 packet receive errors9674 packet receive errors

18634 packets sent18634 packets sent18634 packets sent18634 packets sent

robdep@zircone:~/Corsi/Reti/C> echoudpsrvrobdep@zircone:~/Corsi/Reti/C> echoudpsrvrobdep@zircone:~/Corsi/Reti/C> echoudpsrvrobdep@zircone:~/Corsi/Reti/C> echoudpsrv----countcountcountcount

Datagrams ricevuti: 11 Datagrams ricevuti: 11 Datagrams ricevuti: 11 Datagrams ricevuti: 11

robdep@zircone:~/Corsi/Reti/C> netstat robdep@zircone:~/Corsi/Reti/C> netstat robdep@zircone:~/Corsi/Reti/C> netstat robdep@zircone:~/Corsi/Reti/C> netstat ----s | grep s | grep s | grep s | grep ----C4 "Udp" | tail C4 "Udp" | tail C4 "Udp" | tail C4 "Udp" | tail ----5; 5; 5; 5;

Udp:Udp:Udp:Udp:

7206 packets received7206 packets received7206 packets received7206 packets received

2012 packets to unknown port received.2012 packets to unknown port received.2012 packets to unknown port received.2012 packets to unknown port received.

11154 packet receive errors11154 packet receive errors11154 packet receive errors11154 packet receive errors

20634 packets sent20634 packets sent20634 packets sent20634 packets sent

Client in un’altra shell; dopo un pòCTRLClient in un’altra shell; dopo un pòCTRLClient in un’altra shell; dopo un pòCTRLClient in un’altra shell; dopo un pòCTRL----CCCC

robdep@zircone:~/Corsi/Reti/C> echoudpclirobdep@zircone:~/Corsi/Reti/C> echoudpclirobdep@zircone:~/Corsi/Reti/C> echoudpclirobdep@zircone:~/Corsi/Reti/C> echoudpcli----count 127.0.0.1count 127.0.0.1count 127.0.0.1count 127.0.0.1

robdep@zircone:~/Corsi/Reti/C> robdep@zircone:~/Corsi/Reti/C> robdep@zircone:~/Corsi/Reti/C> robdep@zircone:~/Corsi/Reti/C>

Prof. Roberto De Prisco

Reti di Calcolatori

Corso di laurea in Informatica

A.A. 2007-2008

Lezione

I/O Multiplexing

4444

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

75757575Problema

� Un programma deve gestire due input simultaneamente� Standard 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

� Serve un modo per poter “aspettare” da più canali di input� Il primo che produce dati viene letto

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

76767676Modelli di I/O

� Vari modelli di Input/Output1. Blocking 2. Nonblocking3. I/O multiplexing4. Guidato dai segnali5. Asincrono

� 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 fasi1. Attesa per i dati da parte del kernel2. Copia dei dati dal kernel al processo che deve usarli

sincronisincronisincronisincroni

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

77777777Blocking I/O

applicazioneapplicazioneapplicazioneapplicazione kernelkernelkernelkernel

Recvfrom System callSystem callSystem callSystem call Non ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram pronti

datagram prontodatagram prontodatagram prontodatagram pronto

Copia datagramCopia datagramCopia datagramCopia datagram

Copia completataCopia completataCopia completataCopia completataProcesso continua (elabora il Processo continua (elabora il Processo continua (elabora il Processo continua (elabora il datagram)datagram)datagram)datagram)

BLOCCATA

BLOCCATA

BLOCCATA

BLOCCATA

Ritorna OKRitorna OKRitorna OKRitorna OK

FASE 1: FASE 1: FASE 1: FASE 1:

attesaattesaattesaattesa

FASE 2: FASE 2: FASE 2: FASE 2:

copiacopiacopiacopia

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

78787878Nonblocking I/O

applicazioneapplicazioneapplicazioneapplicazione kernelkernelkernelkernel

Recvfrom System callSystem callSystem callSystem call

Non ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram pronti

datagram prontodatagram prontodatagram prontodatagram pronto

Copia datagramCopia datagramCopia datagramCopia datagram

Copia completataCopia completataCopia completataCopia completataProcesso continua (elabora il Processo continua (elabora il Processo continua (elabora il Processo continua (elabora il datagram)datagram)datagram)datagram)

BLOCCATA

BLOCCATA

BLOCCATA

BLOCCATA

Ritorna OKRitorna OKRitorna OKRitorna OK

FASE 1: FASE 1: FASE 1: FASE 1:

attesaattesaattesaattesa

FASE 2: FASE 2: FASE 2: FASE 2:

copiacopiacopiacopia

EWOULDBLOCK

EWOULDBLOCK

System callSystem callSystem callSystem call

System callSystem callSystem callSystem call

Recvfrom

Recvfrom

Page 14: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

14

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

79797979I/O multiplexing

applicazioneapplicazioneapplicazioneapplicazione kernelkernelkernelkernel

selectSystem callSystem callSystem callSystem call Non ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram pronti

datagram prontodatagram prontodatagram prontodatagram pronto

Copia datagramCopia datagramCopia datagramCopia datagram

Copia completataCopia completataCopia completataCopia completataProcesso continua (elabora il Processo continua (elabora il Processo continua (elabora il Processo continua (elabora il datagram)datagram)datagram)datagram)

BLOCCATA

BLOCCATA

BLOCCATA

BLOCCATA

Ritorna OKRitorna OKRitorna OKRitorna OK

FASE 1: FASE 1: FASE 1: FASE 1:

attesaattesaattesaattesa

FASE 2: FASE 2: FASE 2: FASE 2:

copiacopiacopiacopia

System callSystem callSystem callSystem callRecvfrom

BLOCCATA

BLOCCATA

BLOCCATA

BLOCCATA

Ritorna “pronto”Ritorna “pronto”Ritorna “pronto”Ritorna “pronto”

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

80808080I/O guidato dai segnali

applicazioneapplicazioneapplicazioneapplicazione kernelkernelkernelkernel

signalSystem callSystem callSystem callSystem call

datagram prontodatagram prontodatagram prontodatagram pronto

Copia datagramCopia datagramCopia datagramCopia datagram

Copia completataCopia completataCopia completataCopia completataProcesso continua (elabora il Processo continua (elabora il Processo continua (elabora il Processo continua (elabora il datagram)datagram)datagram)datagram)

BLOCCATA

BLOCCATA

BLOCCATA

BLOCCATA

Ritorna OKRitorna OKRitorna OKRitorna OK

FASE 1: FASE 1: FASE 1: FASE 1:

attesaattesaattesaattesa

FASE 2: FASE 2: FASE 2: FASE 2:

copiacopiacopiacopia

System callSystem callSystem callSystem callRecvfrom

SEGNALESEGNALESEGNALESEGNALEGESTORE SEGNALE

Non ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram pronti

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

81818181I/O asincrono

applicazioneapplicazioneapplicazioneapplicazione kernelkernelkernelkernel

aio_readSystem callSystem callSystem callSystem call

datagram prontodatagram prontodatagram prontodatagram pronto

Copia datagramCopia datagramCopia datagramCopia datagram

Copia completataCopia completataCopia completataCopia completata

FASE 1: FASE 1: FASE 1: FASE 1:

attesaattesaattesaattesa

FASE 2: FASE 2: FASE 2: FASE 2:

copiacopiacopiacopia

SEGNALESEGNALESEGNALESEGNALEGESTORE SEGNALE

Non ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram pronti

Processo continua (elabora il Processo continua (elabora il Processo continua (elabora il Processo continua (elabora il datagram)datagram)datagram)datagram)

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

82828282Funzione select

#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;

}

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

83838383Parametri select

� Timeout1. 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

84848484Parametri select

� 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)

Page 15: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

15

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

85858585Descrittori pronti

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

86868686Descrittori pronti

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

87878787echoclient – versione select (1)

void client_echo_select(FILE *fp, int sockfd) {void client_echo_select(FILE *fp, int sockfd) {void client_echo_select(FILE *fp, int sockfd) {void client_echo_select(FILE *fp, int sockfd) {

int maxfdl;int maxfdl;int maxfdl;int maxfdl;fd_set rset;fd_set rset;fd_set rset;fd_set rset;

char sendline[MAXLINE], recvline[MAXLINE];char sendline[MAXLINE], recvline[MAXLINE];char sendline[MAXLINE], recvline[MAXLINE];char sendline[MAXLINE], recvline[MAXLINE];

int n;int n;int n;int n;

FD_ZERO(&rset);FD_ZERO(&rset);FD_ZERO(&rset);FD_ZERO(&rset);

for( ; ; ) {for( ; ; ) {for( ; ; ) {for( ; ; ) {

FD_SET(fileno(fp), &rset);FD_SET(fileno(fp), &rset);FD_SET(fileno(fp), &rset);FD_SET(fileno(fp), &rset);

FD_SET(sockfd, &rset);FD_SET(sockfd, &rset);FD_SET(sockfd, &rset);FD_SET(sockfd, &rset);

maxfdl = MAX(fileno(fp), sockfd) + 1;maxfdl = MAX(fileno(fp), sockfd) + 1;maxfdl = MAX(fileno(fp), sockfd) + 1;maxfdl = MAX(fileno(fp), sockfd) + 1;

if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )

err_sys("select error");err_sys("select error");err_sys("select error");err_sys("select error");

if( FD_ISSET(sockfd, &rset) ) {if( FD_ISSET(sockfd, &rset) ) {if( FD_ISSET(sockfd, &rset) ) {if( FD_ISSET(sockfd, &rset) ) {

if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {

if( errno == EPIPE ) {if( errno == EPIPE ) {if( errno == EPIPE ) {if( errno == EPIPE ) {

err_msg(“%s [%d]: server disconnesso“, __FILE__,__LINE__);err_msg(“%s [%d]: server disconnesso“, __FILE__,__LINE__);err_msg(“%s [%d]: server disconnesso“, __FILE__,__LINE__);err_msg(“%s [%d]: server disconnesso“, __FILE__,__LINE__);

break;break;break;break;

}}}}

elseelseelseelse

err_sys("readline error");err_sys("readline error");err_sys("readline error");err_sys("readline error");

} } } }

echocliechocliechocliechocli----slct.cslct.cslct.cslct.c

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

88888888echoclient – versione select (2)

if (n == 0)if (n == 0)if (n == 0)if (n == 0)

err_quit(“%s [%d]: server disconnesso“,__FILE__,__LINE__);err_quit(“%s [%d]: server disconnesso“,__FILE__,__LINE__);err_quit(“%s [%d]: server disconnesso“,__FILE__,__LINE__);err_quit(“%s [%d]: server disconnesso“,__FILE__,__LINE__);

fputs(recvline, stdout);fputs(recvline, stdout);fputs(recvline, stdout);fputs(recvline, stdout);

}}}}

if( FD_ISSET(fileno(fp), &rset) ) {if( FD_ISSET(fileno(fp), &rset) ) {if( FD_ISSET(fileno(fp), &rset) ) {if( FD_ISSET(fileno(fp), &rset) ) {

if( fgets(sendline, MAXLINE, fp) == NULL) if( fgets(sendline, MAXLINE, fp) == NULL) if( fgets(sendline, MAXLINE, fp) == NULL) if( fgets(sendline, MAXLINE, fp) == NULL)

return;return;return;return;

if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)

err_sys("write error");err_sys("write error");err_sys("write error");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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

89898989echoclient e select

� Condizioni gestite da select in lettura su stdin ed un socket

Data o EOFData o EOFData o EOFData o EOFstdinstdinstdinstdin

RSTRSTRSTRST FINFINFINFINdatadatadatadata

socketsocketsocketsocket

ClientClientClientClient

TCPTCPTCPTCP

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

90909090Stop-and-wait

� Il client opera in modalità stop-and-wait:� Spedisce una linea di input e si blocca in attesa

della risposta del server echo

CCCC

SSSS

Tempo 0

datidatidatidati

Tempo 1

datidatidatidati

Tempo 2

datidatidatidati

Tempo 3

datidatidatidati

SSSS

Tempo 4

echoechoechoecho

Tempo 5

echoechoechoecho

Tempo 6

echoechoechoecho

CCCC

Tempo 7

echoechoechoecho

Page 16: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

16

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

91919191Batch input

� Si spediscono le richieste consecutivamente senza aspettare le risposte, che arriveranno dopo

CCCC

Tempo 0

d1d1d1d1

Tempo 1

d1d1d1d1CCCC d2d2d2d2

Tempo 2

d1d1d1d1CCCC d3d3d3d3 d2d2d2d2

SSSS

Tempo 3

d1d1d1d1CCCC d4d4d4d4 d3d3d3d3 d2d2d2d2

SSSS

Tempo 4

r1r1r1r1

d5d5d5d5 d4d4d4d4 d3d3d3d3 d2d2d2d2CCCC SSSS

Tempo 5

r1r1r1r1 r2r2r2r2

d5d5d5d5 d4d4d4d4 d3d3d3d3d6d6d6d6CCCC

SSSS

SSSS

Tempo 6

r1r1r1r1 r3r3r3r3r2r2r2r2

d5d5d5d5 d4d4d4d4d6d6d6d6d7d7d7d7CCCC

SSSS

SSSS

CCCC

Tempo 7

r1r1r1r1 r4r4r4r4r3r3r3r3r2r2r2r2

d6d6d6d6 d5d5d5d5d7d7d7d7d8d8d8d8

SSSS

SSSSCCCC

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

92929292Shutdown della connessione

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

93939393echoclient – versione shutdown

void client_echo_shutdown(FILE *fp, int sockfd) {void client_echo_shutdown(FILE *fp, int sockfd) {void client_echo_shutdown(FILE *fp, int sockfd) {void client_echo_shutdown(FILE *fp, int sockfd) {

int int int int maxfdl, stdineof;maxfdl, stdineof;maxfdl, stdineof;maxfdl, stdineof;fd_set fd_set fd_set fd_set rset;rset;rset;rset;

char char char char sendline[MAXLINE], recvline[MAXLINE];sendline[MAXLINE], recvline[MAXLINE];sendline[MAXLINE], recvline[MAXLINE];sendline[MAXLINE], recvline[MAXLINE];

int int int int n;n;n;n;

FD_ZERO(&rset);FD_ZERO(&rset);FD_ZERO(&rset);FD_ZERO(&rset);

for( ; ; ) {for( ; ; ) {for( ; ; ) {for( ; ; ) {

FD_SET(fileno(fp), &rset);FD_SET(fileno(fp), &rset);FD_SET(fileno(fp), &rset);FD_SET(fileno(fp), &rset);

FD_SET(sockfd, &rset);FD_SET(sockfd, &rset);FD_SET(sockfd, &rset);FD_SET(sockfd, &rset);

maxfdl = MAX(fileno(fp), sockfd) + 1;maxfdl = MAX(fileno(fp), sockfd) + 1;maxfdl = MAX(fileno(fp), sockfd) + 1;maxfdl = MAX(fileno(fp), sockfd) + 1;

if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )

err_sys("select error");err_sys("select error");err_sys("select error");err_sys("select error");

if( FD_ISSET(sockfd, &rset) ) {if( FD_ISSET(sockfd, &rset) ) {if( FD_ISSET(sockfd, &rset) ) {if( FD_ISSET(sockfd, &rset) ) {

if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {

if( errno == EPIPE ) {if( errno == EPIPE ) {if( errno == EPIPE ) {if( errno == EPIPE ) {

err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);

break;break;break;break;

}}}}

elseelseelseelse

err_sys("readline error");err_sys("readline error");err_sys("readline error");err_sys("readline error");

} } } }

echocliechocliechocliechocli----shtd.cshtd.cshtd.cshtd.c

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

94949494echoclient – versione shutdown

if (n == 0) {if (n == 0) {if (n == 0) {if (n == 0) {

if( stdineof == 1 )if( stdineof == 1 )if( stdineof == 1 )if( stdineof == 1 )return;return;return;return;

else {else {else {else {

err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);

exit(exit(exit(exit(----1);1);1);1);

}}}}

}}}}

fputs(recvline, stdout);fputs(recvline, stdout);fputs(recvline, stdout);fputs(recvline, stdout);

}}}}

if( FD_ISSET(fileno(fp), &rset) ) {if( FD_ISSET(fileno(fp), &rset) ) {if( FD_ISSET(fileno(fp), &rset) ) {if( FD_ISSET(fileno(fp), &rset) ) {

if( fgets(sendline, MAXLINE, fp) == NULL) {if( fgets(sendline, MAXLINE, fp) == NULL) {if( fgets(sendline, MAXLINE, fp) == NULL) {if( fgets(sendline, MAXLINE, fp) == NULL) {

stdineof = 1;stdineof = 1;stdineof = 1;stdineof = 1;

shutdown(sockfd, SHUT_WR);shutdown(sockfd, SHUT_WR);shutdown(sockfd, SHUT_WR);shutdown(sockfd, SHUT_WR);

FD_CLR(fileno(fp), &rset);FD_CLR(fileno(fp), &rset);FD_CLR(fileno(fp), &rset);FD_CLR(fileno(fp), &rset);

continue;continue;continue;continue;

}}}}

if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)

err_sys("write error");err_sys("write error");err_sys("write error");err_sys("write error");

}}}}

}}}}

}}}}

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

95959595Select per il server� Possiamo usare select anche nel server

� 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

----1111

----1111

----1111

----1111FD_SETSIZEFD_SETSIZEFD_SETSIZEFD_SETSIZE----1111

1111

2222

3333

clientclientclientclient

0000 0000 0000 0000 0000 0000 …………

fd0fd0fd0fd0 fd1fd1fd1fd1 fd2fd2fd2fd2 fd3fd3fd3fd3 fd4fd4fd4fd4 fd5fd5fd5fd5

rsetrsetrsetrset

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

96969696Strutture dati per il server

� 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

� 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

0000 0000 0000 1111 0000 0000 …………

fd0fd0fd0fd0 fd1fd1fd1fd1 fd2fd2fd2fd2 fd3fd3fd3fd3 fd4fd4fd4fd4 fd5fd5fd5fd5

rsetrsetrsetrset

Page 17: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

17

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

97979797Strutture dati per il server

� Quando un client stabilisce una connessione verrà creato un socket per la connessione con la funzione accept � Supponiamo che il fd ritornato è 4

� 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

0000 0000 0000 1111 1111 0000 …………

fd0fd0fd0fd0 fd1fd1fd1fd1 fd2fd2fd2fd2 fd3fd3fd3fd3 fd4fd4fd4fd4 fd5fd5fd5fd5

rsetrsetrsetrset

4444

----1111

----1111

----1111FD_SETSIZEFD_SETSIZEFD_SETSIZEFD_SETSIZE----1111

1111

2222

3333

clientclientclientclient

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

98989898Strutture dati per il server

� Supponiamo che un altro client si connette, verrà creato un nuovo socket� Supponiamo che il fd ritornato è 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

0000 0000 0000 1111 1111 1111 …………

fd0fd0fd0fd0 fd1fd1fd1fd1 fd2fd2fd2fd2 fd3fd3fd3fd3 fd4fd4fd4fd4 fd5fd5fd5fd5

rsetrsetrsetrset

4444

5555

----1111

----1111FD_SETSIZEFD_SETSIZEFD_SETSIZEFD_SETSIZE----1111

1111

2222

3333

clientclientclientclient

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

99999999Strutture dati per il server

� Supponiamo ora che la prima connessione (quella che usa fd 4) venga chiusa

� 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

0000 0000 0000 1111 0000 1111 …………

fd0fd0fd0fd0 fd1fd1fd1fd1 fd2fd2fd2fd2 fd3fd3fd3fd3 fd4fd4fd4fd4 fd5fd5fd5fd5

rsetrsetrsetrset

----1111

5555

----1111

----1111FD_SETSIZEFD_SETSIZEFD_SETSIZEFD_SETSIZE----1111

1111

2222

3333

clientclientclientclient

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

100100100100Echo server versione select (1)

int main(int argc, char **argv) int main(int argc, char **argv) int main(int argc, char **argv) int main(int argc, char **argv)

{{{{int listenfd, connfd, sockfd;int listenfd, connfd, sockfd;int listenfd, connfd, sockfd;int listenfd, connfd, sockfd;

int i, maxi, maxfd;int i, maxi, maxfd;int i, maxi, maxfd;int i, maxi, maxfd;

int ready, client[FD_SETSIZE];int ready, client[FD_SETSIZE];int ready, client[FD_SETSIZE];int ready, client[FD_SETSIZE];

char buff[MAXLINE];char buff[MAXLINE];char buff[MAXLINE];char buff[MAXLINE];

fd_set rset, allset;fd_set rset, allset;fd_set rset, allset;fd_set rset, allset;

ssize_t n;ssize_t n;ssize_t n;ssize_t n;

struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;

socklen_t cliaddr_len;socklen_t cliaddr_len;socklen_t cliaddr_len;socklen_t cliaddr_len;

if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)

err_sys("socket error");err_sys("socket error");err_sys("socket error");err_sys("socket error");

bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));

servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);

if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0)if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0)if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0)if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0)

err_sys("bind error");err_sys("bind error");err_sys("bind error");err_sys("bind error");

if( listen(listenfd, BACKLOG) < 0 )if( listen(listenfd, BACKLOG) < 0 )if( listen(listenfd, BACKLOG) < 0 )if( listen(listenfd, BACKLOG) < 0 )

err_sys("listen error");err_sys("listen error");err_sys("listen error");err_sys("listen error");

echosrvechosrvechosrvechosrv----slct.cslct.cslct.cslct.c

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

101101101101Echo server versione select (2)

maxfd = listenfd; /* inzializza il numero di descrittori */maxfd = listenfd; /* inzializza il numero di descrittori */maxfd = listenfd; /* inzializza il numero di descrittori */maxfd = listenfd; /* inzializza il numero di descrittori */

maxi = maxi = maxi = maxi = ----1;1;1;1;for ( i = 0; i < FD_SETSIZE; i++) client[i] = for ( i = 0; i < FD_SETSIZE; i++) client[i] = for ( i = 0; i < FD_SETSIZE; i++) client[i] = for ( i = 0; i < FD_SETSIZE; i++) client[i] = ----1; /* inizializza l'array client a 1; /* inizializza l'array client a 1; /* inizializza l'array client a 1; /* inizializza l'array client a ----1 */1 */1 */1 */

FD_ZERO(&allset); /* inizializza a zero tutti i descrittori */FD_ZERO(&allset); /* inizializza a zero tutti i descrittori */FD_ZERO(&allset); /* inizializza a zero tutti i descrittori */FD_ZERO(&allset); /* inizializza a zero tutti i descrittori */

FD_SET(listenfd, &allset); /* setta il descrittore di ascolto */FD_SET(listenfd, &allset); /* setta il descrittore di ascolto */FD_SET(listenfd, &allset); /* setta il descrittore di ascolto */FD_SET(listenfd, &allset); /* setta il descrittore di ascolto */

for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {

rset = allset; /* insieme descrittori da controllare per la lettura */rset = allset; /* insieme descrittori da controllare per la lettura */rset = allset; /* insieme descrittori da controllare per la lettura */rset = allset; /* insieme descrittori da controllare per la lettura */

if( (ready = select(maxfd+1, &rset, NULL, NULL, NULL)) < 0 )if( (ready = select(maxfd+1, &rset, NULL, NULL, NULL)) < 0 )if( (ready = select(maxfd+1, &rset, NULL, NULL, NULL)) < 0 )if( (ready = select(maxfd+1, &rset, NULL, NULL, NULL)) < 0 )

err_sys("select error");err_sys("select error");err_sys("select error");err_sys("select error");

if( FD_ISSET(listenfd, &rset) ) { /* richiesta ricevuta dal listening socket */if( FD_ISSET(listenfd, &rset) ) { /* richiesta ricevuta dal listening socket */if( FD_ISSET(listenfd, &rset) ) { /* richiesta ricevuta dal listening socket */if( FD_ISSET(listenfd, &rset) ) { /* richiesta ricevuta dal listening socket */

cliaddr_len = sizeof(cliaddr);cliaddr_len = sizeof(cliaddr);cliaddr_len = sizeof(cliaddr);cliaddr_len = sizeof(cliaddr);

if( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddr_len)) < 0) if( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddr_len)) < 0) if( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddr_len)) < 0) if( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddr_len)) < 0)

err_sys("accept error");err_sys("accept error");err_sys("accept error");err_sys("accept error");

for(i = 0; i < FD_SETSIZE; i++)for(i = 0; i < FD_SETSIZE; i++)for(i = 0; i < FD_SETSIZE; i++)for(i = 0; i < FD_SETSIZE; i++)

if( client[i] < 0 ) { /* cerca il primo posto libero per il nuovo il descrittore */if( client[i] < 0 ) { /* cerca il primo posto libero per il nuovo il descrittore */if( client[i] < 0 ) { /* cerca il primo posto libero per il nuovo il descrittore */if( client[i] < 0 ) { /* cerca il primo posto libero per il nuovo il descrittore */

client[i] = connfd;client[i] = connfd;client[i] = connfd;client[i] = connfd;

break;break;break;break;

}}}}

if( i == FD_SETSIZE ) err_quit("troppi client"); if( i == FD_SETSIZE ) err_quit("troppi client"); if( i == FD_SETSIZE ) err_quit("troppi client"); if( i == FD_SETSIZE ) err_quit("troppi client");

FD_SET(connfd, &allset); /* setta connfd per la select */FD_SET(connfd, &allset); /* setta connfd per la select */FD_SET(connfd, &allset); /* setta connfd per la select */FD_SET(connfd, &allset); /* setta connfd per la select */

if( connfd > maxfd ) maxfd = connfd; /* aggiorna maxfd */if( connfd > maxfd ) maxfd = connfd; /* aggiorna maxfd */if( connfd > maxfd ) maxfd = connfd; /* aggiorna maxfd */if( connfd > maxfd ) maxfd = connfd; /* aggiorna maxfd */

if( i > maxi ) maxi = i; /* aggiorna maxi */if( i > maxi ) maxi = i; /* aggiorna maxi */if( i > maxi ) maxi = i; /* aggiorna maxi */if( i > maxi ) maxi = i; /* aggiorna maxi */

if( if( if( if( --------ready <= 0 ) ready <= 0 ) ready <= 0 ) ready <= 0 )

continue; /* se non ci sono altri socket pronti riprendi da select */continue; /* se non ci sono altri socket pronti riprendi da select */continue; /* se non ci sono altri socket pronti riprendi da select */continue; /* se non ci sono altri socket pronti riprendi da select */

} } } }

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

102102102102Echo server versione select (3)

for( i = 0; i <= maxi; i++ ) {for( i = 0; i <= maxi; i++ ) {for( i = 0; i <= maxi; i++ ) {for( i = 0; i <= maxi; i++ ) {/* controlla tutti i socket attivi per controllare se sono leggibili *//* controlla tutti i socket attivi per controllare se sono leggibili *//* controlla tutti i socket attivi per controllare se sono leggibili *//* controlla tutti i socket attivi per controllare se sono leggibili */

if( (sockfd = client[i]) < 0 ) if( (sockfd = client[i]) < 0 ) if( (sockfd = client[i]) < 0 ) if( (sockfd = client[i]) < 0 )

continue;continue;continue;continue;

if ( FD_ISSET(sockfd, &rset) ) {if ( FD_ISSET(sockfd, &rset) ) {if ( FD_ISSET(sockfd, &rset) ) {if ( FD_ISSET(sockfd, &rset) ) {

/* se sockfd è leggibile invoca la readline *//* se sockfd è leggibile invoca la readline *//* se sockfd è leggibile invoca la readline *//* se sockfd è leggibile invoca la readline */

if ( (n = reti_readline(sockfd, buff, MAXLINE)) == 0) {if ( (n = reti_readline(sockfd, buff, MAXLINE)) == 0) {if ( (n = reti_readline(sockfd, buff, MAXLINE)) == 0) {if ( (n = reti_readline(sockfd, buff, MAXLINE)) == 0) {

/* connessione chiusa dall'altro endpoint *//* connessione chiusa dall'altro endpoint *//* connessione chiusa dall'altro endpoint *//* connessione chiusa dall'altro endpoint */

close(sockfd);close(sockfd);close(sockfd);close(sockfd);

/* rimuovi sockfd dalla lista di socket /* rimuovi sockfd dalla lista di socket /* rimuovi sockfd dalla lista di socket /* rimuovi sockfd dalla lista di socket

che la select deve controllare */che la select deve controllare */che la select deve controllare */che la select deve controllare */

FD_CLR(sockfd, &allset); FD_CLR(sockfd, &allset); FD_CLR(sockfd, &allset); FD_CLR(sockfd, &allset);

client[i] = client[i] = client[i] = client[i] = ----1; /* cancella sockfd da client */1; /* cancella sockfd da client */1; /* cancella sockfd da client */1; /* cancella sockfd da client */

}}}}

elseelseelseelse

reti_writen(sockfd, buff, n);reti_writen(sockfd, buff, n);reti_writen(sockfd, buff, n);reti_writen(sockfd, buff, n);

if ( if ( if ( if ( --------ready <= 0 )ready <= 0 )ready <= 0 )ready <= 0 )

break;break;break;break;

}}}}

}}}}

}}}}

}}}}

Page 18: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

18

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

103103103103Denial of Service Attack

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

104104104104Denial of Service Attack

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

105105105105Esercizi

� 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

Prof. Roberto De Prisco

Reti di Calcolatori

Corso di laurea in Informatica

A.A. 2007-2008

Lezione

Opzioni per i socket

5555

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

107107107107Socket 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 (int o anche strutture più

complesse)

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

108108108108Livelli

� 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

Page 19: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

19

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

109109109109OpzioniSO_BROADCAST permette il broadcastSO_DEBUG abilita le informazioni di debugSO_DONTROUTE salta il lookup nella tavola di routingSO_ERROR legge l’errore corrente SO_KEEPALIVE controlla che la connessione sia attivaSO_LINGER controlla la chiusura della connessioneSO_RCVBUF grandezza del buffer in ricezioneSO_SNDBUF grandezza buffer in spedizioneSO_RCVLOWAT soglia per il buffer in ricezioneSO_SNDLOWAT soglia per il buffer in spedizioneSO_RCVTIMEO timeout per la ricezioneSO_SNDTIMEO timeout per la spedizioneSO_REUSEADDR permette riutilizzo indirizzi localiSO_REUSEPORT permette riutilizzo porte localiSO_TYPE il tipo di socketSO_USELOOPBACK per i socket di routing (copia i pacchetti)

IP_HDRINCL header incluso con i datiIP_OPTIONS opzioni nell’header IPIP_TTL Time-To-Live

TCP_KEEPALIVE tempo per la ritrasmissioneTCP_MAXRT tempo massimo per la ritrasmissioneTCP_MAXSEG MSS (Maximum Segment Size)TCP_NODELAY disabilita algoritmo di Nagle

Livello:Livello:Livello:Livello:

SOL_SOCKETSOL_SOCKETSOL_SOCKETSOL_SOCKET

Livello:Livello:Livello:Livello:

IPPROTO_IPIPPROTO_IPIPPROTO_IPIPPROTO_IP

Livello:Livello:Livello:Livello:

IPPROTO_TCPIPPROTO_TCPIPPROTO_TCPIPPROTO_TCP

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

110110110110getsockopt e setsockopt

#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);

Valore di ritorno: -1 se errore, 0 se OK

Parametri:

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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

111111111111SO_BROADCAST

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

112112112112SO_ERROR

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

113113113113SO_KEEPALIVE

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

114114114114SO_LINGER

� 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

Page 20: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

20

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

115115115115SO_RCVBUF e SO_SNDBUF

� Ogni socket ha un buffer di ricezione ed uno di spedizione

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

116116116116SO_RCVLOWAT e SO_SNDLOWAT

� 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

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

117117117117SO_RCVTIMEO e SO_SNDTIMEO

� Ci 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

� Il timeout in spedizione è per le funzioni� Write, writev, send, sendto

� Per default il timeout è 0� Timeout disabilitato, quindi la funzione si blocca

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

118118118118SO_TYPE

� Ci dà il tipo di socket� SOCK_STREAM� SOCK_DGRAM

� Viene usata da un processo che eredita un socket

� Può essere solo letta, non scritta

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

119119119119Opzioni di IPv4

� IP_HDRINCL� Se è settata per un raw socket di IP allora l’applicazione

deve costruire anche l’IP header che normalmente è scritto dal kernel

� IP_OPTIONS� Permette di inserire le opzioni dell’IP header

� IP_TTL� Permette di specificare il Time-To-Live (in hops) del

datagram� Il default è 64

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

120120120120Opzioni di TCP

� TCP_KEEPALIVE� Permette di moficare le 2 ore di attesa usate da SO_KEEPALIVE

� TCP_MAXRT� -1, ritrasmetti per sempre� 0, usa il default di sistema� n > 0, ritrasmetti per n secondi prima di abbandonare la

connessione

� TCP_MAXSEG� Permette di modificare il valore dell’MSS della connessione

� TCP_NODELAY� Diabilita l’algoritmo di Nagle che diminuisce i dati spediti in caso di

congestione

Page 21: Reti di Calcolatori - INTRANET - Dipartimento di Informatica · 1 Prof. Roberto De Prisco Reti di Calcolatori Corso di laurea in Informatica A.A. 2007-2008 Lezione LABORATORIO 1111

21

LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007

121121121121Esempio di set e get sockopt

#include "basic.h" #include "basic.h" #include "basic.h" #include "basic.h"

#include <netinet/tcp.h> #include <netinet/tcp.h> #include <netinet/tcp.h> #include <netinet/tcp.h> int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {

int sockfd, mss, sendbuff, ttl;int sockfd, mss, sendbuff, ttl;int sockfd, mss, sendbuff, ttl;int sockfd, mss, sendbuff, ttl;

socklen_t optlen;socklen_t optlen;socklen_t optlen;socklen_t optlen;

if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )

err_sys("socket error");err_sys("socket error");err_sys("socket error");err_sys("socket error");

optlen = sizeof(ttl);optlen = sizeof(ttl);optlen = sizeof(ttl);optlen = sizeof(ttl);

if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == ----1 )1 )1 )1 )

err_ret("getsockopt error");err_ret("getsockopt error");err_ret("getsockopt error");err_ret("getsockopt error");

printf("getsockopt: TTL = %dprintf("getsockopt: TTL = %dprintf("getsockopt: TTL = %dprintf("getsockopt: TTL = %d\\\\n", ttl);n", ttl);n", ttl);n", ttl);

ttl = ttl + 16;ttl = ttl + 16;ttl = ttl + 16;ttl = ttl + 16;

if( setsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == if( setsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == if( setsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == if( setsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == ----1 )1 )1 )1 )

err_ret("getsockopt error");err_ret("getsockopt error");err_ret("getsockopt error");err_ret("getsockopt error");

printf("TTL modificatoprintf("TTL modificatoprintf("TTL modificatoprintf("TTL modificato\\\\n", ttl);n", ttl);n", ttl);n", ttl);

if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == ----1 )1 )1 )1 )

err_ret("getsockopt error");err_ret("getsockopt error");err_ret("getsockopt error");err_ret("getsockopt error");

printf("getsockopt: TTL = %dprintf("getsockopt: TTL = %dprintf("getsockopt: TTL = %dprintf("getsockopt: TTL = %d\\\\n", ttl);n", ttl);n", ttl);n", ttl);

exit(0);exit(0);exit(0);exit(0);

}}}}

sockoptssockoptssockoptssockopts----set.cset.cset.cset.c


Recommended