Sei sulla pagina 1di 61

Corso di laurea in Informatica

Reti di Calcolatori
A.A. 2007-2008

Prof. Roberto De Prisco

LABORATORIO

Lezione

Obiettivo

Autunno 2007
LABORATORIO

 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

Autunno 2007

Desktop grafico

LABORATORIO

Desktop grafico
Usare solo per scaricare gli esempi

Terminale testuale
CTRL-ALT-F1
CTRL-ALT-F6

Linea di comando

Autunno 2007
LABORATORIO

 Terminale testuale
 Line di comando
 Prompt
robdep@zircone:~/Corsi/Reti/C> gmake
gcc -g -O0 -Werror -c lib-errori.c
gcc -g -O0 -Werror -c lib-corso-reti.c
compiling daytimesrv.c with rule 1

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

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

Shell

Autunno 2007
LABORATORIO

 Shell
 il programma che interpreta i comandi

 BASH
 la shell standard di Linux
 Echo $SHELL

 Sezione risorse del Sito web


 Link a pagine su Bash e altro (es. editor vi)

 Sito Web del corso LSO

File

Autunno 2007
LABORATORIO

 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

Programmi in C

Autunno 2007
LABORATORIO

#include <stdio.h>
int main(int argc, char **argv) {
union {
short s;
char c[sizeof(short)];
} un;
un.s = 0x0102;
printf("CPU = %s - byte ordering: ",getenv("CPU"));
if (sizeof(short) == 2) {
if ( un.c[0] == 1 && un.c[1] == 2 )
printf ("big("big-endian\
endian\n");
else if ( un.c[0] == 2 && un.c[1] == 1 )
printf ("little("little-endian\
endian\n");
else
printf("unknown\
printf("unknown\n");
}
else
printf("size of short: %d.\
%d.\n",sizeof(short));
exit(0);
}

Compilazione e Makefile

Autunno 2007
LABORATORIO

 Il sorgente C va compilato
 Compilare un programma consiste nel

1. trasformare il sorgente C in codice oggetto


2. unire tale codice oggetto con le librerie (link)

 gcc (GNU C Compiler)

 gcc ls1.c
 gcc o ls1 ls1.c
 gcc Lmylibpath lmylib Imyincpath O DDEBUG ...

 Makefile

 make ... fa tutto

Makefile

Autunno 2007
LABORATORIO

#Makefile
ALL = lib-errori lib-corso-reti \
daytimesrv daytimecli daytimesrv-ric \
echosrv echocli echosrv-sigh
all: $(ALL)
.c:

lib-errori.o lib-corso-reti.o
@echo compiling $< with rule 1
gcc $< -g -O0 Werror -o $@ lib-errori.o lib-corso-reti.o

lib-errori: lib-errori.c
gcc -g -O0 -Werror -c lib-errori.c
lib-corso-reti: lib-corso-reti.c
gcc -g -O0 -Werror -c lib-corso-reti.c
clean:
rm -f $(ALL)
rm -f *~
rm -f *.o

Esempio

Autunno 2007
LABORATORIO

10

robdep@zircone:~/Corsi/Reti/C> gmake
gcc -g -O0 -Werror -c lib-errori.c
gcc -g -O0 -Werror -c lib-corso-reti.c
compiling daytimesrv.c with rule 1
gcc daytimesrv.c -g -O0 -Werror -o daytimesrv lib-errori.o
lib-corso-reti.o
compiling daytimecli.c with rule 1
gcc daytimecli.c -g -O0 -Werror -o daytimecli lib-errori.o
lib-corso-reti.o
compiling daytimesrv-ric.c with rule 1
gcc daytimesrv-ric.c -g -O0 -Werror -o daytimesrv-ric liberrori.o lib-corso-reti.o
compiling echosrv.c with rule 1
gcc echosrv.c -g -O0 -Werror -o echosrv lib-errori.o libcorso-reti.o

Librerie e include file

Autunno 2007
LABORATORIO

11

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

Autunno 2007

Basic.h
#ifndef __BASIC__
#define __BASIC__
#include
<sys/types.h>
#include
<sys/socket.h>
#include
<sys/time.h>
#include
<time.h>
#include
<netinet/in.h>
#include
<arpa/inet.h>
#include
<errno.h>

LABORATORIO

12

/* basic system data types */


/* basic socket definitions */
/* timeval{} for select() */
/* timespec{} for pselect() */
/* sockaddr_in{} and other Internet defns */
/* inet(3) functions */

#include
#include
#include
#define
#define
#define
#define

<unistd.h>
<sys/wait.h>
<sys/un.h>

/* for Unix domain sockets */

MAXLINE 256
PORT 12345
BACKLOG 5
MAX(a, b) ((a) > (b) ? (a) : (b))

#endif

Libreria gestioni errori

Autunno 2007
LABORATORIO

13

 Definisce varie funzioni per la gestione degli errori


 Facciamo il link con questa libreria per usare tali
funzioni
 Sono funzioni che stampano un messaggio di
errore
 Alcune terminano lesecuzione del programma

 File lib-errori.c
 err_msg
 err_quit, err_sys

Libreria corso reti

stampa solo lerrore


chiamano exit

Autunno 2007
LABORATORIO

14

 Definisce varie funzioni per la lettura e scrittura dei socket


 Convezione sul nome
 reti_nomefunzione

 Esempi di funzioni della libreria


 reti_readn

 Legge esattamente n byte

 reti_writen

 Scrive esattamente n byte

 reti_readline

 Legge una riga

 Dettagli nel file lib-corso-reti.c

Corso di laurea in Informatica

Reti di Calcolatori
A.A. 2007-2008

Prof. Roberto De Prisco

Socket TCP

Lezione

Socket

Autunno 2007
LABORATORIO

16

 Letteralmente significa presa (di corrente)


 lastrazione 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 sullaltro host

Autunno 2007

Funzioni per i socket


Tipica interazione in una
connessione TCP

LABORATORIO

17

socket()
bind()

CLIENT

socket()

accept()
Aspetta una
connessione

connect()
write()

Stabilisce una connessione

read()

Dati (richiesta)

read()

Dati (risposta)

close()
Notificazione di fine comunicazione

SERVER

listen()

write()

read()
close()

Autunno 2007

Sockaddr_in
struct in_addr {
in_addr_t
}

s_addr;

struct sockaddr_in {
uint8_t
sin_len;
sa_family_t
sin_family;
in_port_t
sin_port;
struct in_addr
sin_addr;
char
sin_zero[8];
}
struct sockaddr {
uint8_t
sa_family_t
char
}

sin_len;
sin_family;
sa_data[14];

LABORATORIO

18

/* 32-bit, network byte ordered */

/* tipo di protocollo, AF_INET */


/* 16-bit, network byte ordered */
/* struttura indirizzo IP */

/* tipo di protocollo: AF_XXX */


/* 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

Lunghezze strutture socket

Funzione socket

Autunno 2007
LABORATORIO

19

Autunno 2007
LABORATORIO

20

#include <sys/socket.h>
int socket(int family, int type, int protocol );
Valore di ritorno: -1 se errore
un socket descriptor se OK

Socket descriptor come un file descriptor


 Sono presi dallo stesso insieme
 Se un intero usato come file descriptor non pu
essere usato come socket descriptor e viceversa
 Socket e file sono visti pi o meno allo stesso modo
 read, write, close sono le stesse funzioni dei file

10

Funzione socket

Autunno 2007
LABORATORIO

21

 int family

 Un intero che specifica quale famiglia di protocolli si


intende usare:






AF_INET
AF_INET6
AF_LOCAL
AF_ROUTE
altri

IPv4
IPv6
prot. locale (client e server sullo stesso host)
Sockets per routing

 int type

 Un intero che dice il tipo di socket


 SOCK_STREAM
 SOCK_DGRAM
 SOCK_RAW

per uno stream di dati (TCP)


per datagrammi (UDP)
per applicazioni dirette su IP

 int protocol

 0, tranne che per SOCK_RAW

Funzione connect

Autunno 2007
LABORATORIO

22

#include <sys/socket.h>
int connect(int sd, struct sockaddr *servaddr, socklen_t addrlen);
Valore di ritorno: -1 se errore, 0 se OK

 Permette ad un client di aprire una connessione


con il server
 Il kernel sceglie una porta effimera (e lindirizzo
IP)
 Nel caso di una connessione TCP viene fatto
lhandshaking, in caso di errore ritorna (errno)
 ETIMEDOUT
 ECONNREFUSED
 EHOSTUNREACH

11

Autunno 2007

Funzione bind

LABORATORIO

23

#include <sys/socket.h>
int bind(int sd, struct sockaddr*myaddr, socklen_t addrlen);
Valore di ritorno: -1 se errore, 0 se OK

 Permette ad un server di assegnare un indirizzo per il


server al socket
 Con TCP lindirizzo 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 lindirizzo IP quello wildcard (INADDR_ANY, 0) viene
usato quello designato come IP destinazione nel SYN del
client

Funzione listen

Autunno 2007
LABORATORIO

24

#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

12

Autunno 2007

Backlog

Server

LABORATORIO

25

CODA connessioni
completate
(stato ESTABLISHED)

accept
apertura
conn. completata

CODA connessioni
incomplete
(stato SYN_RCVD)
SYN apertura
connessione

connect dal client

La somma degli elementi in entrambe le


code non pu superare il backlog

Funzione accept

Autunno 2007
LABORATORIO

26

#include <sys/socket.h>
int accept(int sd, struct sockaddr*cliaddr, socklen_t addrlen);
Valore di ritorno: -1 se errore, socked descriptor se OK

 Permette ad un server di prendere la prima


connessione completata dalla coda
 Se non ce ne sono si blocca

 cliaddr un parametro valore-risultato


 In chiamata contiene il listening socket
 Al ritorno contiene il socket connesso al particolare
client

13

Daytime server

Autunno 2007
LABORATORIO

27

#include
"basic.h
daytimesrv.c
#include
<time.h>
int main(int argc, char **argv) {
pid_t
pid;
int
listenfd, connfd;
struct sockaddr_in
servaddr;
char
buff[MAXLINE];
time_t
ticks;
if (argc != 2) err_quit("usage: daytimesrv <porta>");
if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err_sys("socket error");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family
= AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port
= htons(atoi(argv[1]));
if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0)
err_sys("bind error");
if( listen(listenfd, 5) < 0 ) err_sys("listen error");
for ( ; ; ) {
if( (connfd = accept(listenfd, (struct sockaddr *) NULL, NULL)) < 0)
err_sys("accept error");
ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
write(connfd, buff, strlen(buff));
close(connfd);
}
}

Server iterativo

Autunno 2007
LABORATORIO

28

 Server iterativo, serve i client uno alla


volta

 Quando un client connesso il seguente


client deve aspettare

 Accettabile per server semplici come il


daytime

14

Autunno 2007

Funzione inet_pton

LABORATORIO

29

#include <sys/socket.h>
int inet_pton(int af, const char* stringa, void* dest);
Valore di ritorno: 0 se errore, > 0 se OK

 Trasforma un indirizzo IP da formato


presentazione a formato network

 Presentazione: stringa
 192.41.218.1

 Network: sequenza di bit


 11000000.00101001.110011010.00000001

Autunno 2007

Daytime client
#include
"basic.h"
int main(int argc, char **argv) {
int
sockfd, n;
char
recvline[MAXLINE + 1];
struct sockaddr_in
servaddr;

LABORATORIO

30
daytimecli.c

if (argc != 3) err_quit("usage: daytimecli <inidirrizzoIP> <porta>");


if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
err_sys("socket error");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(atoi(argv[2]));
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
err_quit("inet_pton error for %s", argv[1]);
if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
err_quit("connect error");
while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
recvline[n] = 0;
/* 0 finale richiesto dal C per le stringhe */
fputs(recvline, stdout);
}
exit(0);
}

15

Autunno 2007

Server ricorsivi

LABORATORIO

31

 Un server ricorsivo usa una copia di se


stesso per servire una richiesta
pid_t
int

pid;
listenfd, connfd;

listenfd = socket(.);
/* riempi la struttura sockaddr_in (es. numero di porta) */
bind(listenfd,.)
listen(listenfd, LISTENQ)
for ( ; ; ) {
connfd = accept(listenfd,);
if ( (pid = fork()) == 0) {
close(listenfd); /* figlio chiude il socket di ascolto */
DOIT(connfd);
/* serve la richiesta */
close(connfd);
/* chiude il socket */
exit(0);
/* il figlio termina */
}
close(connfd);
/* il padre chiude il socket della connessione */
}

Autunno 2007

Server iterativi

LABORATORIO

32

Client

Server
listensd

connect()

Richiesta di connessione

 Il server chiama accept()


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

Server
listensd

connect()

Connessione stabilita

connsd

16

Autunno 2007

Server ricorsivi
Client

LABORATORIO

33

Server
listensd
Connessione stabilita

connect()

connsd

padre

Server
listensd
connsd

figlio

 Il server chiama fork()


 Padre e figlio nel server condividono il socket

Autunno 2007

Server ricorsivi
Client

LABORATORIO

34

Server
listensd

connect()

connsd

Connessione stabilita

padre

Server
listensd

connsd
 Il padre chiude il socket della connessione

figlio

 Pu accettare nuove connessioni

 Il figlio chiude il socket per laccettazione di


nuove connessioni
 Pu gestire la connessione con il client

17

Getsockname e getpeername

Autunno 2007
LABORATORIO

35

#include <sys/socket.h>
int getsockname(int sd, struct sockaddr*localaddr, socklen_t addrlen);
int getpeername(int sd, struct sockaddr*remoteaddr, socklen_t addrlen);
Valore di ritorno: -1 se errore, socked descriptor se OK

 Ritornano

 lindirizzo locale associato al socket


 Lindirizzo dellaltro lato della connessione associata al socket

 Serve perch






Un client che non chiama bind non sa quale porta stata usata
Un client non sa lindirizzo IP usato se ci sono pi interfaccie
Una chiamata a bind con porta=0 assegna una porta effimera
Stessa cosa per lindirizzo IP (INADDR_ANY)
Dopo una exec si pu risalire agli indirizzi della connessione
 NB: un file descriptor rimane aperto quando si chiama exec

Echo server (1)


#include
#include

"basic.h"
"echo.h"

Autunno 2007
LABORATORIO

36

echosrv.c

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


pid_t
childpid;
int
listenfd, connfd;
struct sockaddr_in
servaddr, cliaddr;
socklen_t
cliaddr_len;
if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err_sys("socket error");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family
= AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port
= htons(PORT); /* daytime server */
if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0)
err_sys("bind error");
if( listen(listenfd, LISTENQ) < 0 )
err_sys("listen error");

