Upload
fabio
View
28
Download
4
Embed Size (px)
DESCRIPTION
riassunto di reti di calcolatori
Citation preview
1Prof. Roberto De Prisco
Reti di Calcolatori
Corso di laurea in Informatica
A.A. 2007-2008
Lezione
LABORATORIO
1111
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
2LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
3333Desktop grafico
Desktop grafico
Usare solo per scaricare gli esempi
Terminale testuale
CTRL-ALT-F1
CTRL-ALT-F6
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
4444Linea di comando
Terminale testuale Line di comando Prompt
Comandi ls, cd, pwd, cp, rm, mv, cat, mkdir man
robdep@zircone:~/Corsi/Reti/C> gmakegcc -g -O0 -Werror -c lib-errori.cgcc -g -O0 -Werror -c lib-corso-reti.ccompiling daytimesrv.c with rule 1
robdep@zircone:~/Corsi/Reti/C>
3LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
4LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
7777Programmi in C#include #include #include #include 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);
}}}}
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
5LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
10101010Esempio
robdep@zircone:~/Corsi/Reti/C> gmakegcc -g -O0 -Werror -c lib-errori.cgcc -g -O0 -Werror -c lib-corso-reti.ccompiling daytimesrv.c with rule 1gcc daytimesrv.c -g -O0 -Werror -o daytimesrv lib-errori.o lib-corso-reti.ocompiling daytimecli.c with rule 1gcc daytimecli.c -g -O0 -Werror -o daytimecli lib-errori.o lib-corso-reti.ocompiling daytimesrv-ric.c with rule 1gcc daytimesrv-ric.c -g -O0 -Werror -o daytimesrv-ric lib-errori.o lib-corso-reti.ocompiling echosrv.c with rule 1gcc echosrv.c -g -O0 -Werror -o echosrv lib-errori.o lib-corso-reti.o
6LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
11111111Librerie e include file
#include 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)
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
12121212Basic.h
#ifndef __BASIC__
#define __BASIC__
#include /* basic system data types */
#include /* basic socket definitions */
#include /* timeval{} for select() */
#include /* timespec{} for pselect() */
#include /* sockaddr_in{} and other Internet defns */
#include /* inet(3) functions */
#include
#include
#include
#include /* for Unix domain sockets */
#define MAXLINE 256
#define PORT 12345
#define BACKLOG 5
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
7LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 lesecuzione del programma
File lib-errori.c err_msg stampa solo lerrore err_quit, err_sys chiamano exit
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
8Prof. Roberto De Prisco
Reti di Calcolatori
Corso di laurea in Informatica
A.A. 2007-2008
Lezione
Socket TCP
2222
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
16161616Socket
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
9LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
10
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
19191919Lunghezze strutture socket
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
20202020Funzione socket
#include 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
11
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
22222222Funzione connect
#include 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
12
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
23232323Funzione bind#include 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
24242424Funzione listen
#include 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
13
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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)
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
26262626Funzione accept
#include 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
14
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
27272727Daytime server
#include "basic.h#include 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 ");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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
15
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
29292929Funzione inet_pton
#include 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 ");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) {recvline[n] = 0; /* 0 finale richiesto dal C per le stringhe */fputs(recvline, stdout);
}exit(0);
}
daytimecli.cdaytimecli.cdaytimecli.cdaytimecli.c
16
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 */
}
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 stabilita connsdconnsdconnsdconnsd
Il server chiama accept()
Viene creato un nuovo socket descriptor nel server per la connessione con questo particolare client
ClientClientClientClient ServerServerServerServer
ClientClientClientClient ServerServerServerServer
17
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 laccettazione di nuove connessioni Pu gestire la connessione con il client
ClientClientClientClient ServerServerServerServer
listensdlistensdlistensdlistensd
connsdconnsdconnsdconnsd
ServerServerServerServer
padrepadrepadrepadre
figliofigliofigliofiglio
18
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
35353535Getsockname e getpeername#include 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
19
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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);}
}
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 ");
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)
20
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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);
}}
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 unaltra finestra
21
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 lultimo ACK viene perso e laltro lato
rispedisce lultimo FIN2. 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 terminaleRisposta del serverRisposta del serverRisposta del serverRisposta del server
Digitata al terminaleDigitata al terminaleDigitata al terminaleDigitata al terminale
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 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
22
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 00:00:00 echosrv 00:00:00 echosrv 00:00:00 echosrv
30767 pts/11 30767 pts/11 30767 pts/11 30767 pts/11 00:00:00 echosrv 00:00:00 echosrv 00:00:00 echosrv 00:00:00 echosrv
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 indica uno zombie
23
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
46464646Interruzione delle system call
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
24
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 lerrore diverso da EINTR Si gestisce lerrore (nellesempio si chiama exit)
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
25
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
50505050SIGPIPE
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 allerrore1. Ignorare (SIG_IGN) il segnale di SIGPIPE
Assume che non occorre fare niente di speciale in tale circostanza2. Controllare lerrore di EPIPE sulle write e nel caso di errore
terminare (non scrivere pi)
26
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
51515151Macchina server non raggiungibile
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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
27
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 dallutente alla tastiera
Questo problema verr affrontato in seguito IO multiplexing Select
Il codice disponibile sulla pagina Web sommacli.c
28
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 laltra 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
29
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
30
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
59595959Spedire e ricevere datagrammi
#include 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
31
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 ");err_quit("usage: udpclient ");err_quit("usage: udpclient ");err_quit("usage: udpclient ");
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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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);
}}}}}}}}
32
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
33
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 lindirizzo IP e la porta con cui si vuole comunicare
Quindi dobbiamo distinguire tra Socket UDP connesso Socket UDP non connesso
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 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
34
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
67676767connect
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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
35
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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, ce un gestore di
segnale che semplicemente stampa quanti datagram sono stati ricevuti
Il client spedisce un serie di pacchetti, senza aspettare alcuna risposta
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
36
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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);
}}}}
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
37
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
73737373Errori non segnalatirobdep@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 unaltra shell; dopo un pCTRLClient in unaltra shell; dopo un pCTRLClient in unaltra shell; dopo un pCTRLClient in unaltra shell; dopo un pCTRL----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
38
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 loperazione di lettura) fino alla conclusione delloperazione
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
39
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
78787878Nonblocking I/O
applicazioneapplicazioneapplicazioneapplicazione kernelkernelkernelkernel
Recvfrom System callSystem callSystem callSystem callNon 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
40
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 prontoRitorna prontoRitorna prontoRitorna pronto
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
41
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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)
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 2007Autunno 2007Autunno 2007Autunno 2007
82828282Funzione select
#include #include 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 strutturastruct timeval {
long tv_sec;long tv_usec;
}
42
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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)
43
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 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
44
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 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
45
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 echoCCCC
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
46
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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 int shutdown(int sockfd, int howto);
Valore di ritorno: -1 se errore, 0 se OK
howto = SHUT_RD, SHUT_WR, SHUT_RDWR
47
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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");
}}}}
}}}}
}}}}
48
LABORATORIOLABORATORIOLABORATORIOLABORATORIO
Autunno 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