18

Echo server (2)

Autunno 2007
LABORATORIO

37

for ( ; ; ) {
cliaddr_len = sizeof(cliaddr);
if( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddr_len)) < 0)
err_sys("accept error");
if( (childpid = fork()) == 0 ) {
close(listenfd);
str_echo(connfd);
exit(0);
}
close(connfd);
}
}
void str_echo(int sockfd) {
ssize_t
n;
char
line[MAXLINE];
for ( ; ; ) {
if ( (n = read(sockfd, line, MAXLINE)) == 0)
return;
/* connection closed by other end */
write(sockfd, line, n);
}
}

Echo client (1)


#include
"basic.h"
#include
"echo.h"
int main(int argc, char **argv) {
int
sockfd, n;
struct sockaddr_in
servaddr;

Autunno 2007
LABORATORIO

38
echocli.c

if (argc != 2)
err_quit("usage: echotcpcli <IPaddress>");
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err_sys("socket error");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
/* echo server */
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
err_quit("inet_pton error for %s", argv[1]);
if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
err_sys("connect error");
str_cli(stdin, sockfd); /* svolge tutto il lavoro del client */
exit(0);
}

19

Autunno 2007

Echo client (2)

LABORATORIO

39

void str_cli(FILE *fp, int sockfd) {


char sendline[MAXLINE], recvline[MAXLINE];
while (fgets(sendline, MAXLINE, fp) != NULL) {
reti_writen(sockfd, sendline, strlen(sendline));
if (reti_readline(sockfd, recvline, MAXLINE) == 0)
err_quit("str_cli: server terminated prematurely");
fputs(recvline, stdout);
}
}

Autunno 2007

Echo server

LABORATORIO

40

 Per semplicit facciamo girare server e client sulla stessa macchina


prompt > echoserver &
[1] 21130
prompt > netstat a
Proto RecvRecv-Q SendSend-Q
Tcp
0
LISTEN

Local address
0
*.9877

Foreign address
*.*

(state)

prompt > echoclient 127.0.0.1

 In unaltra finestra
prompt > netstat a
Proto RecvRecv-Q SendSend-Q
Tcp
0
Tcp
0
Tcp
0

Local address
Foreign address
0
localhost.9877
localhost.1052
0
localhost.1052
localhost.9877
0
*.9877
*.*

(state)
ESTABLISHED
ESTABLISHED
LISTEN

 A questo punto la connessione stabilita

20

Autunno 2007

Echo server
prompt > echoclient 127.0.0.1
Ciao server
Ciao server
Arrivederci
Arrivederci
^D
prompt >
prompt > netstat a | grep 9877
0
Tcp
0
Tcp
0
0

LABORATORIO

41
Digitata al terminale
Risposta del server
Digitata al terminale
Risposta del server
Digitata al terminale

localhost.1052
*.9877

localhost.9877
*.*

TIME_WAIT
LISTEN

 Il server ha chiuso il socket


 Il client nello stato di TIME_WAIT


Il lato che chiude la connessione rimane in questo stato per un certo


periodo (2MSL) per
1.
2.

Mantenere informazioni nel caso lultimo ACK viene perso e laltro lato
rispedisce lultimo FIN
Permettere a vecchi pacchetti di essere eliminati dalla rete in modo da non
farli interferire con successive connessioni

Echo server





Autunno 2007
LABORATORIO

42

Digitando ^D, il client termina chiamando exit


Il kernel chiude tutti i file descriptor, quindi anche i socket descriptor
Quindi il socket del client viene chiuso
La chiusura implica
 la spedizione di FIN al server
 La ricezione dellACK al FIN

 A questo punto il server nello stato CLOSE_WAIT mentre il client


nello stato FIN_WAIT_2
 La prima parte della chiusura di una connessione TCP conclusa
 Quando il server riceve il FIN nella readline che ritorna EOF e quindi
chiama exit
 I file descriptor vengono chiusi, quindi anche il socket ed un FIN viene
spedito al client
 A questo punto la conessione completamente terminata ed il client
va nello stato TIME_WAIT mentre il server ha chiuso la connessione
 Dopo un certo periodo (2 Maximum Segment Lifetime) il client chiude
la connessione

21

Segnale SIGCHLD

Autunno 2007
LABORATORIO

43

 In un server ricorsivo, il server crea un figlio per


gestire la connessione
 quando la connessione viene chiusa il figlio
termina
 Il sistema operativo manda un segnale di
SIGCHLD al padre e il figlio diventa zombie
 Zombie sono dei processi terminati per i quali
vengono mantenuti dei dati nel sistema operativo
 Zombie sono necessari per permettere al padre di
controllare il valore di uscita del processo e utilizzo
delle risorse del figlio (memoria, CPU, etc.)

 Ovviamente non vogliamo lasciare zombie


 Occorre scrivere un signal handler che chiama
wait

zombie

Autunno 2007
LABORATORIO

44

 Ognli client che termina lascia uno zombie


 <defunct> indica uno zombie
robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1
ciao
ciao
^D
robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1
pippo
pippo
^D
robdep@zaffiro:~/Corsi/Reti/C> ps
PID TTY
TIME
CMD
1077 pts/0
00:00:00 cat
22084 pts/2
00:00:00 bash
27162 pts/3
00:00:00 ssh
30007 pts/6
00:00:00 bash
30331 pts/11
00:00:00 bash
30761 pts/11
00:00:00 echosrv
30765 pts/11
00:00:00 echosrv <defunct>
30767 pts/11
00:00:00 echosrv <defunct>
30768 pts/6
00:00:00 ps

Il client viene ucciso

Il client viene ucciso

22

Signal handler

Autunno 2007
LABORATORIO

45

void sig_child(int signo) {


pid_t pid;
int stat;
while ( pid = waitpid(waitpid(-1,&stat,WNOHANG)) > 0) {
printf(Child %d terminated\
terminated\n,pid);
}
}

 Utilizzando il gestore di segnali si evitano i


processi zombie
 Appena il figlio finisce viene chiamata waitpid
Prompt > echoserver &
[2] 19287
prompt > echoclient 127.0.0.1
Ciao server
Ciao server
^D
Child 19293 terminated
accept error: interrupted system call

Interruzione delle system call

Autunno 2007
LABORATORIO

46

 Il segnale stato catturato dal padre durante


lesecuzione 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 lesecuzione
 Occorre tener presente questo problema
 In alcuni sistemi le system call sono
automaticamente richiamate in altri no

23

Una possibile soluzione

Autunno 2007
LABORATORIO

47

for ( ; ; ) {
clilen = sizeof(cliaddr);
if ( (connfd = accept(listenfd, &cliaddr, &clilen)) < 0) {
if (errno = EINTR)
continue;
else {
perror(accept error);
exit(1);
}
}
}

 Se la chiamata ad accept ritorna EINTR


 accept viene richiamata

 Se lerrore diverso da EINTR


 Si gestisce lerrore (nellesempio si chiama exit)

Reset connessione e accept

Autunno 2007
LABORATORIO

48

 Un altro errore tipico da gestire con accept il


reset della connessione prima della chiamata ad
accept
 La connessione diventa ESTABLISHED
 Il client spedisce un RST
 Il server chiama accept

 Accept ritorna un codice di errore


 ECONNABORTED

 Il server pu richiamare accept per la prossima


connessione

24

Terminazione del server

Autunno 2007
LABORATORIO

49

 Cosa succede se il server termina prematuramente?


Prompt > echoclient 127.0.0.1
Ciao
Ciao
Arrivederci
Il server non risponde (dipende dal codice)

Il server viene ucciso

 Al kill i socket descriptor vengono chiusi


 Un FIN viene spedito al client

 Il client spedisce Arrivederci al server


 permesso perch il client non ha chiuso il socket

 Il client chiama readline che ritorna EOF


 Non si aspetta di ricevere EOF quindi stampa il messaggio di
errore e termina

SIGPIPE

Autunno 2007
LABORATORIO

50

 Cosa succede se il client ignora lerrore 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 loperazione di write genera


lerrore di EPIPE
 Soluzione semplice, quando non si deve reagire
allerrore
1. Ignorare (SIG_IGN) il segnale di SIGPIPE

 Assume che non occorre fare niente di speciale in tale circostanza

2. Controllare lerrore di EPIPE sulle write e nel caso di errore


terminare (non scrivere pi)

25

Macchina server non raggiungibile

Autunno 2007
LABORATORIO

51

 Unaltra 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 lACK fino ad
un certo timeout
 La funzione di lettura dal socket ritorna un errore
 ETIMEOUT
 EHOSTUNREACH, ENETUNREACH

Server shutdown and reboot

Autunno 2007
LABORATORIO

52

 La connessione viene stabilita


 Il server va gi e fa il reboot senza che il client
se ne accorga
 Non c comunicazione durante lo shutdown (server
scollegato dalla rete altrimenti spedisce FIN)

 Il client spedisce nuovi dati al server dopo il


reboot
 Il server non ha pi il socket aperto
 TCP risponde ai dati con un RST

 Client in readline quando riceve RST


 Readline ritorna ECONNRESET

26

Server somma

Autunno 2007
LABORATORIO

53

 Solo la funziona che gestisce il client


void server_somma(int sockfd) {
int
i, arg1, arg2;
ssize_t
n;
char
sendline[MAXLINE], rcvline[MAXLINE];
char c;
for ( ; ; ) {
if ( (n = reti_readline(sockfd, rcvline, MAXLINE)) == 0)
return;
/* connection closed by other end */
/* legge dalla stringa passata dal client i due interi
da sommare */
if( sscanf(rcvline, "%d %d", &arg1, &arg2) == 2 )
/* converte il risultato in stringa e lo scrive nel
buffer */
sprintf(sendline, "%d\
"%d\n", arg1 + arg2);
else
sprintf(sendline, "input error\
error\n");
n = strlen(sendline);
reti_writen(sockfd, sendline, n);
}
}

Client somma

sommasrv.c

Autunno 2007
LABORATORIO

54

 Il codice del client somma un p pi complesso


 Deve gestire due input
 I dati in arrivo dal socket
 I dati digitati dallutente alla tastiera

 Questo problema verr affrontato in seguito


 IO multiplexing
 Select

 Il codice disponibile sulla pagina Web


 sommacli.c

27

Autunno 2007

Problema

LABORATORIO

55

 Client e server, stesso tipo di macchina


sunos5 > sommacli 206.62.226.33
11 22
33
-11 -44
-55

 Client e server, macchine di tipo diverso


 Una Sparc laltra Intel
bsdi > sommacli 206.62.226.33
11 22
33
-11 -44
-16542537

 Sparc: big-endian, Intel: little-endian

Corso di laurea in Informatica

Reti di Calcolatori
A.A. 2007-2008

Prof. Roberto De Prisco

Socket UDP

Lezione

28

Autunno 2007

UDP

LABORATORIO

57

 TCP
 Trasporto orientato alla connessione, affidabile

 UDP
 Senza connessione, inaffidabile

 Ci sono situazione in cui sensato usare UDP


 Esempi
 DNS
 NFS
 SNMP

Autunno 2007

Funzioni per i socket


bind()

socket()

recvfrom()

Aspetta un
datagram

Dati (richiesta)

recvfrom()
Aspetta un
datagram

close()

SERVER

CLIENT

58
socket()

Tipica interazione per il


protocollo UDP

sendto()

LABORATORIO

sendto()
Dati (risposta)

close()

29

Autunno 2007

Spedire 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,
struct sockaddr* to, socklen_t len);

LABORATORIO

59

const

Valore di ritorno: -1 se errore, byte letti o scritti se OK


 sd, buf e nbytes

 Il socket descriptor, i dati da scrivere o il buffer in cui leggere e la


lunghezza dei dati/buffer

 flags = 0, per ora

 (vedremo a che serve con le funzioni di I/O avanzato)

 from o to, len

 Specificano la struttura che descrive il socket, il from e len verranno


scritti dalla funzione
 Se sono nulli inizialmente significa che non siamo interessati a saperli e non
verranno scritti

 Simili agli ultimi due parametri di accept

Autunno 2007

Server echo con UDP


int main(int argc, char **argv) {
int
sockfd;
struct sockaddr_in
servaddr, cliaddr;
if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )
err_sys("socket error");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family
= AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port
= htons(PORT);

LABORATORIO

60
echoudpsrv.c

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


err_sys("bind error");
server_echo_udp(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));
}
void server_echo_udp(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen) {
int
n;
socklen_t
len;
char
mesg[MAXLINE];
for ( ; ; ) {
len = clilen;
if( (n = recvfrom(sockfd, mesg, MAXLINE, 0, p_cliaddr, &len)) < 0)
err_sys("recvfrom error");
if( sendto(sockfd, mesg, n, 0, p_cliaddr, len) != n )
err_sys("sendto error");
}
}

30

Autunno 2007

Client echo con UDP (1)


#include
#include

LABORATORIO

61

"basic.h"
"echo.h"

echoudpcli.c

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


int
struct sockaddr_in

sockfd;
servaddr;

if (argc != 2)
err_quit("usage: udpclient <IPaddress>");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )
err_sys("socket error");
client_echo_udp(stdin, sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
exit(0);
}

Autunno 2007

Client echo con UDP (2)


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

LABORATORIO

62
socklen_t servlen) {

p_replyaddr = malloc(servlen);
while (fgets(sendline, MAXLINE, fp) != NULL) {
sendto(sockfd, sendline, strlen(sendline), 0, p_servaddr, servlen);
len = servlen;
if( (n = recvfrom(sockfd, recvline, MAXLINE, 0, p_replyaddr, &len)) < 0 )
err_sys("recvfrom error");
if( (len != servlen) || memcmp(p_servaddr, p_replyaddr, len) != 0 ) {
struct sockaddr_in *sin = (struct sockaddr_in *) p_replyaddr;
err_msg("risposta da %s ignorata\
ignorata\n",
inet_ntop(AF_INET, &sin&sin->sin_addr, buff, sizeof(buff)));
continue;
}
recvline[n] = 0;
fputs(recvline, stdout);
}
}

31

Controllo sul mittente

Autunno 2007
LABORATORIO

63

 Il client controlla che il datagram di risposta


venga dal server
 Infatti potrebbe ricevere un qualsiasi altro
datagram
 Tale datagram sarebbe interpratato come la risposta
del server

 Esercizio
 Provare a creare una situazione del genere
 Sul sito c il codice di spedisce_dg.c
 Permette di spedire un datagram verso una porta UDP

Datagrammi perduti

Autunno 2007
LABORATORIO

64

 Cosa succede se un datagram si perde?


 Per esempio un router lo butta via

 Chi lo sta aspettando (server o client) rimane


bloccato in attesa
 Per evitare questo problema si pu usare un
timeout
 In alcuni casi non basta
 Non sappiamo se il messaggio del client non mai
arrivato al server oppure se la risposta del server non
arrivata al client
 In alcuni casi (es. transazioni bancarie) fa molta
differenza

32

Connect e UDP

Autunno 2007
LABORATORIO

65

 Sebbene UPD sia senza connessione possibile


chiamare la funzione connect su un socket UDP
 Non si crea una connessione (handshake TCP)
 Semplicemente il kernel memorizza lindirizzo IP
e la porta con cui si vuole comunicare
 Quindi dobbiamo distinguire tra
 Socket UDP connesso
 Socket UDP non connesso

Socket UDP connesso

Autunno 2007
LABORATORIO

66

 Non si pu specificare il destinatario: quello


specificato in connect
 Non si usa sendto ma write o send
 I pacchetti verrano automaticamente spediti allindirizzo
specificato nella chiamata a connect

 I datagram letti sono quelli che arrivano


dallindirizzo connesso
 Non si usa recvfrom, ma si usa read o readv
 Ci limita un server UDP a comunicare con un solo
client

 Errori asincroni possono essere controllati

 Un socket UDP non connesso non pu controllare errori


asincroni

33

Autunno 2007

connect

LABORATORIO

67

 possibili chiamare connect pi di una


volta
 Pu essere usato per
 cambiare lindirizzo con cui si vuol comunicare
 Disconnettere il socket (specificando
AF_UNSPEC come famiglia di protocolli nel
campo sin_family)
 Potrebbe ritornare lerrore EAFNOSUPPORT, ma
non un problema

UDP client versione connect

Autunno 2007
LABORATORIO

68

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

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

if( connect(sockfd, (struct sockaddr *) p_servaddr, servlen) < 0 )


err_sys("connect error");
while (fgets(sendline, MAXLINE, fp) != NULL) {
write(sockfd, sendline, strlen(sendline));
n = read(sockfd, recvline, MAXLINE);
recvline[n] = 0;
fputs(recvline, stdout);

/* null terminate */

}
}
echoudpcliechoudpcli-connect.c

34

Autunno 2007

Inaffidabilit di UDP

LABORATORIO

69

 UDP non d alcuna garanzia sulla


consegna dei datagram
 Consideriamo la seguente applicazione
client server UDP
 Il server riceve datagram e semplicemente li
conta
 Pu essere interrotto con CTRL-C, ce un gestore di
segnale che semplicemente stampa quanti datagram
sono stati ricevuti

 Il client spedisce un serie di pacchetti, senza


aspettare alcuna risposta

UDP echo server count (1)


#include
#include

"basic.h"
"echo.h"

Autunno 2007
LABORATORIO

70
echoudpcliechoudpcli-count.c

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


static void gestisci_interrupt(int signo);
int count = 0;
int main(int argc, char **argv) {
int
sockfd;
struct sockaddr_in
servaddr, cliaddr;
if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )
err_sys("socket error");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family
= AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port
= htons(PORT);
if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )
err_sys("bind error");
signal(SIGINT, gestisci_interrupt);
server_echo_udp_count(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));
}

35

Autunno 2007

UDP echo server count (2)

LABORATORIO

71

void server_echo_udp_count(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen) {


int
n;
socklen_t
len;
char
mesg[MAXLINE];
n = 240 * 1024;
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
for ( ; ; ) {
len = clilen;
recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
count++;
sleep(1); /* rallentiamo il server */
}
}
static void gestisci_interrupt(int signo) {
printf("\
%d\\n", count);
printf("\nDatagrams ricevuti: %d
exit(0);
}

Autunno 2007

UDP echo client count


#define NDG
#define DGLEN

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

LABORATORIO

72
echoudpcliechoudpcli-count.c

void client_echo_udp_count(FILE *fp, int sockfd, const struct sockaddr *pservaddr, socklen_t servlen) {
int
i;
char
sendline[MAXLINE];
for (i = 0; i < NDG; i++) {
sendto(sockfd, sendline, DGLEN, 0, pservaddr, servlen);
}
}

 Il client come gli altri


 Cambia solo la funzione che spedisce i datagram

 Cosa succede se usiamo questo client-server?

36

Autunno 2007

Errori non segnalati

LABORATORIO

73

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


Udp:
6686 packets received
2012 packets to unknown port received.
9674 packet receive errors
18634 packets sent
robdep@zircone:~/Corsi/Reti/C> echoudpsrvechoudpsrv-count
Client in unaltra shell; dopo un pCTRLpCTRL-C
Datagrams ricevuti: 11
robdep@zircone:~/Corsi/Reti/C> netstat -s | grep -C4 "Udp" | tail -5;
Udp:
7206 packets received
2012 packets to unknown port received.
11154 packet receive errors
20634 packets sent

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


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

Corso di laurea in Informatica

Reti di Calcolatori
A.A. 2007-2008

Prof. Roberto De Prisco

I/O Multiplexing

Lezione

37

Autunno 2007

Problema

LABORATORIO

75

 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

Autunno 2007

Modelli di I/O

LABORATORIO

76

 Vari modelli di Input/Output


1.
2.
3.
4.
5.

Blocking
Nonblocking
I/O multiplexing
Guidato dai segnali
Asincrono

sincroni

 Sincrono: il processo si blocca (quando chiama


loperazione di lettura) fino alla conclusione
delloperazione
 In una operazione di lettura da un canale di I/O possiamo
distinguere due fasi
1. Attesa per i dati da parte del kernel
2. Copia dei dati dal kernel al processo che deve usarli

38

Autunno 2007

Blocking I/O

LABORATORIO

77

applicazione

kernel
System call

Recvfrom

Non ci sono datagram pronti

BLOCCATA

FASE 1:
attesa

datagram pronto
Copia datagram

FASE 2:
copia
Processo continua (elabora il
datagram)

Ritorna OK

Copia completata

Autunno 2007

Nonblocking I/O

LABORATORIO

78

applicazione

kernel
System call

Recvfrom
EWOULDBLOCK
Recvfrom

Non ci sono datagram pronti

System call

FASE 1:
attesa

EWOULDBLOCK
System call

Recvfrom

datagram pronto

BLOCCATA

Copia datagram

Processo continua (elabora il


datagram)

FASE 2:
copia
Ritorna OK

Copia completata

39

Autunno 2007

I/O multiplexing

LABORATORIO

79

applicazione

kernel
System call

Non ci sono datagram pronti

BLOCCATA

select

FASE 1:
attesa
Ritorna pronto

Recvfrom

datagram pronto
Copia datagram

BLOCCATA

System call

FASE 2:
copia
Ritorna OK

Copia completata

Processo continua (elabora il


datagram)

Autunno 2007

I/O guidato dai segnali


applicazione

LABORATORIO

80

kernel
System call

signal

Non ci sono datagram pronti

FASE 1:
attesa
SEGNALE

datagram pronto
System call

Copia datagram

BLOCCATA

GESTORE SEGNALE
Recvfrom

FASE 2:
copia
Ritorna OK

Processo continua (elabora il


datagram)

Copia completata

40

Autunno 2007

I/O asincrono

LABORATORIO

81

applicazione

kernel
System call

aio_read

Non ci sono datagram pronti

FASE 1:
attesa
datagram pronto
Copia datagram

FASE 2:
copia
GESTORE SEGNALE
Processo continua (elabora il
datagram)

SEGNALE

Copia completata

Funzione select

Autunno 2007
LABORATORIO

82

#include <sys/select.h>
#include <sys/time.h>
int select(int maxfd, fd_set readset, fd_set writeset,
fd_set exceptionset, const struct timeval *timeout);
Valore di ritorno: -1 se errore, 0 se timeout, numero di descrittori pronti

 Permette di aspettare che uno o pi file


descriptor siano pronti per essere letti
 Il timeout dato dalla struttura
struct timeval {
long
tv_sec;
long
tv_usec;
}

41

Parametri select

Autunno 2007
LABORATORIO

83

 Timeout
1. Puntatore nullo: aspetta senza timeout (fino a che un descrittore
pronto)
2. Struttura con un timeout non zero: aspetta fino al timeout, poi
ritorna anche se non ci sono descrittori pronti
 Anche se possiamo specificare i microsecondi alcuni kernel
arrotondano a multipli di 10 microsecondi
 Alcuni sistemi Linux modificano la struttura timeout (vale il tempo
rimanente al momento del ritorno)

3. Struttura con un timeout pari a 0: non aspettare, ritorna


immediatamente (polling)

 File descriptor da controllare


 Readset: pronti per la lettura
 Writeset: pronti per la scrittura
 Exceptionset: condizioni particolari
 Arrivo di dati fuori banda su un socket
 Informazioni di controllo da uno pseudo terminale

Parametri select

Autunno 2007
LABORATORIO

84

 Per descriveri gli insiemi si usa la struttura fd_set che


un insieme di bit
 void FD_ZERO(fd_set *fdset);
 Azzera la struttura fdset

 void FD_SET(int fd, fd_set *fdset);

 Mette a 1 il bit relativo al file descriptor fd

 void FD_CLR(int fd, fd_set *fdset);

 Mette a 0 il bit relativo al file descriptor fd

 int FD_ISSET(int fd, fd_set *fdset);

 Controlla se il bit relativo al file descriptor fd a 1

 La costante FD_SETSIZE (select.h) il numero di


descrittori in fd_set (solitamente 1024)
 maxfd: il numero massimo di descrittori effetivamente
usati
 Usato per efficienza dal kernel
 Es. se siamo interessati ai descrittori 1,4,7,9 maxfd deve valere
10 (i file descriptor iniziano da 0)

42

Descrittori pronti

Autunno 2007
LABORATORIO

85

 Quando un socket descriptor pronto per essere


usato?
 Socket in lettura
 Quando c almeno un byte da leggere
 La soglia si pu cambiare con le opzioni dei socket

 Il socket stato chiuso in lettura


 Es. stato ricevuto il FIN
 Loperazione di lettura ritorna EOF

 Il socket un listening socket e ci sono delle


connessioni completate
 C un errore
 Loperazione di lettura ritorner -1 e errno specificher lerrore

Descrittori pronti

Autunno 2007
LABORATORIO

86

 Socket in scrittura
 Il numero di byte di spazio disponibile nel buffer del
kernel maggiore di 2048
 La soglia si pu cambiare con le opzioni dei socket
 Loperazione di scrittura ritorna il numero di byte effettivamente
passati al livello di trasporto

 Il socket stato chiuso in scrittura


 Unoperazione di scrittura genera SIGPIPE

 C un errore
 Loperazione di scrittura ritorner -1 e errno specificher lerrore

 Eccezioni per socket


 Arrivo di dati fuori banda

43

Autunno 2007

echoclient versione select (1)


void client_echo_select(FILE *fp, int sockfd) {
int
maxfdl;
fd_set rset;
char
sendline[MAXLINE], recvline[MAXLINE];
int
n;

LABORATORIO

87
echocliechocli-slct.c

FD_ZERO(&rset);
for( ; ; ) {
FD_SET(fileno(fp), &rset);
FD_SET(sockfd, &rset);
maxfdl = MAX(fileno(fp), sockfd) + 1;
if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )
err_sys("select error");
if( FD_ISSET(sockfd, &rset) ) {
if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {
if( errno == EPIPE ) {
err_msg(%s [%d]: server disconnesso, __FILE__,__LINE__);
break;
}
else
err_sys("readline error");
}

echoclient versione select (2)

Autunno 2007
LABORATORIO

88

if (n == 0)
err_quit(%s [%d]: server disconnesso,__FILE__,__LINE__);
fputs(recvline, stdout);
}
if( FD_ISSET(fileno(fp), &rset) ) {
if( fgets(sendline, MAXLINE, fp) == NULL)
return;
if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)
err_sys("write error");
}
}
}

 Il client riesce a gestire sia linput da tastiera che linput


dal socket
 Se il server termina viene spedito un EOF sul socket
 Il client lo riceve e termina la connessione
 Senza select il client se ne sarebbe accorto dopo

44

Autunno 2007

echoclient e select

LABORATORIO

89

 Condizioni gestite da select in lettura su


stdin ed un socket
Client
stdin

Data o EOF

socket

TCP

RST

data

FIN

Autunno 2007

Stop-and-wait

LABORATORIO

90

 Il client opera in modalit stop-and-wait:


 Spedisce una linea di input e si blocca in attesa
della risposta del server echo
Tempo 0

Tempo 4

dati
echo
Tempo 1

Tempo 5

dati
echo
Tempo 2

Tempo 6

dati
echo
Tempo 3

Tempo 7

dati

S
C

echo

45

Autunno 2007

Batch input

LABORATORIO

91

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

Tempo 4

d1

Tempo 1

d2

d3

d4

d3

d2
r1

S
S

d6

d5

d4
r1

d3
r2

S
S

d6
r1

d5
r2

d4
r3

S
S

d7
r2

d6
r3

d5
r4

S
S

Tempo 6

d2

d1

d3

d2

Tempo 3

d4

Tempo 5

d1

Tempo 2

d5

d7

Tempo 7

d1

C
C

d8
r1

Shutdown della connessione

Autunno 2007
LABORATORIO

92

 Quando il client finisce di spedire non pu


chiudere il socket
 Ci possono esser ancora dati in arrivo

 Si deve chiudere il socket solo in scrittura e


lasciarlo aperto in lettura
 Spedire il FIN solo in una direzione
#include <sys/socket.h>
int shutdown(int sockfd, int howto);
Valore di ritorno: -1 se errore, 0 se OK
howto = SHUT_RD, SHUT_WR, SHUT_RDWR

46

echoclient versione shutdown


void client_echo_shutdown(FILE *fp, int sockfd) {
int
maxfdl, stdineof;
fd_set
rset;
char
sendline[MAXLINE], recvline[MAXLINE];
int
n;

Autunno 2007
LABORATORIO

93
echocliechocli-shtd.c

FD_ZERO(&rset);
for( ; ; ) {
FD_SET(fileno(fp), &rset);
FD_SET(sockfd, &rset);
maxfdl = MAX(fileno(fp), sockfd) + 1;
if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )
err_sys("select error");
if( FD_ISSET(sockfd, &rset) ) {
if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {
if( errno == EPIPE ) {
err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);
break;
}
else
err_sys("readline error");
}

echoclient versione shutdown

Autunno 2007
LABORATORIO

94

if (n == 0) {
if( stdineof == 1 )
return;
else {
err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);
exit(exit(-1);
}
}
fputs(recvline, stdout);
}
if( FD_ISSET(fileno(fp), &rset) ) {
if( fgets(sendline, MAXLINE, fp) == NULL) {
stdineof = 1;
shutdown(sockfd, SHUT_WR);
FD_CLR(fileno(fp), &rset);
continue;
}
if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)
err_sys("write error");
}
}
}

47

Autunno 2007

Select per il server

LABORATORIO

95

 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
client

-1

-1

rset
fd0

-1

fd1

fd2

fd3

fd4

fd5

-1

FD_SETSIZEFD_SETSIZE-1

Strutture dati per il server

Autunno 2007
LABORATORIO

96

 Il server crea il listening socket chiamando listen


 Supponiamo che gli standard file siano aperti e che il fd
ritornato da listen sia 3
 Tale informazione verr memorizzata in rset
fd0

rset

fd1

fd2

fd3

fd4

fd5

 Il server chiama select per leggere da tutti i socket


(file descriptor) aperti
 Allinizio c solo il il listening socket, su fd 3
 Quindi il parametro maxfd di select deve essere 4

48

Autunno 2007

Strutture dati per il server

LABORATORIO

97

 Quando un client stabilisce una connessione verr creato


un socket per la connessione con la funzione accept
 Supponiamo che il fd ritornato 4

client

1
2
3

FD_SETSIZEFD_SETSIZE-1

4
-1
-1

fd0

rset

fd1

fd2

fd3

fd4

fd5

-1

 Di nuovo il server chiama select per leggere da tutti i


socket (file descriptor) aperti
 Ora ci sono due socket, su fd 3 (listening) e fd 4 (connessione)
 Quindi il parametro maxfd di select deve essere 5

Autunno 2007

Strutture dati per il server

LABORATORIO

98

 Supponiamo che un altro client si connette, verr creato


un nuovo socket
 Supponiamo che il fd ritornato 5

client

1
2
3

FD_SETSIZEFD_SETSIZE-1

4
5
-1

fd0

rset

fd1

fd2

fd3

fd4

fd5

-1

 Di nuovo il server chiama select per leggere da tutti i


socket (file descriptor) aperti
 Ora ci sono tre socket, su fd 3 (listening), e fd 4 e fd 5 per le
due connessioni
 Quindi il parametro maxfd di select deve essere 6

49

Autunno 2007

Strutture dati per il server

LABORATORIO

99

 Supponiamo ora che la prima connessione (quella che


usa fd 4) venga chiusa
client

1
2
3

-1
5
-1
fd0

rset

FD_SETSIZEFD_SETSIZE-1

fd1

fd2

fd3

fd4

fd5

-1

 Il server chiama select per leggere da tutti i socket (file


descriptor) aperti
 Ora ci sono due socket, su fd 3 (listening), e fd 5 (connessione)
 Quindi il parametro maxfd di select deve essere 6

Echo server versione select (1)


int main(int argc, char **argv)
{
int
listenfd, connfd, sockfd;
int
i, maxi, maxfd;
int
ready, client[FD_SETSIZE];
char
buff[MAXLINE];
fd_set
rset, allset;
ssize_t
n;
struct sockaddr_in
servaddr, cliaddr;
socklen_t
cliaddr_len;

Autunno 2007
LABORATORIO

100
echosrvechosrv-slct.c

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


err_sys("socket error");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family
= AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port
= htons(PORT);
if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0)
err_sys("bind error");
if( listen(listenfd, BACKLOG) < 0 )
err_sys("listen error");

50

Echo server versione select (2)

Autunno 2007
LABORATORIO

101

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


maxi = -1;
for ( i = 0; i < FD_SETSIZE; i++) client[i] = -1; /* inizializza l'array client a -1 */
FD_ZERO(&allset); /* inizializza a zero tutti i descrittori */
FD_SET(listenfd, &allset); /* setta il descrittore di ascolto */
for ( ; ; ) {
rset = allset; /* insieme descrittori da controllare per la lettura */
if( (ready = select(maxfd+1, &rset, NULL, NULL, NULL)) < 0 )
err_sys("select error");
if( FD_ISSET(listenfd, &rset) ) { /* richiesta ricevuta dal listening socket */
cliaddr_len = sizeof(cliaddr);
if( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddr_len)) < 0)
err_sys("accept error");
for(i = 0; i < FD_SETSIZE; i++)
if( client[i] < 0 ) { /* cerca il primo posto libero per il nuovo il descrittore */
client[i] = connfd;
break;
}
if( i == FD_SETSIZE ) err_quit("troppi client");
FD_SET(connfd, &allset); /* setta connfd per la select */
if( connfd > maxfd ) maxfd = connfd; /* aggiorna maxfd */
/* aggiorna maxi */
if( i > maxi ) maxi = i;
if( --ready
--ready <= 0 )
continue; /* se non ci sono altri socket pronti riprendi da select */
}

Echo server versione select (3)

Autunno 2007
LABORATORIO

102

for( i = 0; i <= maxi; i++ ) {


/* controlla tutti i socket attivi per controllare se sono leggibili */
if( (sockfd = client[i]) < 0 )
continue;
if ( FD_ISSET(sockfd, &rset) ) {
/* se sockfd leggibile invoca la readline */
if ( (n = reti_readline(sockfd, buff, MAXLINE)) == 0) {
/* connessione chiusa dall'altro endpoint */
close(sockfd);
/* rimuovi sockfd dalla lista di socket
che la select deve controllare */
FD_CLR(sockfd, &allset);
client[i] = -1; /* cancella sockfd da client */
}
else
reti_writen(sockfd, buff, n);
if ( --ready
--ready <= 0 )
break;
}
}
}
}

51

Denial of Service Attack

Autunno 2007
LABORATORIO

103

 Consideriamo la seguente situazione che


pu accadere con il server visto poco fa
 Un client si connette, spedisce un solo byte
(che non sia un newline) e non fa pi nulla
 Il server chiama readline che legger il singolo
byte ma si bloccher nella prossima chiamata a
read in attesa di un newline
 Il server bloccato e nessun altro client
ricever il servizio

 Denial of Service Attack


 Un client riesce a far i modo che il server non
risponda pi ad altri client

Denial of Service Attack

Autunno 2007
LABORATORIO

104

 Il problema deriva dal fatto che il server si


blocca in una funzione relativa ad un client
mentre ci sono anche altri client
 Potrebbe andare bene se il server deve gestire
un solo client

 Possibili soluzioni
 Usare un singolo processo per ogni client
 Utilizzare un timeout sulle operazioni di I/O
 Usare I/O non-blocking

52

Autunno 2007

Esercizi

LABORATORIO

105

 Modificare echosrv-slct in modo che sia un


server somma
 Modificare echosrv-slct in modo che stampi
lindirizzo del client quando
 il client si connette
 Il client si disconnette

Corso di laurea in Informatica

Reti di Calcolatori
A.A. 2007-2008

Prof. Roberto De Prisco

Opzioni per i socket

Lezione

53

Socket options

Autunno 2007
LABORATORIO

107

 Ogni socket aperto ha delle propriet che ne


determinano alcuni comportamenti
 Possono essere cambiate: opzioni
 Lanologo 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)

Autunno 2007

Livelli

LABORATORIO

108

 Sono divise in vari livelli








SOL_SOCKET
IPPROTO_IP
IPPROTO_IPV6
IPPROTO_ICMPV6
IPPROTO_TCP

livello socket
livello IP
livello IP per la versione 6
livello messaggi di controllo
livello TCP

 Ogni livello ha varie opzioni


 Non detto che tutte siano supportate
 Il programma sockopts-check stampa i valori di
default delle opzioni supportate

54

Autunno 2007

Opzioni

Livello:
SOL_SOCKET

Livello:
IPPROTO_IP
Livello:
IPPROTO_TCP

LABORATORIO

109

SO_BROADCAST
SO_DEBUG
SO_DONTROUTE
SO_ERROR
SO_KEEPALIVE
SO_LINGER
SO_RCVBUF
SO_SNDBUF
SO_RCVLOWAT
SO_SNDLOWAT
SO_RCVTIMEO
SO_SNDTIMEO
SO_REUSEADDR
SO_REUSEPORT
SO_TYPE
SO_USELOOPBACK

permette il broadcast
abilita le informazioni di debug
salta il lookup nella tavola di routing
legge lerrore corrente
controlla che la connessione sia attiva
controlla la chiusura della connessione
grandezza del buffer in ricezione
grandezza buffer in spedizione
soglia per il buffer in ricezione
soglia per il buffer in spedizione
timeout per la ricezione
timeout per la spedizione
permette riutilizzo indirizzi locali
permette riutilizzo porte locali
il tipo di socket
per i socket di routing (copia i pacchetti)

IP_HDRINCL
IP_OPTIONS
IP_TTL

header incluso con i dati


opzioni nellheader IP
Time-To-Live

TCP_KEEPALIVE
TCP_MAXRT
TCP_MAXSEG
TCP_NODELAY

tempo per la ritrasmissione


tempo massimo per la ritrasmissione
MSS (Maximum Segment Size)
disabilita algoritmo di Nagle

getsockopt e setsockopt

Autunno 2007
LABORATORIO

110

#include <sys/socket.h>
int getsockopt(int sd, int level, int name, void *value, socklen_t *len);
int setsockopt(int sd, int level, int name, void *value, socklen_t len);
Valore di ritorno: -1 se errore, 0 se OK
Parametri:
setsockopt(3,SOL_SOCKET,SO_KEEPALIVE,1,sizeof(int));
attiva lopzione 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 lopzione
disabilitata, oppure diverso da 0 se lopzione abilitata

55

SO_BROADCAST

Autunno 2007
LABORATORIO

111

 Rende possibile la trasmissione broadcast


 Un applicazione che intende spedire in broadcast
deve abilitare questa opzione
 Serve a prevenire broadcast accidentali
 Es. un programma prende in input lindirizzo di
destinazione
 Lutente potrebbe inserire un indirizzo di broadcast
 Il programma dovrebbe controllare
 Se lopzione disabilitata il broadcast non viene fatto
comunque
 Viene ritornato lerrore EACCES

SO_ERROR

Autunno 2007
LABORATORIO

112

 Quando si verifica un errore, un codice di errore viene


memorizzato in una variabile chiamata so_error
 La notifica dellerrore 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
 Lintero ritornato il valore di so_error

 Pu essere solo letta, non scritta

56

Autunno 2007

SO_KEEPALIVE

LABORATORIO

113

 Quando abilitata per un socket TCP spedisce


un messaggio di controllo se non c scambio di
dati per un periodo di 2 ore
 Serve a mantenere attiva la connessione
 3 possibili casi

 ACK di risposta: la connessione ancora attiva


 RST di risposta: la connessione era stata chiusa,
viene generato lerrore ECONNRESET
 Non c risposta, vengono spediti 8 ulteriori messaggi
ogni 75 secondi
 Nessuna riposta dopo 11 minuti e 15 secondi: viene
generato lerrore ETIMEDOUT
 Messaggi ICMP di errore sulla rete, viene generato lerrore
EHOSTUNREACH

Autunno 2007

SO_LINGER

LABORATORIO

114

 Specifica il funzionamento della close per protocolli


orientati alla connessione (TCP)
struct linger {
int l_onoff;
int l_linger;

/* 0=off, nonzero=on */
/* tempo attesa in secondi */

 l_onoff = 0,

 close ritorna immediatamente ed il kernel spedir eventuali dati


residui

 l_onoff 0, l_linger = 0

 La connessione viene resettata, i dati residui non spediti


 Evita il TIME_WAIT, ma rischia la reincarnazione della
connessione

 l_onoff 0, l_linger 0

 Close non ritorna subito ma aspetta l_linger secondi affinch i


dati residui vengano spediti
 importante controllare il valore di ritorno perch dopo l_linger
secondi i dati vengono cancellati

57

SO_RCVBUF e SO_SNDBUF

Autunno 2007
LABORATORIO

115

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


dallapplicazione prima di spedirli
 SO_RCVBUF e SO_SNDBUF ci permettono di
cambiare la grandezza dei buffer

SO_RCVLOWAT e SO_SNDLOWAT

Autunno 2007
LABORATORIO

116

 Sono le soglie usate dalla funzione select


 Quando in ricezione sono presenti almeno un
numero di byte pari al low-watermark allora select
ritorna socket pronto in lettura
 Per TCP e UDP il default 1

 In modo simile per poter scrivere (socket pronto in


scrittura) i byte disponibili nel buffer di spedizione
deve essere almeno pari al low-watermark
 Il default 2048

58

SO_RCVTIMEO e SO_SNDTIMEO

Autunno 2007
LABORATORIO

117

 Ci permettono di usare un timeout per la


ricezione e la spedizione sul socket
struct timeval {
long tv_sec;
long tv_usec;
}

/* secondi */
/* 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

SO_TYPE

Autunno 2007
LABORATORIO

118

 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

59

Opzioni di IPv4

Autunno 2007
LABORATORIO

119

 IP_HDRINCL
 Se settata per un raw socket di IP allora lapplicazione
deve costruire anche lIP header che normalmente
scritto dal kernel

 IP_OPTIONS
 Permette di inserire le opzioni dellIP header

 IP_TTL
 Permette di specificare il Time-To-Live (in hops) del
datagram
 Il default 64

Opzioni di TCP

Autunno 2007
LABORATORIO

120

 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 dellMSS della connessione

 TCP_NODELAY
 Diabilita lalgoritmo di Nagle che diminuisce i dati spediti in caso di
congestione

60

Esempio di set e get sockopt


#include
"basic.h"
#include
<netinet/tcp.h>
int main(int argc, char **argv) {
int
sockfd, mss, sendbuff, ttl;
socklen_t
optlen;

Autunno 2007
LABORATORIO

121
sockoptssockopts-set.c

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


err_sys("socket error");
optlen = sizeof(ttl);
if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == -1 )
err_ret("getsockopt error");
printf("getsockopt: TTL = %d\
%d\n", ttl);
ttl = ttl + 16;
if( setsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == -1 )
err_ret("getsockopt error");
printf("TTL modificato\
modificato\n", ttl);
if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == -1 )
err_ret("getsockopt error");
printf("getsockopt: TTL = %d\
%d\n", ttl);
exit(0);
}

61