View
228
Download
0
Category
Preview:
Citation preview
1
Prof. Roberto De Prisco
Reti di Calcolatori
Corso di laurea in Informatica
A.A. 2007-2008
Lezione
LABORATORIO
1111
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
2222Obiettivo
� Saper scrivere semplici programmi per la comunicazione su una rete di calcolatori
� Assunzione� Sapete già programmare …
� Ambiente di sviluppo� LINUX� Compilatore C� Socket per la comunicazione in rete
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
3333Desktop grafico …
Desktop grafico
Usare solo per scaricare gli esempi
Terminale testuale
CTRL-ALT-F1 …
CTRL-ALT-F6
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
4444Linea di comando
� Terminale testuale� Line di comando� Prompt
� Comandi� ls, cd, pwd, cp, rm, mv, cat, mkdir� man <comando>
robdep@zircone:~/Corsi/Reti/C> gmake
gcc -g -O0 -Werror -c lib-errori.c
gcc -g -O0 -Werror -c lib-corso-reti.c
compiling daytimesrv.c with rule 1
… … … …
robdep@zircone:~/Corsi/Reti/C>
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
5555Shell
� Shell� È il programma che interpreta i comandi
� BASH� È la shell standard di Linux� Echo $SHELL
� Sezione risorse del Sito web� Link a pagine su Bash e altro (es. editor vi)
� Sito Web del corso LSO
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
6666File
� Editor di file� vi� emacs
� Occorre imparare ad usare uno di questi due editor� Basta il minimo indispensabile
� Manuali� Una ricerca su Internet vi fornirà numerosi fonti� “manuale editor vi”
2
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
7777Programmi in C
#include <stdio.h>#include <stdio.h>#include <stdio.h>#include <stdio.h>int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {
union {union {union {union {short s;short s;short s;short s;char c[sizeof(short)];char c[sizeof(short)];char c[sizeof(short)];char c[sizeof(short)];
} un;} un;} un;} un;un.s = 0x0102;un.s = 0x0102;un.s = 0x0102;un.s = 0x0102;printf("CPU = %s printf("CPU = %s printf("CPU = %s printf("CPU = %s ---- byte ordering: ",getenv("CPU"));byte ordering: ",getenv("CPU"));byte ordering: ",getenv("CPU"));byte ordering: ",getenv("CPU"));if (sizeof(short) == 2) {if (sizeof(short) == 2) {if (sizeof(short) == 2) {if (sizeof(short) == 2) {
if ( un.c[0] == 1 && un.c[1] == 2 ) if ( un.c[0] == 1 && un.c[1] == 2 ) if ( un.c[0] == 1 && un.c[1] == 2 ) if ( un.c[0] == 1 && un.c[1] == 2 ) printf ("bigprintf ("bigprintf ("bigprintf ("big----endianendianendianendian\\\\n");n");n");n");
else if ( un.c[0] == 2 && un.c[1] == 1 ) else if ( un.c[0] == 2 && un.c[1] == 1 ) else if ( un.c[0] == 2 && un.c[1] == 1 ) else if ( un.c[0] == 2 && un.c[1] == 1 ) printf ("littleprintf ("littleprintf ("littleprintf ("little----endianendianendianendian\\\\n");n");n");n");
elseelseelseelseprintf("unknownprintf("unknownprintf("unknownprintf("unknown\\\\n");n");n");n");
}}}}elseelseelseelse
printf("size of short: %d.printf("size of short: %d.printf("size of short: %d.printf("size of short: %d.\\\\n",sizeof(short));n",sizeof(short));n",sizeof(short));n",sizeof(short));exit(0);exit(0);exit(0);exit(0);
}}}}
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
8888Compilazione e Makefile
� Il sorgente C va compilato
� Compilare un programma consiste nel1.trasformare il sorgente C in codice oggetto2.unire tale codice oggetto con le librerie (link)
� gcc (GNU C Compiler)� gcc ls1.c� gcc –o ls1 ls1.c� gcc –Lmylibpath –lmylib –Imyincpath –O –DDEBUG ...
� Makefile� make ... fa tutto
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
9999Makefile
#Makefile
ALL = lib-errori lib-corso-reti \
daytimesrv daytimecli daytimesrv-ric \
echosrv echocli echosrv-sigh
all: $(ALL)
.c: lib-errori.o lib-corso-reti.o
@echo compiling $< with rule 1
gcc $< -g -O0 –Werror -o $@ lib-errori.o lib-corso-reti.o
lib-errori: lib-errori.c
gcc -g -O0 -Werror -c lib-errori.c
lib-corso-reti: lib-corso-reti.c
gcc -g -O0 -Werror -c lib-corso-reti.c
clean:
rm -f $(ALL)
rm -f *~
rm -f *.o
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
10101010Esempio
robdep@zircone:~/Corsi/Reti/C> gmake
gcc -g -O0 -Werror -c lib-errori.c
gcc -g -O0 -Werror -c lib-corso-reti.c
compiling daytimesrv.c with rule 1
gcc daytimesrv.c -g -O0 -Werror -o daytimesrv lib-errori.o
lib-corso-reti.o
compiling daytimecli.c with rule 1
gcc daytimecli.c -g -O0 -Werror -o daytimecli lib-errori.o
lib-corso-reti.o
compiling daytimesrv-ric.c with rule 1
gcc daytimesrv-ric.c -g -O0 -Werror -o daytimesrv-ric lib-
errori.o lib-corso-reti.o
compiling echosrv.c with rule 1
gcc echosrv.c -g -O0 -Werror -o echosrv lib-errori.o lib-
corso-reti.o
……
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
11111111Librerie e include file
� #include <nome.h>� Cerca il file da includere nelle directory di ricerca
standard del compilatore� /usr/include, /usr/lib/include, ...
� # include “nome.h”� Cerca il file da includere nella cwd
� Al momento di eseguire il link il compilatore cerca il codice necessario nelle librerie� librerie specificate con –l nel comando di compilazione� la ricerca di tali librerie è fatta in posti standard (/usr/lib,
/usr/local/lib, ...) e nelle directory specificate con –L� libreria di default (contiene printf)
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
12121212Basic.h
#ifndef __BASIC__
#define __BASIC__
#include <sys/types.h> /* basic system data types */
#include <sys/socket.h> /* basic socket definitions */
#include <sys/time.h> /* timeval{} for select() */
#include <time.h> /* timespec{} for pselect() */
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <arpa/inet.h> /* inet(3) functions */
#include <errno.h>
……
#include <unistd.h>
#include <sys/wait.h>
#include <sys/un.h> /* for Unix domain sockets */
#define MAXLINE 256
#define PORT 12345
#define BACKLOG 5
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
3
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
13131313Libreria gestioni errori
� Definisce varie funzioni per la gestione degli errori
� Facciamo il link con questa libreria per usare tali funzioni
� Sono funzioni che stampano un messaggio di errore� Alcune terminano l’esecuzione del programma
� File lib-errori.c� err_msg stampa solo l’errore� err_quit, err_sys chiamano exit
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
14141414Libreria corso reti
� Definisce varie funzioni per la lettura e scrittura dei socket
� Convezione sul nome� reti_nomefunzione
� Esempi di funzioni della libreria� reti_readn
� Legge esattamente n byte� reti_writen
� Scrive esattamente n byte� reti_readline
� Legge una riga
� Dettagli nel file lib-corso-reti.c
Prof. Roberto De Prisco
Reti di Calcolatori
Corso di laurea in Informatica
A.A. 2007-2008
Lezione
Socket TCP
2222
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
16161616Socket
� Letteralmente significa “presa” (di corrente)
� È l’astrazione di un canale di comunicazione fra due computer connessi da una rete
� Sono definiti per vari protocolli
� Per TCP/IP un socket identifica i due punti della connessione� Un indirizzo IP ed una porta su un host� Un indirizzo IP ed una porta sull’altro host
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
17171717Funzioni per i socket
socket()
bind()
listen()
accept()socket()
connect()
write()
read()
read()
write()
read()
close()
close()
Aspetta una connessione
Stabilisce una connessione
Dati (richiesta)
Notificazione di fine comunicazione
Dati (risposta)
CLIENT
CLIENT
CLIENT
CLIENT
SERVER
SERVER
SERVER
SERVER
Tipica interazione in una connessione TCP
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
18181818Sockaddr_in
struct in_addr {
in_addr_t s_addr; /* 32-bit, network byte ordered */
}
struct sockaddr_in {
uint8_t sin_len;
sa_family_t sin_family; /* tipo di protocollo, AF_INET */
in_port_t sin_port; /* 16-bit, network byte ordered */
struct in_addr sin_addr; /* struttura indirizzo IP */
char sin_zero[8];
}
struct sockaddr {
uint8_t sin_len;
sa_family_t sin_family; /* tipo di protocollo: AF_XXX */
char sa_data[14]; /* indirizzo specifico del protocollo */
}
� sin_zero� Utilizzata per far si che la grandezza della struttura sia almeno 16 byte
� sin_len� Non è richiesta dallo standard Posix� Esistono diverse strutture con grandezze differenti
4
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
19191919Lunghezze strutture socket LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
20202020Funzione socket
#include <sys/socket.h>int socket(int family, int type, int protocol );
Valore di ritorno: -1 se erroreun socket descriptor se OK
Socket descriptor è come un file descriptor� Sono presi dallo stesso insieme� Se un intero è usato come file descriptor non può
essere usato come socket descriptor e viceversa� Socket e file sono visti più o meno allo stesso modo
� read, write, close sono le stesse funzioni dei file
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
21212121Funzione socket
� int family� Un intero che specifica quale famiglia di protocolli si
intende usare:� AF_INET IPv4� AF_INET6 IPv6� AF_LOCAL prot. locale (client e server sullo stesso host)� AF_ROUTE Sockets per routing� altri …
� int type� Un intero che dice il tipo di socket
� SOCK_STREAM per uno stream di dati (TCP)� SOCK_DGRAM per datagrammi (UDP)� SOCK_RAW per applicazioni dirette su IP
� int protocol� 0, tranne che per SOCK_RAW
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
22222222Funzione connect
#include <sys/socket.h>int connect(int sd, struct sockaddr *servaddr, socklen_t addrlen);
Valore di ritorno: -1 se errore, 0 se OK
� Permette ad un client di aprire una connessione con il server
� Il kernel sceglie una porta effimera (e l’indirizzo IP)
� Nel caso di una connessione TCP viene fatto l’handshaking, in caso di errore ritorna (errno)� ETIMEDOUT� ECONNREFUSED� EHOSTUNREACH
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
23232323Funzione bind#include <sys/socket.h>int bind(int sd, struct sockaddr*myaddr, socklen_t addrlen);
Valore di ritorno: -1 se errore, 0 se OK
� Permette ad un server di assegnare un indirizzo per il server al socket
� Con TCP l’indirizzo può essere� indirizzo IP (deve essere una delle interfacce)� porta� entrambi� nessuno
� Se la porta non è specificata (valore 0) ne viene scelta una effimera
� Se l’indirizzo IP è quello wildcard (INADDR_ANY, 0) viene usato quello designato come IP destinazione nel SYN del client
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
24242424Funzione listen
#include <sys/socket.h>int listen(int sd, int backlog);
Valore di ritorno: -1 se errore, 0 se OK
� Usata solo da un server TCP, serve a
1. Convertire il socket da attivo a passivo, per far sì che il kernel accetti connessioni sul socket� Per default un socket è creato attivo, e il kernel si aspetta che sia
il socket di un client� Nel diagramma a stati TCP fa muovere da CLOSED a LISTEN
2. Backlog specifica quante connessioni accettare e mettere in attesa per essere servite
5
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
25252525Backlog
La somma degli elementi in entrambe le code non può superare il backlog
ServerServerServerServer acceptacceptacceptaccept
connect dal clientconnect dal clientconnect dal clientconnect dal client
SYN apertura connessione
apertura conn. completata
CODA connessioni completate
(stato ESTABLISHED)
CODA connessioni incomplete
(stato SYN_RCVD)
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
26262626Funzione accept
#include <sys/socket.h>int accept(int sd, struct sockaddr*cliaddr, socklen_t addrlen);
Valore di ritorno: -1 se errore, socked descriptor se OK
� Permette ad un server di prendere la prima connessione completata dalla coda � Se non ce ne sono si blocca
� cliaddr è un parametro valore-risultato� In chiamata contiene il listening socket� Al ritorno contiene il socket connesso al particolare
client
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
27272727Daytime server
#include "basic.h“#include <time.h>int main(int argc, char **argv) {
pid_t pid;int listenfd, connfd;struct sockaddr_in servaddr;char buff[MAXLINE];time_t ticks;if (argc != 2) err_quit("usage: daytimesrv <porta>");if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err_sys("socket error");bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(atoi(argv[1]));if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0)
err_sys("bind error");if( listen(listenfd, 5) < 0 ) err_sys("listen error");for ( ; ; ) {
if( (connfd = accept(listenfd, (struct sockaddr *) NULL, NULL)) < 0)
err_sys("accept error");ticks = time(NULL);snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));write(connfd, buff, strlen(buff));close(connfd);
}}
daytimesrv.c
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
28282828Server iterativo
� Server iterativo, serve i client uno alla volta
� Quando un client è connesso il seguente client deve aspettare
� Accettabile per server semplici come il daytime
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
29292929Funzione inet_pton
#include <sys/socket.h>int inet_pton(int af, const char* stringa, void* dest);
Valore di ritorno: ≤0 se errore, > 0 se OK
� Trasforma un indirizzo IP da formato “presentazione” a formato “network”
� Presentazione: stringa� “192.41.218.1”
� Network: sequenza di bit� 11000000.00101001.110011010.00000001
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
30303030Daytime client
#include "basic.h"int main(int argc, char **argv) {
int sockfd, n;char recvline[MAXLINE + 1];struct sockaddr_in servaddr;
if (argc != 3) err_quit("usage: daytimecli <inidirrizzoIP> <porta>");if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
err_sys("socket error");bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(atoi(argv[2]));
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)err_quit("inet_pton error for %s", argv[1]);
if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)err_quit("connect error");
while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {recvline[n] = 0; /* 0 finale richiesto dal C per le stringhe */fputs(recvline, stdout);
}exit(0);
}
daytimecli.cdaytimecli.cdaytimecli.cdaytimecli.c
6
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
31313131Server ricorsivi
� Un server ricorsivo usa una copia di se stesso per servire una richiesta
pid_t pid;int listenfd, connfd;
listenfd = socket(….);
/* riempi la struttura sockaddr_in (es. numero di porta) */
bind(listenfd,….)listen(listenfd, LISTENQ)
for ( ; ; ) {connfd = accept(listenfd,…);if ( (pid = fork()) == 0) {
close(listenfd); /* figlio chiude il socket di ascolto */DOIT(connfd); /* serve la richiesta */close(connfd); /* chiude il socket */exit(0); /* il figlio termina */
}close(connfd); /* il padre chiude il socket della connessione */
}
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
32323232Server iterativi
connect()connect()connect()connect()
listensdlistensdlistensdlistensd
Richiesta di connessioneRichiesta di connessioneRichiesta di connessioneRichiesta di connessione
connect()connect()connect()connect()
listensdlistensdlistensdlistensd
Connessione stabilitaConnessione stabilitaConnessione stabilitaConnessione stabilitaconnsdconnsdconnsdconnsd
� Il server chiama accept()
� Viene creato un nuovo socket descriptor nel server per la connessione con questo particolare client
ClientClientClientClient ServerServerServerServer
ClientClientClientClient ServerServerServerServer
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
33333333Server ricorsivi
connect()connect()connect()connect()
listensdlistensdlistensdlistensd
Connessione stabilitaConnessione stabilitaConnessione stabilitaConnessione stabilita
connsdconnsdconnsdconnsd
� Il server chiama fork()
� Padre e figlio nel server condividono il socket
ClientClientClientClient ServerServerServerServer
listensdlistensdlistensdlistensd
connsdconnsdconnsdconnsd
ServerServerServerServer
padrepadrepadrepadre
figliofigliofigliofiglio
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
34343434Server ricorsivi
connect()connect()connect()connect()
listensdlistensdlistensdlistensd
Connessione stabilitaConnessione stabilitaConnessione stabilitaConnessione stabilita
connsdconnsdconnsdconnsd
� Il padre chiude il socket della connessione� Può accettare nuove connessioni
� Il figlio chiude il socket per l’accettazione di nuove connessioni� Può gestire la connessione con il client
ClientClientClientClient ServerServerServerServer
listensdlistensdlistensdlistensd
connsdconnsdconnsdconnsd
ServerServerServerServer
padrepadrepadrepadre
figliofigliofigliofiglio
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
35353535Getsockname e getpeername#include <sys/socket.h>int getsockname(int sd, struct sockaddr*localaddr, socklen_t addrlen);int getpeername(int sd, struct sockaddr*remoteaddr, socklen_t addrlen);
Valore di ritorno: -1 se errore, socked descriptor se OK
� Ritornano� l’indirizzo locale associato al socket� L’indirizzo dell’altro lato della connessione associata al socket
� Serve perché� Un client che non chiama bind non sa quale porta è stata usata� Un client non sa l’indirizzo IP usato se ci sono più interfaccie� Una chiamata a bind con porta=0 assegna una porta effimera� Stessa cosa per l’indirizzo IP (INADDR_ANY)� Dopo una exec si può risalire agli indirizzi della connessione
� NB: un file descriptor rimane aperto quando si chiama exec
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
36363636Echo server (1)
#include "basic.h"#include "echo.h"
int main(int argc, char **argv) {pid_t childpid;int listenfd, connfd;struct sockaddr_in servaddr, cliaddr;socklen_t cliaddr_len;
if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)err_sys("socket error");
bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(PORT); /* daytime server */
if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0)err_sys("bind error");
if( listen(listenfd, LISTENQ) < 0 )err_sys("listen error");
echosrv.cechosrv.cechosrv.cechosrv.c
7
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
37373737Echo server (2)
for ( ; ; ) {cliaddr_len = sizeof(cliaddr);if( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddr_len)) < 0)
err_sys("accept error");
if( (childpid = fork()) == 0 ) {close(listenfd);str_echo(connfd);exit(0);
}close(connfd);
}}
void str_echo(int sockfd) {ssize_t n;char line[MAXLINE];for ( ; ; ) {
if ( (n = read(sockfd, line, MAXLINE)) == 0)return; /* connection closed by other end */
write(sockfd, line, n);}
}
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
38383838Echo client (1)
#include "basic.h"#include "echo.h"int main(int argc, char **argv) {
int sockfd, n;struct sockaddr_in servaddr;
if (argc != 2)err_quit("usage: echotcpcli <IPaddress>");
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)err_sys("socket error");
bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(PORT); /* echo server */if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
err_quit("inet_pton error for %s", argv[1]);
if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)err_sys("connect error");
str_cli(stdin, sockfd); /* svolge tutto il lavoro del client */exit(0);
}
echocli.cechocli.cechocli.cechocli.c
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
39393939Echo client (2)
void str_cli(FILE *fp, int sockfd) {char sendline[MAXLINE], recvline[MAXLINE];while (fgets(sendline, MAXLINE, fp) != NULL) {
reti_writen(sockfd, sendline, strlen(sendline));if (reti_readline(sockfd, recvline, MAXLINE) == 0)
err_quit("str_cli: server terminated prematurely");fputs(recvline, stdout);
}}
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
40404040Echo server
prompt > echoserver &prompt > echoserver &prompt > echoserver &prompt > echoserver &
[1] 21130 [1] 21130 [1] 21130 [1] 21130
prompt > netstat prompt > netstat prompt > netstat prompt > netstat ––––aaaa
Proto RecvProto RecvProto RecvProto Recv----Q SendQ SendQ SendQ Send----Q Local address Foreign address (state)Q Local address Foreign address (state)Q Local address Foreign address (state)Q Local address Foreign address (state)
TcpTcpTcpTcp 0000 0 *.98770 *.98770 *.98770 *.9877 *.**.**.**.*
LISTENLISTENLISTENLISTEN
prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1
� Per semplicità facciamo girare server e client sulla stessa macchina
� A questo punto la connessione è stabilita
prompt > netstat prompt > netstat prompt > netstat prompt > netstat ––––aaaa
Proto RecvProto RecvProto RecvProto Recv----Q SendQ SendQ SendQ Send----Q Local address Foreign addressQ Local address Foreign addressQ Local address Foreign addressQ Local address Foreign address (state)(state)(state)(state)
TcpTcpTcpTcp 0000 0 localhost.9877 localhost.1052 0 localhost.9877 localhost.1052 0 localhost.9877 localhost.1052 0 localhost.9877 localhost.1052 ESTABLISHEDESTABLISHEDESTABLISHEDESTABLISHED
TcpTcpTcpTcp 0000 0 localhost.1052 localhost.9877 0 localhost.1052 localhost.9877 0 localhost.1052 localhost.9877 0 localhost.1052 localhost.9877 ESTABLISHEDESTABLISHEDESTABLISHEDESTABLISHED
TcpTcpTcpTcp 0000 0 *.98770 *.98770 *.98770 *.9877 *.**.**.**.* LISTENLISTENLISTENLISTEN
� In un’altra finestra
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
41414141Echo server
� Il server ha chiuso il socket � Il client è nello stato di TIME_WAIT
� Il lato che chiude la connessione rimane in questo stato per un certo periodo (2MSL) per1. Mantenere informazioni nel caso l’ultimo ACK viene perso e l’altro lato
rispedisce l’ultimo FIN
2. Permettere a vecchi pacchetti di essere eliminati dalla rete in modo da non farli interferire con successive connessioni
prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1
Ciao serverCiao serverCiao serverCiao server
Ciao serverCiao serverCiao serverCiao server
ArrivederciArrivederciArrivederciArrivederci
ArrivederciArrivederciArrivederciArrivederci
^D^D^D^D
prompt >prompt >prompt >prompt >
prompt > netstat prompt > netstat prompt > netstat prompt > netstat ––––a | grep 9877a | grep 9877a | grep 9877a | grep 9877
TcpTcpTcpTcp 0000 0 localhost.1052 localhost.9877 0 localhost.1052 localhost.9877 0 localhost.1052 localhost.9877 0 localhost.1052 localhost.9877 TIME_WAITTIME_WAITTIME_WAITTIME_WAIT
TcpTcpTcpTcp 0000 0 *.98770 *.98770 *.98770 *.9877 *.**.**.**.* LISTENLISTENLISTENLISTEN
Digitata al terminaleDigitata al terminaleDigitata al terminaleDigitata al terminaleRisposta del serverRisposta del serverRisposta del serverRisposta del server
Digitata al terminaleDigitata al terminaleDigitata al terminaleDigitata al terminale
Risposta del serverRisposta del serverRisposta del serverRisposta del server
Digitata al terminaleDigitata al terminaleDigitata al terminaleDigitata al terminale
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
42424242Echo server
� Digitando ̂ D, il client termina chiamando exit� Il kernel chiude tutti i file descriptor, quindi anche i socket descriptor� Quindi il socket del client viene chiuso� La chiusura implica
� la spedizione di FIN al server� La ricezione dell’ACK al FIN
� A questo punto il server è nello stato CLOSE_WAIT mentre il client è nello stato FIN_WAIT_2
� La prima parte della chiusura di una connessione TCP è conclusa
� Quando il server riceve il FIN è nella readline che ritorna EOF e quindi chiama exit
� I file descriptor vengono chiusi, quindi anche il socket ed un FIN viene spedito al client
� A questo punto la conessione è completamente terminata ed il client va nello stato TIME_WAIT mentre il server ha chiuso la connessione
� Dopo un certo periodo (2 Maximum Segment Lifetime) il client chiude la connessione
8
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
43434343Segnale SIGCHLD
� In un server ricorsivo, il server crea un figlio per gestire la connessione
� quando la connessione viene chiusa il figlio termina
� Il sistema operativo manda un segnale di SIGCHLD al padre e il figlio diventa zombie� Zombie sono dei processi terminati per i quali
vengono mantenuti dei dati nel sistema operativo� Zombie sono necessari per permettere al padre di
controllare il valore di uscita del processo e utilizzo delle risorse del figlio (memoria, CPU, etc.)
� Ovviamente non vogliamo lasciare zombie� Occorre scrivere un signal handler che chiama
wait
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
44444444zombie
robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1
ciaociaociaociao
ciao ciao ciao ciao
^D^D^D^D
robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1robdep@zaffiro:~/Corsi/Reti/C> echocli 127.0.0.1
pippopippopippopippo
pippopippopippopippo
^D^D^D^D
robdep@zaffiro:~/Corsi/Reti/C> psrobdep@zaffiro:~/Corsi/Reti/C> psrobdep@zaffiro:~/Corsi/Reti/C> psrobdep@zaffiro:~/Corsi/Reti/C> ps
PID TTY PID TTY PID TTY PID TTY TIME CMDTIME CMDTIME CMDTIME CMD
1077 pts/0 1077 pts/0 1077 pts/0 1077 pts/0 00:00:00 cat00:00:00 cat00:00:00 cat00:00:00 cat
22084 pts/2 22084 pts/2 22084 pts/2 22084 pts/2 00:00:00 bash00:00:00 bash00:00:00 bash00:00:00 bash
27162 pts/3 27162 pts/3 27162 pts/3 27162 pts/3 00:00:00 ssh00:00:00 ssh00:00:00 ssh00:00:00 ssh
30007 pts/6 30007 pts/6 30007 pts/6 30007 pts/6 00:00:00 bash00:00:00 bash00:00:00 bash00:00:00 bash
30331 pts/11 30331 pts/11 30331 pts/11 30331 pts/11 00:00:00 bash00:00:00 bash00:00:00 bash00:00:00 bash
30761 pts/11 30761 pts/11 30761 pts/11 30761 pts/11 00:00:00 echosrv00:00:00 echosrv00:00:00 echosrv00:00:00 echosrv
30765 pts/11 30765 pts/11 30765 pts/11 30765 pts/11 00:00:00 echosrv <defunct>00:00:00 echosrv <defunct>00:00:00 echosrv <defunct>00:00:00 echosrv <defunct>
30767 pts/11 30767 pts/11 30767 pts/11 30767 pts/11 00:00:00 echosrv <defunct>00:00:00 echosrv <defunct>00:00:00 echosrv <defunct>00:00:00 echosrv <defunct>
30768 pts/6 30768 pts/6 30768 pts/6 30768 pts/6 00:00:00 ps00:00:00 ps00:00:00 ps00:00:00 ps
Il client viene uccisoIl client viene uccisoIl client viene uccisoIl client viene ucciso
Il client viene uccisoIl client viene uccisoIl client viene uccisoIl client viene ucciso
� Ognli client che termina lascia uno zombie� <defunct> indica uno zombie
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
45454545Signal handler
Prompt > echoserver &Prompt > echoserver &Prompt > echoserver &Prompt > echoserver &
[2] 19287[2] 19287[2] 19287[2] 19287
prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1prompt > echoclient 127.0.0.1
Ciao serverCiao serverCiao serverCiao server
Ciao serverCiao serverCiao serverCiao server
^D^D^D^D
Child 19293 terminatedChild 19293 terminatedChild 19293 terminatedChild 19293 terminated
accept error: interrupted system callaccept error: interrupted system callaccept error: interrupted system callaccept error: interrupted system call
void sig_child(int signo) {void sig_child(int signo) {void sig_child(int signo) {void sig_child(int signo) {
pid_t pid;pid_t pid;pid_t pid;pid_t pid;
int stat;int stat;int stat;int stat;
while ( pid = waitpid(while ( pid = waitpid(while ( pid = waitpid(while ( pid = waitpid(----1,&stat,WNOHANG)) > 0) {1,&stat,WNOHANG)) > 0) {1,&stat,WNOHANG)) > 0) {1,&stat,WNOHANG)) > 0) {
printf(“Child %d terminatedprintf(“Child %d terminatedprintf(“Child %d terminatedprintf(“Child %d terminated\\\\n”,pid);n”,pid);n”,pid);n”,pid);
}}}}
}}}}
� Utilizzando il gestore di segnali si evitano i processi zombie� Appena il figlio finisce viene chiamata waitpid
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
46464646Interruzione delle system call
� Il segnale è stato catturato dal padre durante l’esecuzione di accept
� Il gestore del segnale viene eseguito
� Poiché è stata interrotta la funzione accept ritorna con il codice di errore EINTR
� Poiché la gestione di tale errore non è prevista il server termina l’esecuzione
� Occorre tener presente questo problema� In alcuni sistemi le system call sono
automaticamente richiamate in altri no
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
47474747Una possibile soluzione
for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {
clilen = sizeof(cliaddr);clilen = sizeof(cliaddr);clilen = sizeof(cliaddr);clilen = sizeof(cliaddr);
if ( (connfd = accept(listenfd, &cliaddr, &clilen)) < 0) {if ( (connfd = accept(listenfd, &cliaddr, &clilen)) < 0) {if ( (connfd = accept(listenfd, &cliaddr, &clilen)) < 0) {if ( (connfd = accept(listenfd, &cliaddr, &clilen)) < 0) {
if (errno = EINTR)if (errno = EINTR)if (errno = EINTR)if (errno = EINTR)
continue;continue;continue;continue;
else {else {else {else {
perror(“accept error”);perror(“accept error”);perror(“accept error”);perror(“accept error”);
exit(1);exit(1);exit(1);exit(1);
}}}}
}}}}
}}}}
� Se la chiamata ad accept ritorna EINTR� accept viene richiamata
� Se l’errore è diverso da EINTR� Si gestisce l’errore (nell’esempio si chiama exit)
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
48484848Reset connessione e accept
� Un altro errore tipico da gestire con accept è il reset della connessione prima della chiamata ad accept� La connessione diventa ESTABLISHED� Il client spedisce un RST� Il server chiama accept
� Accept ritorna un codice di errore � ECONNABORTED
� Il server può richiamare accept per la prossima connessione
9
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
49494949Terminazione del server
� Cosa succede se il server termina prematuramente?
� Al kill i socket descriptor vengono chiusi� Un FIN viene spedito al client
� Il client spedisce “Arrivederci” al server� È permesso perché il client non ha chiuso il socket
� Il client chiama readline che ritorna EOF� Non si aspetta di ricevere EOF quindi stampa il messaggio di
errore e termina
Prompt > echoclient 127.0.0.1Prompt > echoclient 127.0.0.1Prompt > echoclient 127.0.0.1Prompt > echoclient 127.0.0.1
CiaoCiaoCiaoCiao
CiaoCiaoCiaoCiao
ArrivederciArrivederciArrivederciArrivederci
Il server non risponde (dipende dal codice)Il server non risponde (dipende dal codice)Il server non risponde (dipende dal codice)Il server non risponde (dipende dal codice)
Il server viene uccisoIl server viene uccisoIl server viene uccisoIl server viene ucciso
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
50505050SIGPIPE
� Cosa succede se il client ignora l’errore su readline e scrive nel socket?� Questo può capitare se il codice ha due write consecutive
� La prima fa sì che il server spedisca RST� La seconda crea il problema
� Viene generato un segnale di SIGPIPE� Il processo termina se il segnale non viene catturato o ignorato
� Se SIGPIPE è ignorato l’operazione di write genera l’errore di EPIPE
� Soluzione semplice, quando non si deve “reagire” all’errore1. Ignorare (SIG_IGN) il segnale di SIGPIPE
� Assume che non occorre fare niente di speciale in tale circostanza2. Controllare l’errore di EPIPE sulle write e nel caso di errore
terminare (non scrivere più)
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
51515151Macchina server non raggiungibile
� Un’altra possibile causa di errore è se la macchina server non risponde proprio� Diverso da uccidere il processo server (in quel caso
vengono spediti FIN, RST)� Può dipendere dalla rete� O dalla macchina server
� Il client è bloccato in readline
� TCP ritrasmetterà i dati per ricevere l’ACK fino ad un certo timeout
� La funzione di lettura dal socket ritorna un errore� ETIMEOUT� EHOSTUNREACH, ENETUNREACH
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
52525252Server shutdown and reboot
� La connessione viene stabilita
� Il server va giù e fa il reboot senza che il client se ne accorga� Non c’è comunicazione durante lo shutdown (server
scollegato dalla rete altrimenti spedisce FIN)
� Il client spedisce nuovi dati al server dopo il reboot� Il server non ha più il socket aperto� TCP risponde ai dati con un RST
� Client è in readline quando riceve RST� Readline ritorna ECONNRESET
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
53535353Server “somma”
� Solo la funziona che gestisce il clientvoid server_somma(int sockfd) {void server_somma(int sockfd) {void server_somma(int sockfd) {void server_somma(int sockfd) {
int i, arg1, arg2;int i, arg1, arg2;int i, arg1, arg2;int i, arg1, arg2;
ssize_t n;ssize_t n;ssize_t n;ssize_t n;
char sendline[MAXLINE], rcvline[MAXLINE];char sendline[MAXLINE], rcvline[MAXLINE];char sendline[MAXLINE], rcvline[MAXLINE];char sendline[MAXLINE], rcvline[MAXLINE];
char c;char c;char c;char c;
for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {
if ( (n = reti_readline(sockfd, rcvline, MAXLINE)) == 0)if ( (n = reti_readline(sockfd, rcvline, MAXLINE)) == 0)if ( (n = reti_readline(sockfd, rcvline, MAXLINE)) == 0)if ( (n = reti_readline(sockfd, rcvline, MAXLINE)) == 0)
return; /* connection closed by other end */return; /* connection closed by other end */return; /* connection closed by other end */return; /* connection closed by other end */
/* legge dalla stringa passata dal client i due interi /* legge dalla stringa passata dal client i due interi /* legge dalla stringa passata dal client i due interi /* legge dalla stringa passata dal client i due interi
da sommare */da sommare */da sommare */da sommare */
if( sscanf(rcvline, "%d %d", &arg1, &arg2) == 2 ) if( sscanf(rcvline, "%d %d", &arg1, &arg2) == 2 ) if( sscanf(rcvline, "%d %d", &arg1, &arg2) == 2 ) if( sscanf(rcvline, "%d %d", &arg1, &arg2) == 2 )
/* converte il risultato in stringa e lo scrive nel /* converte il risultato in stringa e lo scrive nel /* converte il risultato in stringa e lo scrive nel /* converte il risultato in stringa e lo scrive nel
buffer */buffer */buffer */buffer */
sprintf(sendline, "%dsprintf(sendline, "%dsprintf(sendline, "%dsprintf(sendline, "%d\\\\n", arg1 + arg2);n", arg1 + arg2);n", arg1 + arg2);n", arg1 + arg2);
else else else else
sprintf(sendline, "input errorsprintf(sendline, "input errorsprintf(sendline, "input errorsprintf(sendline, "input error\\\\n");n");n");n");
n = strlen(sendline);n = strlen(sendline);n = strlen(sendline);n = strlen(sendline);
reti_writen(sockfd, sendline, n);reti_writen(sockfd, sendline, n);reti_writen(sockfd, sendline, n);reti_writen(sockfd, sendline, n);
}}}}
}}}}
sommasrv.csommasrv.csommasrv.csommasrv.c
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
54545454Client “somma”
� Il codice del client somma è un pò più complesso
� Deve gestire due input� I dati in arrivo dal socket� I dati digitati dall’utente alla tastiera
� Questo problema verrà affrontato in seguito� IO multiplexing� Select
� Il codice è disponibile sulla pagina Web� sommacli.c
10
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
55555555Problema
sunos5 > sommacli 206.62.226.33sunos5 > sommacli 206.62.226.33sunos5 > sommacli 206.62.226.33sunos5 > sommacli 206.62.226.33
11 2211 2211 2211 22
33333333
----11 11 11 11 ----44444444
----55555555
bsdi > sommacli 206.62.226.33bsdi > sommacli 206.62.226.33bsdi > sommacli 206.62.226.33bsdi > sommacli 206.62.226.33
11 2211 2211 2211 22
33333333
----11 11 11 11 ----44444444
----16542537165425371654253716542537
� Client e server, stesso tipo di macchina
� Client e server, macchine di tipo diverso� Una Sparc l’altra Intel
� Sparc: big-endian, Intel: little-endian
Prof. Roberto De Prisco
Reti di Calcolatori
Corso di laurea in Informatica
A.A. 2007-2008
Lezione
Socket UDP
3333
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
57575757UDP
� TCP� Trasporto orientato alla connessione, affidabile
� UDP� Senza connessione, inaffidabile
� Ci sono situazione in cui è sensato usare UDP
� Esempi� DNS� NFS� SNMP
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
58585858Funzioni per i socket
socket()socket()socket()socket()
bind()bind()bind()bind()
recvfrom()recvfrom()recvfrom()recvfrom()socket()socket()socket()socket()
sendto()sendto()sendto()sendto()
recvfrom()recvfrom()recvfrom()recvfrom()
sendto()sendto()sendto()sendto()
close()close()close()close()close()close()close()close()
Aspetta un datagram
Dati (richiesta)
Dati (risposta)
CLIENT
CLIENT
CLIENT
CLIENT
SERVER
SERVER
SERVER
SERVER
Tipica interazione per il protocollo UDP
Aspetta un datagram
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
59595959Spedire e ricevere datagrammi
#include <sys/socket.h>int recvfrom(int sd, void* buf, int nbytes, int flags, struct
sockaddr* from, socklen_t *len);int sendto(int sd, const void* buf, int nbytes, int flags, const
struct sockaddr* to, socklen_t len);
Valore di ritorno: -1 se errore, byte letti o scritti se OK
� sd, buf e nbytes� Il socket descriptor, i dati da scrivere o il buffer in cui leggere e la
lunghezza dei dati/buffer� flags = 0, per ora
� (vedremo a che serve con le funzioni di I/O avanzato)� from o to, len
� Specificano la struttura che descrive il socket, il from e len verranno scritti dalla funzione� Se sono nulli inizialmente significa che non siamo interessati a saperli e non
verranno scritti� Simili agli ultimi due parametri di accept
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
60606060Server echo con UDP
int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {int sockfd;int sockfd;int sockfd;int sockfd;struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )
err_sys("socket error");err_sys("socket error");err_sys("socket error");err_sys("socket error");bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);
if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )err_sys("bind error");err_sys("bind error");err_sys("bind error");err_sys("bind error");
server_echo_udp(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));server_echo_udp(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));server_echo_udp(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));server_echo_udp(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));}}}}
void server_echo_udp(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen) {void server_echo_udp(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen) {void server_echo_udp(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen) {void server_echo_udp(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen) {int int int int n;n;n;n;socklen_t socklen_t socklen_t socklen_t len;len;len;len;char char char char mesg[MAXLINE];mesg[MAXLINE];mesg[MAXLINE];mesg[MAXLINE];for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {
len = clilen;len = clilen;len = clilen;len = clilen;if( (n = recvfrom(sockfd, mesg, MAXLINE, 0, p_cliaddr, &len)) < 0)if( (n = recvfrom(sockfd, mesg, MAXLINE, 0, p_cliaddr, &len)) < 0)if( (n = recvfrom(sockfd, mesg, MAXLINE, 0, p_cliaddr, &len)) < 0)if( (n = recvfrom(sockfd, mesg, MAXLINE, 0, p_cliaddr, &len)) < 0)
err_sys("recvfrom error");err_sys("recvfrom error");err_sys("recvfrom error");err_sys("recvfrom error");if( sendto(sockfd, mesg, n, 0, p_cliaddr, len) != n )if( sendto(sockfd, mesg, n, 0, p_cliaddr, len) != n )if( sendto(sockfd, mesg, n, 0, p_cliaddr, len) != n )if( sendto(sockfd, mesg, n, 0, p_cliaddr, len) != n )
err_sys("sendto error");err_sys("sendto error");err_sys("sendto error");err_sys("sendto error");}}}}
}}}}
echoudpsrv.cechoudpsrv.cechoudpsrv.cechoudpsrv.c
11
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
61616161Client echo con UDP (1)
#include "basic.h"#include "basic.h"#include "basic.h"#include "basic.h"#include "echo.h"#include "echo.h"#include "echo.h"#include "echo.h"
int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {int sockfd;int sockfd;int sockfd;int sockfd;
struct sockaddr_in servaddr;struct sockaddr_in servaddr;struct sockaddr_in servaddr;struct sockaddr_in servaddr;
if (argc != 2)if (argc != 2)if (argc != 2)if (argc != 2)err_quit("usage: udpclient <IPaddress>");err_quit("usage: udpclient <IPaddress>");err_quit("usage: udpclient <IPaddress>");err_quit("usage: udpclient <IPaddress>");
bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);inet_pton(AF_INET, argv[1], &servaddr.sin_addr);inet_pton(AF_INET, argv[1], &servaddr.sin_addr);inet_pton(AF_INET, argv[1], &servaddr.sin_addr);inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )err_sys("socket error");err_sys("socket error");err_sys("socket error");err_sys("socket error");
client_echo_udp(stdin, sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));client_echo_udp(stdin, sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));client_echo_udp(stdin, sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));client_echo_udp(stdin, sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
exit(0);exit(0);exit(0);exit(0);}}}}
echoudpcli.cechoudpcli.cechoudpcli.cechoudpcli.c
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
62626262Client echo con UDP (2)
void client_echo_udp(FILE *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) {void client_echo_udp(FILE *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) {void client_echo_udp(FILE *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) {void client_echo_udp(FILE *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) {int int int int n;n;n;n;char char char char sendline[MAXLINE], recvline[MAXLINE + 1];sendline[MAXLINE], recvline[MAXLINE + 1];sendline[MAXLINE], recvline[MAXLINE + 1];sendline[MAXLINE], recvline[MAXLINE + 1];char char char char buff[MAXLINE];buff[MAXLINE];buff[MAXLINE];buff[MAXLINE];socklen_t socklen_t socklen_t socklen_t len;len;len;len;struct sockaddr *p_replyaddr;struct sockaddr *p_replyaddr;struct sockaddr *p_replyaddr;struct sockaddr *p_replyaddr;
p_replyaddr = malloc(servlen);p_replyaddr = malloc(servlen);p_replyaddr = malloc(servlen);p_replyaddr = malloc(servlen);
while (fgets(sendline, MAXLINE, fp) != NULL) {while (fgets(sendline, MAXLINE, fp) != NULL) {while (fgets(sendline, MAXLINE, fp) != NULL) {while (fgets(sendline, MAXLINE, fp) != NULL) {sendto(sockfd, sendline, strlen(sendline), 0, p_servaddr, servlen);sendto(sockfd, sendline, strlen(sendline), 0, p_servaddr, servlen);sendto(sockfd, sendline, strlen(sendline), 0, p_servaddr, servlen);sendto(sockfd, sendline, strlen(sendline), 0, p_servaddr, servlen);len = servlen;len = servlen;len = servlen;len = servlen;
if( (n = recvfrom(sockfd, recvline, MAXLINE, 0, p_replyaddr, &len)) < 0 )if( (n = recvfrom(sockfd, recvline, MAXLINE, 0, p_replyaddr, &len)) < 0 )if( (n = recvfrom(sockfd, recvline, MAXLINE, 0, p_replyaddr, &len)) < 0 )if( (n = recvfrom(sockfd, recvline, MAXLINE, 0, p_replyaddr, &len)) < 0 )err_sys("recvfrom error");err_sys("recvfrom error");err_sys("recvfrom error");err_sys("recvfrom error");
if( (len != servlen) || memcmp(p_servaddr, p_replyaddr, len) != 0 ) {if( (len != servlen) || memcmp(p_servaddr, p_replyaddr, len) != 0 ) {if( (len != servlen) || memcmp(p_servaddr, p_replyaddr, len) != 0 ) {if( (len != servlen) || memcmp(p_servaddr, p_replyaddr, len) != 0 ) {struct sockaddr_in *sin = (struct sockaddr_in *) p_replyaddr;struct sockaddr_in *sin = (struct sockaddr_in *) p_replyaddr;struct sockaddr_in *sin = (struct sockaddr_in *) p_replyaddr;struct sockaddr_in *sin = (struct sockaddr_in *) p_replyaddr;err_msg("risposta da %s ignorataerr_msg("risposta da %s ignorataerr_msg("risposta da %s ignorataerr_msg("risposta da %s ignorata\\\\n", n", n", n",
inet_ntop(AF_INET, &sininet_ntop(AF_INET, &sininet_ntop(AF_INET, &sininet_ntop(AF_INET, &sin---->sin_addr, buff, sizeof(buff)));>sin_addr, buff, sizeof(buff)));>sin_addr, buff, sizeof(buff)));>sin_addr, buff, sizeof(buff)));continue;continue;continue;continue;
}}}}
recvline[n] = 0;recvline[n] = 0;recvline[n] = 0;recvline[n] = 0;fputs(recvline, stdout);fputs(recvline, stdout);fputs(recvline, stdout);fputs(recvline, stdout);
}}}}}}}}
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
63636363Controllo sul mittente
� Il client controlla che il datagram di risposta venga dal server
� Infatti potrebbe ricevere un qualsiasi altro datagram� Tale datagram sarebbe interpratato come la risposta
del server
� Esercizio� Provare a creare una situazione del genere� Sul sito c’è il codice di spedisce_dg.c
� Permette di spedire un datagram verso una porta UDP
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
64646464Datagrammi perduti
� Cosa succede se un datagram si perde?� Per esempio un router lo butta via
� Chi lo sta aspettando (server o client) rimane bloccato in attesa
� Per evitare questo problema si può usare un timeout� In alcuni casi non basta� Non sappiamo se il messaggio del client non è mai
arrivato al server oppure se la risposta del server non è arrivata al client
� In alcuni casi (es. transazioni bancarie) fa molta differenza
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
65656565Connect e UDP
� Sebbene UPD sia senza connessione è possibile chiamare la funzione connect su un socket UDP
� Non si crea una connessione (handshake TCP)
� Semplicemente il kernel memorizza l’indirizzo IP e la porta con cui si vuole comunicare
� Quindi dobbiamo distinguire tra� Socket UDP connesso� Socket UDP non connesso
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
66666666Socket UDP connesso
� Non si può specificare il destinatario: è quello specificato in connect� Non si usa sendto ma write o send� I pacchetti verrano automaticamente spediti all’indirizzo
specificato nella chiamata a connect
� I datagram letti sono quelli che arrivano dall’indirizzo “connesso”� Non si usa recvfrom, ma si usa read o readv� Ciò limita un server UDP a comunicare con un solo
client
� Errori asincroni possono essere controllati� Un socket UDP non connesso non può controllare errori
asincroni
12
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
67676767connect
� È possibili chiamare connect più di una volta
� Può essere usato per � cambiare l’indirizzo con cui si vuol comunicare� Disconnettere il socket (specificando
AF_UNSPEC come famiglia di protocolli nel campo sin_family)� Potrebbe ritornare l’errore EAFNOSUPPORT, ma
non è un problema
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
68686868UDP client – versione connect
void client_echo_udp_conn(FILE *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) {void client_echo_udp_conn(FILE *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) {void client_echo_udp_conn(FILE *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) {void client_echo_udp_conn(FILE *fp, int sockfd, const struct sockaddr *p_servaddr, socklen_t servlen) {
int n;int n;int n;int n;char sendline[MAXLINE], recvline[MAXLINE + 1];char sendline[MAXLINE], recvline[MAXLINE + 1];char sendline[MAXLINE], recvline[MAXLINE + 1];char sendline[MAXLINE], recvline[MAXLINE + 1];
if( connect(sockfd, (struct sockaddr *) p_servaddr, servlen) < 0 )if( connect(sockfd, (struct sockaddr *) p_servaddr, servlen) < 0 )if( connect(sockfd, (struct sockaddr *) p_servaddr, servlen) < 0 )if( connect(sockfd, (struct sockaddr *) p_servaddr, servlen) < 0 )err_sys("connect error");err_sys("connect error");err_sys("connect error");err_sys("connect error");
while (fgets(sendline, MAXLINE, fp) != NULL) {while (fgets(sendline, MAXLINE, fp) != NULL) {while (fgets(sendline, MAXLINE, fp) != NULL) {while (fgets(sendline, MAXLINE, fp) != NULL) {
write(sockfd, sendline, strlen(sendline));write(sockfd, sendline, strlen(sendline));write(sockfd, sendline, strlen(sendline));write(sockfd, sendline, strlen(sendline));
n = read(sockfd, recvline, MAXLINE);n = read(sockfd, recvline, MAXLINE);n = read(sockfd, recvline, MAXLINE);n = read(sockfd, recvline, MAXLINE);
recvline[n] = 0; /* null terminate */recvline[n] = 0; /* null terminate */recvline[n] = 0; /* null terminate */recvline[n] = 0; /* null terminate */fputs(recvline, stdout);fputs(recvline, stdout);fputs(recvline, stdout);fputs(recvline, stdout);
}}}}}}}}
echoudpcliechoudpcliechoudpcliechoudpcli----connect.cconnect.cconnect.cconnect.c
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
69696969Inaffidabilità di UDP
� UDP non dà alcuna garanzia sulla consegna dei datagram
� Consideriamo la seguente applicazione client server UDP� Il server riceve datagram e semplicemente li
conta� Può essere interrotto con CTRL-C, c’e’ un gestore di
segnale che semplicemente stampa quanti datagram sono stati ricevuti
� Il client spedisce un serie di pacchetti, senza aspettare alcuna risposta
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
70707070UDP echo server count (1)
#include "basic.h"#include "basic.h"#include "basic.h"#include "basic.h"#include "echo.h"#include "echo.h"#include "echo.h"#include "echo.h"
void server_echo_udp_count(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen);void server_echo_udp_count(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen);void server_echo_udp_count(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen);void server_echo_udp_count(int sockfd, struct sockaddr *p_cliaddr, socklen_t clilen);static void gestisci_interrupt(int signo); static void gestisci_interrupt(int signo); static void gestisci_interrupt(int signo); static void gestisci_interrupt(int signo);
int count = 0;int count = 0;int count = 0;int count = 0;
int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {int sockfd;int sockfd;int sockfd;int sockfd;struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;
if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )err_sys("socket error");err_sys("socket error");err_sys("socket error");err_sys("socket error");
bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);
if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )err_sys("bind error");err_sys("bind error");err_sys("bind error");err_sys("bind error");
signal(SIGINT, gestisci_interrupt);signal(SIGINT, gestisci_interrupt);signal(SIGINT, gestisci_interrupt);signal(SIGINT, gestisci_interrupt);
server_echo_udp_count(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));server_echo_udp_count(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));server_echo_udp_count(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));server_echo_udp_count(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));}}}}
echoudpcliechoudpcliechoudpcliechoudpcli----count.ccount.ccount.ccount.c
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
71717171UDP echo server count (2)
void server_echo_udp_count(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen) {void server_echo_udp_count(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen) {void server_echo_udp_count(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen) {void server_echo_udp_count(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen) {int int int int n;n;n;n;socklen_t socklen_t socklen_t socklen_t len;len;len;len;char char char char mesg[MAXLINE];mesg[MAXLINE];mesg[MAXLINE];mesg[MAXLINE];
n = 240 * 1024;n = 240 * 1024;n = 240 * 1024;n = 240 * 1024;setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {len = clilen;len = clilen;len = clilen;len = clilen;recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);count++;count++;count++;count++;sleep(1); /* rallentiamo il server */sleep(1); /* rallentiamo il server */sleep(1); /* rallentiamo il server */sleep(1); /* rallentiamo il server */
}}}}}}}}
static void gestisci_interrupt(int signo) {static void gestisci_interrupt(int signo) {static void gestisci_interrupt(int signo) {static void gestisci_interrupt(int signo) {printf("printf("printf("printf("\\\\nDatagrams ricevuti: %dnDatagrams ricevuti: %dnDatagrams ricevuti: %dnDatagrams ricevuti: %d\\\\n", count);n", count);n", count);n", count);exit(0);exit(0);exit(0);exit(0);
}}}}
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
72727272UDP echo client count
� Il client è come gli altri� Cambia solo la funzione che spedisce i datagram
� Cosa succede se usiamo questo client-server?
#define NDG 2000 /* #datagrams to send */#define NDG 2000 /* #datagrams to send */#define NDG 2000 /* #datagrams to send */#define NDG 2000 /* #datagrams to send */#define DGLEN 1400 /* length of each datagram */#define DGLEN 1400 /* length of each datagram */#define DGLEN 1400 /* length of each datagram */#define DGLEN 1400 /* length of each datagram */
void client_echo_udp_count(FILE *fp, int sockfd, const struct sockaddr *pservaddr, socklen_t servlen) {void client_echo_udp_count(FILE *fp, int sockfd, const struct sockaddr *pservaddr, socklen_t servlen) {void client_echo_udp_count(FILE *fp, int sockfd, const struct sockaddr *pservaddr, socklen_t servlen) {void client_echo_udp_count(FILE *fp, int sockfd, const struct sockaddr *pservaddr, socklen_t servlen) {int i;int i;int i;int i;char sendline[MAXLINE];char sendline[MAXLINE];char sendline[MAXLINE];char sendline[MAXLINE];
for (i = 0; i < NDG; i++) {for (i = 0; i < NDG; i++) {for (i = 0; i < NDG; i++) {for (i = 0; i < NDG; i++) {sendto(sockfd, sendline, DGLEN, 0, pservaddr, servlen);sendto(sockfd, sendline, DGLEN, 0, pservaddr, servlen);sendto(sockfd, sendline, DGLEN, 0, pservaddr, servlen);sendto(sockfd, sendline, DGLEN, 0, pservaddr, servlen);
}}}}}}}}
echoudpcliechoudpcliechoudpcliechoudpcli----count.ccount.ccount.ccount.c
13
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
73737373Errori non segnalati
robdep@zircone:~/Corsi/Reti/C> netstat robdep@zircone:~/Corsi/Reti/C> netstat robdep@zircone:~/Corsi/Reti/C> netstat robdep@zircone:~/Corsi/Reti/C> netstat ----s | grep s | grep s | grep s | grep ----C4 "Udp" | tail C4 "Udp" | tail C4 "Udp" | tail C4 "Udp" | tail ----5; 5; 5; 5;
Udp:Udp:Udp:Udp:
6686 packets received6686 packets received6686 packets received6686 packets received
2012 packets to unknown port received.2012 packets to unknown port received.2012 packets to unknown port received.2012 packets to unknown port received.
9674 packet receive errors9674 packet receive errors9674 packet receive errors9674 packet receive errors
18634 packets sent18634 packets sent18634 packets sent18634 packets sent
robdep@zircone:~/Corsi/Reti/C> echoudpsrvrobdep@zircone:~/Corsi/Reti/C> echoudpsrvrobdep@zircone:~/Corsi/Reti/C> echoudpsrvrobdep@zircone:~/Corsi/Reti/C> echoudpsrv----countcountcountcount
Datagrams ricevuti: 11 Datagrams ricevuti: 11 Datagrams ricevuti: 11 Datagrams ricevuti: 11
robdep@zircone:~/Corsi/Reti/C> netstat robdep@zircone:~/Corsi/Reti/C> netstat robdep@zircone:~/Corsi/Reti/C> netstat robdep@zircone:~/Corsi/Reti/C> netstat ----s | grep s | grep s | grep s | grep ----C4 "Udp" | tail C4 "Udp" | tail C4 "Udp" | tail C4 "Udp" | tail ----5; 5; 5; 5;
Udp:Udp:Udp:Udp:
7206 packets received7206 packets received7206 packets received7206 packets received
2012 packets to unknown port received.2012 packets to unknown port received.2012 packets to unknown port received.2012 packets to unknown port received.
11154 packet receive errors11154 packet receive errors11154 packet receive errors11154 packet receive errors
20634 packets sent20634 packets sent20634 packets sent20634 packets sent
Client in un’altra shell; dopo un pòCTRLClient in un’altra shell; dopo un pòCTRLClient in un’altra shell; dopo un pòCTRLClient in un’altra shell; dopo un pòCTRL----CCCC
robdep@zircone:~/Corsi/Reti/C> echoudpclirobdep@zircone:~/Corsi/Reti/C> echoudpclirobdep@zircone:~/Corsi/Reti/C> echoudpclirobdep@zircone:~/Corsi/Reti/C> echoudpcli----count 127.0.0.1count 127.0.0.1count 127.0.0.1count 127.0.0.1
robdep@zircone:~/Corsi/Reti/C> robdep@zircone:~/Corsi/Reti/C> robdep@zircone:~/Corsi/Reti/C> robdep@zircone:~/Corsi/Reti/C>
Prof. Roberto De Prisco
Reti di Calcolatori
Corso di laurea in Informatica
A.A. 2007-2008
Lezione
I/O Multiplexing
4444
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
75757575Problema
� Un programma deve gestire due input simultaneamente� Standard input (leggere da tastiera)� Un socket (leggere dal socket)
� Abbiamo visto un esempio in cui il client era bloccato a leggere da standard input� Non poteva leggere il FIN sul socket
� Normalmente una funzione di I/O si blocca se non ci sono dati da leggere
� Serve un modo per poter “aspettare” da più canali di input� Il primo che produce dati viene letto
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
76767676Modelli di I/O
� Vari modelli di Input/Output1. Blocking 2. Nonblocking3. I/O multiplexing4. Guidato dai segnali5. Asincrono
� Sincrono: il processo si blocca (quando chiama l’operazione di lettura) fino alla conclusione dell’operazione
� In una operazione di lettura da un canale di I/O possiamo distinguere due fasi1. Attesa per i dati da parte del kernel2. Copia dei dati dal kernel al processo che deve usarli
sincronisincronisincronisincroni
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
77777777Blocking I/O
applicazioneapplicazioneapplicazioneapplicazione kernelkernelkernelkernel
Recvfrom System callSystem callSystem callSystem call Non ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram pronti
datagram prontodatagram prontodatagram prontodatagram pronto
Copia datagramCopia datagramCopia datagramCopia datagram
Copia completataCopia completataCopia completataCopia completataProcesso continua (elabora il Processo continua (elabora il Processo continua (elabora il Processo continua (elabora il datagram)datagram)datagram)datagram)
BLOCCATA
BLOCCATA
BLOCCATA
BLOCCATA
Ritorna OKRitorna OKRitorna OKRitorna OK
FASE 1: FASE 1: FASE 1: FASE 1:
attesaattesaattesaattesa
FASE 2: FASE 2: FASE 2: FASE 2:
copiacopiacopiacopia
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
78787878Nonblocking I/O
applicazioneapplicazioneapplicazioneapplicazione kernelkernelkernelkernel
Recvfrom System callSystem callSystem callSystem call
Non ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram pronti
datagram prontodatagram prontodatagram prontodatagram pronto
Copia datagramCopia datagramCopia datagramCopia datagram
Copia completataCopia completataCopia completataCopia completataProcesso continua (elabora il Processo continua (elabora il Processo continua (elabora il Processo continua (elabora il datagram)datagram)datagram)datagram)
BLOCCATA
BLOCCATA
BLOCCATA
BLOCCATA
Ritorna OKRitorna OKRitorna OKRitorna OK
FASE 1: FASE 1: FASE 1: FASE 1:
attesaattesaattesaattesa
FASE 2: FASE 2: FASE 2: FASE 2:
copiacopiacopiacopia
EWOULDBLOCK
EWOULDBLOCK
System callSystem callSystem callSystem call
System callSystem callSystem callSystem call
Recvfrom
Recvfrom
14
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
79797979I/O multiplexing
applicazioneapplicazioneapplicazioneapplicazione kernelkernelkernelkernel
selectSystem callSystem callSystem callSystem call Non ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram pronti
datagram prontodatagram prontodatagram prontodatagram pronto
Copia datagramCopia datagramCopia datagramCopia datagram
Copia completataCopia completataCopia completataCopia completataProcesso continua (elabora il Processo continua (elabora il Processo continua (elabora il Processo continua (elabora il datagram)datagram)datagram)datagram)
BLOCCATA
BLOCCATA
BLOCCATA
BLOCCATA
Ritorna OKRitorna OKRitorna OKRitorna OK
FASE 1: FASE 1: FASE 1: FASE 1:
attesaattesaattesaattesa
FASE 2: FASE 2: FASE 2: FASE 2:
copiacopiacopiacopia
System callSystem callSystem callSystem callRecvfrom
BLOCCATA
BLOCCATA
BLOCCATA
BLOCCATA
Ritorna “pronto”Ritorna “pronto”Ritorna “pronto”Ritorna “pronto”
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
80808080I/O guidato dai segnali
applicazioneapplicazioneapplicazioneapplicazione kernelkernelkernelkernel
signalSystem callSystem callSystem callSystem call
datagram prontodatagram prontodatagram prontodatagram pronto
Copia datagramCopia datagramCopia datagramCopia datagram
Copia completataCopia completataCopia completataCopia completataProcesso continua (elabora il Processo continua (elabora il Processo continua (elabora il Processo continua (elabora il datagram)datagram)datagram)datagram)
BLOCCATA
BLOCCATA
BLOCCATA
BLOCCATA
Ritorna OKRitorna OKRitorna OKRitorna OK
FASE 1: FASE 1: FASE 1: FASE 1:
attesaattesaattesaattesa
FASE 2: FASE 2: FASE 2: FASE 2:
copiacopiacopiacopia
System callSystem callSystem callSystem callRecvfrom
SEGNALESEGNALESEGNALESEGNALEGESTORE SEGNALE
Non ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram pronti
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
81818181I/O asincrono
applicazioneapplicazioneapplicazioneapplicazione kernelkernelkernelkernel
aio_readSystem callSystem callSystem callSystem call
datagram prontodatagram prontodatagram prontodatagram pronto
Copia datagramCopia datagramCopia datagramCopia datagram
Copia completataCopia completataCopia completataCopia completata
FASE 1: FASE 1: FASE 1: FASE 1:
attesaattesaattesaattesa
FASE 2: FASE 2: FASE 2: FASE 2:
copiacopiacopiacopia
SEGNALESEGNALESEGNALESEGNALEGESTORE SEGNALE
Non ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram prontiNon ci sono datagram pronti
Processo continua (elabora il Processo continua (elabora il Processo continua (elabora il Processo continua (elabora il datagram)datagram)datagram)datagram)
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
82828282Funzione select
#include <sys/select.h>#include <sys/time.h>int select(int maxfd, fd_set readset, fd_set writeset,
fd_set exceptionset, const struct timeval *timeout);
Valore di ritorno: -1 se errore, 0 se timeout, numero di descrittori pronti
� Permette di aspettare che uno o più file descriptor siano pronti per essere letti
� Il timeout è dato dalla struttura
struct timeval {long tv_sec;long tv_usec;
}
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
83838383Parametri select
� Timeout1. Puntatore nullo: aspetta senza timeout (fino a che un descrittore
è pronto)2. Struttura con un timeout non zero: aspetta fino al timeout, poi
ritorna anche se non ci sono descrittori pronti� Anche se possiamo specificare i microsecondi alcuni kernel
arrotondano a multipli di 10 microsecondi� Alcuni sistemi Linux modificano la struttura timeout (vale il tempo
rimanente al momento del ritorno)3. Struttura con un timeout pari a 0: non aspettare, ritorna
immediatamente (polling)
� File descriptor da controllare� Readset: pronti per la lettura� Writeset: pronti per la scrittura� Exceptionset: condizioni particolari
� Arrivo di dati fuori banda su un socket� Informazioni di controllo da uno pseudo terminale
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
84848484Parametri select
� Per descriveri gli insiemi si usa la struttura fd_set che è un insieme di bit� void FD_ZERO(fd_set *fdset);
� Azzera la struttura fdset
� void FD_SET(int fd, fd_set *fdset);� Mette a 1 il bit relativo al file descriptor fd
� void FD_CLR(int fd, fd_set *fdset);� Mette a 0 il bit relativo al file descriptor fd
� int FD_ISSET(int fd, fd_set *fdset);� Controlla se il bit relativo al file descriptor fd è a 1
� La costante FD_SETSIZE (select.h) è il numero di descrittori in fd_set (solitamente 1024)
� maxfd: è il numero massimo di descrittori effetivamente usati� Usato per efficienza dal kernel� Es. se siamo interessati ai descrittori 1,4,7,9 maxfd deve valere
10 (i file descriptor iniziano da 0)
15
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
85858585Descrittori pronti
� Quando un socket descriptor è pronto per essere usato?
� Socket in lettura� Quando c’è almeno un byte da leggere
� La soglia si può cambiare con le opzioni dei socket
� Il socket è stato chiuso in lettura� Es. è stato ricevuto il FIN� L’operazione di lettura ritorna EOF
� Il socket è un “listening” socket e ci sono delle connessioni completate
� C’è un errore� L’operazione di lettura ritornerà -1 e errno specificherà l’errore
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
86868686Descrittori pronti
� Socket in scrittura� Il numero di byte di spazio disponibile nel buffer del
kernel è maggiore di 2048� La soglia si può cambiare con le opzioni dei socket� L’operazione di scrittura ritorna il numero di byte effettivamente
passati al livello di trasporto
� Il socket è stato chiuso in scrittura� Un’operazione di scrittura genera SIGPIPE
� C’è un errore� L’operazione di scrittura ritornerà -1 e errno specificherà l’errore
� Eccezioni per socket� Arrivo di dati fuori banda
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
87878787echoclient – versione select (1)
void client_echo_select(FILE *fp, int sockfd) {void client_echo_select(FILE *fp, int sockfd) {void client_echo_select(FILE *fp, int sockfd) {void client_echo_select(FILE *fp, int sockfd) {
int maxfdl;int maxfdl;int maxfdl;int maxfdl;fd_set rset;fd_set rset;fd_set rset;fd_set rset;
char sendline[MAXLINE], recvline[MAXLINE];char sendline[MAXLINE], recvline[MAXLINE];char sendline[MAXLINE], recvline[MAXLINE];char sendline[MAXLINE], recvline[MAXLINE];
int n;int n;int n;int n;
FD_ZERO(&rset);FD_ZERO(&rset);FD_ZERO(&rset);FD_ZERO(&rset);
for( ; ; ) {for( ; ; ) {for( ; ; ) {for( ; ; ) {
FD_SET(fileno(fp), &rset);FD_SET(fileno(fp), &rset);FD_SET(fileno(fp), &rset);FD_SET(fileno(fp), &rset);
FD_SET(sockfd, &rset);FD_SET(sockfd, &rset);FD_SET(sockfd, &rset);FD_SET(sockfd, &rset);
maxfdl = MAX(fileno(fp), sockfd) + 1;maxfdl = MAX(fileno(fp), sockfd) + 1;maxfdl = MAX(fileno(fp), sockfd) + 1;maxfdl = MAX(fileno(fp), sockfd) + 1;
if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )
err_sys("select error");err_sys("select error");err_sys("select error");err_sys("select error");
if( FD_ISSET(sockfd, &rset) ) {if( FD_ISSET(sockfd, &rset) ) {if( FD_ISSET(sockfd, &rset) ) {if( FD_ISSET(sockfd, &rset) ) {
if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {
if( errno == EPIPE ) {if( errno == EPIPE ) {if( errno == EPIPE ) {if( errno == EPIPE ) {
err_msg(“%s [%d]: server disconnesso“, __FILE__,__LINE__);err_msg(“%s [%d]: server disconnesso“, __FILE__,__LINE__);err_msg(“%s [%d]: server disconnesso“, __FILE__,__LINE__);err_msg(“%s [%d]: server disconnesso“, __FILE__,__LINE__);
break;break;break;break;
}}}}
elseelseelseelse
err_sys("readline error");err_sys("readline error");err_sys("readline error");err_sys("readline error");
} } } }
echocliechocliechocliechocli----slct.cslct.cslct.cslct.c
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
88888888echoclient – versione select (2)
if (n == 0)if (n == 0)if (n == 0)if (n == 0)
err_quit(“%s [%d]: server disconnesso“,__FILE__,__LINE__);err_quit(“%s [%d]: server disconnesso“,__FILE__,__LINE__);err_quit(“%s [%d]: server disconnesso“,__FILE__,__LINE__);err_quit(“%s [%d]: server disconnesso“,__FILE__,__LINE__);
fputs(recvline, stdout);fputs(recvline, stdout);fputs(recvline, stdout);fputs(recvline, stdout);
}}}}
if( FD_ISSET(fileno(fp), &rset) ) {if( FD_ISSET(fileno(fp), &rset) ) {if( FD_ISSET(fileno(fp), &rset) ) {if( FD_ISSET(fileno(fp), &rset) ) {
if( fgets(sendline, MAXLINE, fp) == NULL) if( fgets(sendline, MAXLINE, fp) == NULL) if( fgets(sendline, MAXLINE, fp) == NULL) if( fgets(sendline, MAXLINE, fp) == NULL)
return;return;return;return;
if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)
err_sys("write error");err_sys("write error");err_sys("write error");err_sys("write error");
}}}}
}}}}
}}}}
� Il client riesce a gestire sia l’input da tastiera che l’input dal socket
� Se il server termina viene spedito un EOF sul socket� Il client lo riceve e termina la connessione� Senza select il client se ne sarebbe accorto dopo
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
89898989echoclient e select
� Condizioni gestite da select in lettura su stdin ed un socket
Data o EOFData o EOFData o EOFData o EOFstdinstdinstdinstdin
RSTRSTRSTRST FINFINFINFINdatadatadatadata
socketsocketsocketsocket
ClientClientClientClient
TCPTCPTCPTCP
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
90909090Stop-and-wait
� Il client opera in modalità stop-and-wait:� Spedisce una linea di input e si blocca in attesa
della risposta del server echo
CCCC
SSSS
Tempo 0
datidatidatidati
Tempo 1
datidatidatidati
Tempo 2
datidatidatidati
Tempo 3
datidatidatidati
SSSS
Tempo 4
echoechoechoecho
Tempo 5
echoechoechoecho
Tempo 6
echoechoechoecho
CCCC
Tempo 7
echoechoechoecho
16
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
91919191Batch input
� Si spediscono le richieste consecutivamente senza aspettare le risposte, che arriveranno dopo
CCCC
Tempo 0
d1d1d1d1
Tempo 1
d1d1d1d1CCCC d2d2d2d2
Tempo 2
d1d1d1d1CCCC d3d3d3d3 d2d2d2d2
SSSS
Tempo 3
d1d1d1d1CCCC d4d4d4d4 d3d3d3d3 d2d2d2d2
SSSS
Tempo 4
r1r1r1r1
d5d5d5d5 d4d4d4d4 d3d3d3d3 d2d2d2d2CCCC SSSS
Tempo 5
r1r1r1r1 r2r2r2r2
d5d5d5d5 d4d4d4d4 d3d3d3d3d6d6d6d6CCCC
SSSS
SSSS
Tempo 6
r1r1r1r1 r3r3r3r3r2r2r2r2
d5d5d5d5 d4d4d4d4d6d6d6d6d7d7d7d7CCCC
SSSS
SSSS
CCCC
Tempo 7
r1r1r1r1 r4r4r4r4r3r3r3r3r2r2r2r2
d6d6d6d6 d5d5d5d5d7d7d7d7d8d8d8d8
SSSS
SSSSCCCC
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
92929292Shutdown della connessione
� Quando il client finisce di spedire non può chiudere il socket� Ci possono esser ancora dati in arrivo
� Si deve chiudere il socket solo in scrittura e lasciarlo aperto in lettura� Spedire il FIN solo in una direzione
#include <sys/socket.h>int shutdown(int sockfd, int howto);
Valore di ritorno: -1 se errore, 0 se OK
howto = SHUT_RD, SHUT_WR, SHUT_RDWR
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
93939393echoclient – versione shutdown
void client_echo_shutdown(FILE *fp, int sockfd) {void client_echo_shutdown(FILE *fp, int sockfd) {void client_echo_shutdown(FILE *fp, int sockfd) {void client_echo_shutdown(FILE *fp, int sockfd) {
int int int int maxfdl, stdineof;maxfdl, stdineof;maxfdl, stdineof;maxfdl, stdineof;fd_set fd_set fd_set fd_set rset;rset;rset;rset;
char char char char sendline[MAXLINE], recvline[MAXLINE];sendline[MAXLINE], recvline[MAXLINE];sendline[MAXLINE], recvline[MAXLINE];sendline[MAXLINE], recvline[MAXLINE];
int int int int n;n;n;n;
FD_ZERO(&rset);FD_ZERO(&rset);FD_ZERO(&rset);FD_ZERO(&rset);
for( ; ; ) {for( ; ; ) {for( ; ; ) {for( ; ; ) {
FD_SET(fileno(fp), &rset);FD_SET(fileno(fp), &rset);FD_SET(fileno(fp), &rset);FD_SET(fileno(fp), &rset);
FD_SET(sockfd, &rset);FD_SET(sockfd, &rset);FD_SET(sockfd, &rset);FD_SET(sockfd, &rset);
maxfdl = MAX(fileno(fp), sockfd) + 1;maxfdl = MAX(fileno(fp), sockfd) + 1;maxfdl = MAX(fileno(fp), sockfd) + 1;maxfdl = MAX(fileno(fp), sockfd) + 1;
if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )if( select(maxfdl, &rset, NULL, NULL, NULL) < 0 )
err_sys("select error");err_sys("select error");err_sys("select error");err_sys("select error");
if( FD_ISSET(sockfd, &rset) ) {if( FD_ISSET(sockfd, &rset) ) {if( FD_ISSET(sockfd, &rset) ) {if( FD_ISSET(sockfd, &rset) ) {
if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {if ( (n = reti_readline(sockfd, recvline, MAXLINE)) < 0) {
if( errno == EPIPE ) {if( errno == EPIPE ) {if( errno == EPIPE ) {if( errno == EPIPE ) {
err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);
break;break;break;break;
}}}}
elseelseelseelse
err_sys("readline error");err_sys("readline error");err_sys("readline error");err_sys("readline error");
} } } }
echocliechocliechocliechocli----shtd.cshtd.cshtd.cshtd.c
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
94949494echoclient – versione shutdown
if (n == 0) {if (n == 0) {if (n == 0) {if (n == 0) {
if( stdineof == 1 )if( stdineof == 1 )if( stdineof == 1 )if( stdineof == 1 )return;return;return;return;
else {else {else {else {
err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);err_msg("%s [%d]: server disconnesso",__FILE__,__LINE__);
exit(exit(exit(exit(----1);1);1);1);
}}}}
}}}}
fputs(recvline, stdout);fputs(recvline, stdout);fputs(recvline, stdout);fputs(recvline, stdout);
}}}}
if( FD_ISSET(fileno(fp), &rset) ) {if( FD_ISSET(fileno(fp), &rset) ) {if( FD_ISSET(fileno(fp), &rset) ) {if( FD_ISSET(fileno(fp), &rset) ) {
if( fgets(sendline, MAXLINE, fp) == NULL) {if( fgets(sendline, MAXLINE, fp) == NULL) {if( fgets(sendline, MAXLINE, fp) == NULL) {if( fgets(sendline, MAXLINE, fp) == NULL) {
stdineof = 1;stdineof = 1;stdineof = 1;stdineof = 1;
shutdown(sockfd, SHUT_WR);shutdown(sockfd, SHUT_WR);shutdown(sockfd, SHUT_WR);shutdown(sockfd, SHUT_WR);
FD_CLR(fileno(fp), &rset);FD_CLR(fileno(fp), &rset);FD_CLR(fileno(fp), &rset);FD_CLR(fileno(fp), &rset);
continue;continue;continue;continue;
}}}}
if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)if( (reti_writen(sockfd, sendline, strlen(sendline))) < 0)
err_sys("write error");err_sys("write error");err_sys("write error");err_sys("write error");
}}}}
}}}}
}}}}
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
95959595Select per il server� Possiamo usare select anche nel server
� Al posto di creare un figlio per ogni connessione� Select può leggere da tutti i client connessi
� Strutture dati utilizzate� Array rset, contiene file descriptor dei socket utilizzati
dal server (sia listening che connessi)� Array client, contiene interi che indicano fd
----1111
----1111
----1111
----1111FD_SETSIZEFD_SETSIZEFD_SETSIZEFD_SETSIZE----1111
1111
2222
3333
clientclientclientclient
0000 0000 0000 0000 0000 0000 …………
fd0fd0fd0fd0 fd1fd1fd1fd1 fd2fd2fd2fd2 fd3fd3fd3fd3 fd4fd4fd4fd4 fd5fd5fd5fd5
rsetrsetrsetrset
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
96969696Strutture dati per il server
� Il server crea il listening socket chiamando listen� Supponiamo che gli standard file siano aperti e che il fd
ritornato da listen sia 3� Tale informazione verrà memorizzata in rset
� Il server chiama select per leggere da tutti i socket (file descriptor) aperti� All’inizio c’è solo il il listening socket, su fd 3� Quindi il parametro maxfd di select deve essere 4
0000 0000 0000 1111 0000 0000 …………
fd0fd0fd0fd0 fd1fd1fd1fd1 fd2fd2fd2fd2 fd3fd3fd3fd3 fd4fd4fd4fd4 fd5fd5fd5fd5
rsetrsetrsetrset
17
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
97979797Strutture dati per il server
� Quando un client stabilisce una connessione verrà creato un socket per la connessione con la funzione accept � Supponiamo che il fd ritornato è 4
� Di nuovo il server chiama select per leggere da tutti i socket (file descriptor) aperti� Ora ci sono due socket, su fd 3 (listening) e fd 4 (connessione)� Quindi il parametro maxfd di select deve essere 5
0000 0000 0000 1111 1111 0000 …………
fd0fd0fd0fd0 fd1fd1fd1fd1 fd2fd2fd2fd2 fd3fd3fd3fd3 fd4fd4fd4fd4 fd5fd5fd5fd5
rsetrsetrsetrset
4444
----1111
----1111
----1111FD_SETSIZEFD_SETSIZEFD_SETSIZEFD_SETSIZE----1111
1111
2222
3333
clientclientclientclient
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
98989898Strutture dati per il server
� Supponiamo che un altro client si connette, verrà creato un nuovo socket� Supponiamo che il fd ritornato è 5
� Di nuovo il server chiama select per leggere da tutti i socket (file descriptor) aperti� Ora ci sono tre socket, su fd 3 (listening), e fd 4 e fd 5 per le
due connessioni� Quindi il parametro maxfd di select deve essere 6
0000 0000 0000 1111 1111 1111 …………
fd0fd0fd0fd0 fd1fd1fd1fd1 fd2fd2fd2fd2 fd3fd3fd3fd3 fd4fd4fd4fd4 fd5fd5fd5fd5
rsetrsetrsetrset
4444
5555
----1111
----1111FD_SETSIZEFD_SETSIZEFD_SETSIZEFD_SETSIZE----1111
1111
2222
3333
clientclientclientclient
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
99999999Strutture dati per il server
� Supponiamo ora che la prima connessione (quella che usa fd 4) venga chiusa
� Il server chiama select per leggere da tutti i socket (file descriptor) aperti� Ora ci sono due socket, su fd 3 (listening), e fd 5 (connessione)� Quindi il parametro maxfd di select deve essere 6
0000 0000 0000 1111 0000 1111 …………
fd0fd0fd0fd0 fd1fd1fd1fd1 fd2fd2fd2fd2 fd3fd3fd3fd3 fd4fd4fd4fd4 fd5fd5fd5fd5
rsetrsetrsetrset
----1111
5555
----1111
----1111FD_SETSIZEFD_SETSIZEFD_SETSIZEFD_SETSIZE----1111
1111
2222
3333
clientclientclientclient
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
100100100100Echo server versione select (1)
int main(int argc, char **argv) int main(int argc, char **argv) int main(int argc, char **argv) int main(int argc, char **argv)
{{{{int listenfd, connfd, sockfd;int listenfd, connfd, sockfd;int listenfd, connfd, sockfd;int listenfd, connfd, sockfd;
int i, maxi, maxfd;int i, maxi, maxfd;int i, maxi, maxfd;int i, maxi, maxfd;
int ready, client[FD_SETSIZE];int ready, client[FD_SETSIZE];int ready, client[FD_SETSIZE];int ready, client[FD_SETSIZE];
char buff[MAXLINE];char buff[MAXLINE];char buff[MAXLINE];char buff[MAXLINE];
fd_set rset, allset;fd_set rset, allset;fd_set rset, allset;fd_set rset, allset;
ssize_t n;ssize_t n;ssize_t n;ssize_t n;
struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;struct sockaddr_in servaddr, cliaddr;
socklen_t cliaddr_len;socklen_t cliaddr_len;socklen_t cliaddr_len;socklen_t cliaddr_len;
if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err_sys("socket error");err_sys("socket error");err_sys("socket error");err_sys("socket error");
bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);servaddr.sin_port = htons(PORT);
if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0)if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0)if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0)if( (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0)
err_sys("bind error");err_sys("bind error");err_sys("bind error");err_sys("bind error");
if( listen(listenfd, BACKLOG) < 0 )if( listen(listenfd, BACKLOG) < 0 )if( listen(listenfd, BACKLOG) < 0 )if( listen(listenfd, BACKLOG) < 0 )
err_sys("listen error");err_sys("listen error");err_sys("listen error");err_sys("listen error");
echosrvechosrvechosrvechosrv----slct.cslct.cslct.cslct.c
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
101101101101Echo server versione select (2)
maxfd = listenfd; /* inzializza il numero di descrittori */maxfd = listenfd; /* inzializza il numero di descrittori */maxfd = listenfd; /* inzializza il numero di descrittori */maxfd = listenfd; /* inzializza il numero di descrittori */
maxi = maxi = maxi = maxi = ----1;1;1;1;for ( i = 0; i < FD_SETSIZE; i++) client[i] = for ( i = 0; i < FD_SETSIZE; i++) client[i] = for ( i = 0; i < FD_SETSIZE; i++) client[i] = for ( i = 0; i < FD_SETSIZE; i++) client[i] = ----1; /* inizializza l'array client a 1; /* inizializza l'array client a 1; /* inizializza l'array client a 1; /* inizializza l'array client a ----1 */1 */1 */1 */
FD_ZERO(&allset); /* inizializza a zero tutti i descrittori */FD_ZERO(&allset); /* inizializza a zero tutti i descrittori */FD_ZERO(&allset); /* inizializza a zero tutti i descrittori */FD_ZERO(&allset); /* inizializza a zero tutti i descrittori */
FD_SET(listenfd, &allset); /* setta il descrittore di ascolto */FD_SET(listenfd, &allset); /* setta il descrittore di ascolto */FD_SET(listenfd, &allset); /* setta il descrittore di ascolto */FD_SET(listenfd, &allset); /* setta il descrittore di ascolto */
for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {for ( ; ; ) {
rset = allset; /* insieme descrittori da controllare per la lettura */rset = allset; /* insieme descrittori da controllare per la lettura */rset = allset; /* insieme descrittori da controllare per la lettura */rset = allset; /* insieme descrittori da controllare per la lettura */
if( (ready = select(maxfd+1, &rset, NULL, NULL, NULL)) < 0 )if( (ready = select(maxfd+1, &rset, NULL, NULL, NULL)) < 0 )if( (ready = select(maxfd+1, &rset, NULL, NULL, NULL)) < 0 )if( (ready = select(maxfd+1, &rset, NULL, NULL, NULL)) < 0 )
err_sys("select error");err_sys("select error");err_sys("select error");err_sys("select error");
if( FD_ISSET(listenfd, &rset) ) { /* richiesta ricevuta dal listening socket */if( FD_ISSET(listenfd, &rset) ) { /* richiesta ricevuta dal listening socket */if( FD_ISSET(listenfd, &rset) ) { /* richiesta ricevuta dal listening socket */if( FD_ISSET(listenfd, &rset) ) { /* richiesta ricevuta dal listening socket */
cliaddr_len = sizeof(cliaddr);cliaddr_len = sizeof(cliaddr);cliaddr_len = sizeof(cliaddr);cliaddr_len = sizeof(cliaddr);
if( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddr_len)) < 0) if( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddr_len)) < 0) if( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddr_len)) < 0) if( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddr_len)) < 0)
err_sys("accept error");err_sys("accept error");err_sys("accept error");err_sys("accept error");
for(i = 0; i < FD_SETSIZE; i++)for(i = 0; i < FD_SETSIZE; i++)for(i = 0; i < FD_SETSIZE; i++)for(i = 0; i < FD_SETSIZE; i++)
if( client[i] < 0 ) { /* cerca il primo posto libero per il nuovo il descrittore */if( client[i] < 0 ) { /* cerca il primo posto libero per il nuovo il descrittore */if( client[i] < 0 ) { /* cerca il primo posto libero per il nuovo il descrittore */if( client[i] < 0 ) { /* cerca il primo posto libero per il nuovo il descrittore */
client[i] = connfd;client[i] = connfd;client[i] = connfd;client[i] = connfd;
break;break;break;break;
}}}}
if( i == FD_SETSIZE ) err_quit("troppi client"); if( i == FD_SETSIZE ) err_quit("troppi client"); if( i == FD_SETSIZE ) err_quit("troppi client"); if( i == FD_SETSIZE ) err_quit("troppi client");
FD_SET(connfd, &allset); /* setta connfd per la select */FD_SET(connfd, &allset); /* setta connfd per la select */FD_SET(connfd, &allset); /* setta connfd per la select */FD_SET(connfd, &allset); /* setta connfd per la select */
if( connfd > maxfd ) maxfd = connfd; /* aggiorna maxfd */if( connfd > maxfd ) maxfd = connfd; /* aggiorna maxfd */if( connfd > maxfd ) maxfd = connfd; /* aggiorna maxfd */if( connfd > maxfd ) maxfd = connfd; /* aggiorna maxfd */
if( i > maxi ) maxi = i; /* aggiorna maxi */if( i > maxi ) maxi = i; /* aggiorna maxi */if( i > maxi ) maxi = i; /* aggiorna maxi */if( i > maxi ) maxi = i; /* aggiorna maxi */
if( if( if( if( --------ready <= 0 ) ready <= 0 ) ready <= 0 ) ready <= 0 )
continue; /* se non ci sono altri socket pronti riprendi da select */continue; /* se non ci sono altri socket pronti riprendi da select */continue; /* se non ci sono altri socket pronti riprendi da select */continue; /* se non ci sono altri socket pronti riprendi da select */
} } } }
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
102102102102Echo server versione select (3)
for( i = 0; i <= maxi; i++ ) {for( i = 0; i <= maxi; i++ ) {for( i = 0; i <= maxi; i++ ) {for( i = 0; i <= maxi; i++ ) {/* controlla tutti i socket attivi per controllare se sono leggibili *//* controlla tutti i socket attivi per controllare se sono leggibili *//* controlla tutti i socket attivi per controllare se sono leggibili *//* controlla tutti i socket attivi per controllare se sono leggibili */
if( (sockfd = client[i]) < 0 ) if( (sockfd = client[i]) < 0 ) if( (sockfd = client[i]) < 0 ) if( (sockfd = client[i]) < 0 )
continue;continue;continue;continue;
if ( FD_ISSET(sockfd, &rset) ) {if ( FD_ISSET(sockfd, &rset) ) {if ( FD_ISSET(sockfd, &rset) ) {if ( FD_ISSET(sockfd, &rset) ) {
/* se sockfd è leggibile invoca la readline *//* se sockfd è leggibile invoca la readline *//* se sockfd è leggibile invoca la readline *//* se sockfd è leggibile invoca la readline */
if ( (n = reti_readline(sockfd, buff, MAXLINE)) == 0) {if ( (n = reti_readline(sockfd, buff, MAXLINE)) == 0) {if ( (n = reti_readline(sockfd, buff, MAXLINE)) == 0) {if ( (n = reti_readline(sockfd, buff, MAXLINE)) == 0) {
/* connessione chiusa dall'altro endpoint *//* connessione chiusa dall'altro endpoint *//* connessione chiusa dall'altro endpoint *//* connessione chiusa dall'altro endpoint */
close(sockfd);close(sockfd);close(sockfd);close(sockfd);
/* rimuovi sockfd dalla lista di socket /* rimuovi sockfd dalla lista di socket /* rimuovi sockfd dalla lista di socket /* rimuovi sockfd dalla lista di socket
che la select deve controllare */che la select deve controllare */che la select deve controllare */che la select deve controllare */
FD_CLR(sockfd, &allset); FD_CLR(sockfd, &allset); FD_CLR(sockfd, &allset); FD_CLR(sockfd, &allset);
client[i] = client[i] = client[i] = client[i] = ----1; /* cancella sockfd da client */1; /* cancella sockfd da client */1; /* cancella sockfd da client */1; /* cancella sockfd da client */
}}}}
elseelseelseelse
reti_writen(sockfd, buff, n);reti_writen(sockfd, buff, n);reti_writen(sockfd, buff, n);reti_writen(sockfd, buff, n);
if ( if ( if ( if ( --------ready <= 0 )ready <= 0 )ready <= 0 )ready <= 0 )
break;break;break;break;
}}}}
}}}}
}}}}
}}}}
18
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
103103103103Denial of Service Attack
� Consideriamo la seguente situazione che può accadere con il server visto poco fa� Un client si connette, spedisce un solo byte
(che non sia un newline) e non fa più nulla� Il server chiama readline che leggerà il singolo
byte ma si bloccherà nella prossima chiamata a read in attesa di un newline
� Il server è bloccato e nessun altro client riceverà il servizio
� Denial of Service Attack� Un client riesce a far i modo che il server non
risponda più ad altri client
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
104104104104Denial of Service Attack
� Il problema deriva dal fatto che il server si blocca in una funzione relativa ad un client mentre ci sono anche altri client� Potrebbe andare bene se il server deve gestire
un solo client
� Possibili soluzioni� Usare un singolo processo per ogni client� Utilizzare un timeout sulle operazioni di I/O� Usare I/O non-blocking
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
105105105105Esercizi
� Modificare echosrv-slct in modo che sia un server somma
� Modificare echosrv-slct in modo che stampi l’indirizzo del client quando � il client si connette� Il client si disconnette
Prof. Roberto De Prisco
Reti di Calcolatori
Corso di laurea in Informatica
A.A. 2007-2008
Lezione
Opzioni per i socket
5555
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
107107107107Socket options
� Ogni socket aperto ha delle proprietà che ne determinano alcuni comportamenti
� Possono essere cambiate: opzioni
� L’anologo per i file sono le proprietà che vengono gestite tramite la funzione fcntl� La funzione fcntl può essere usata con i socket
� Ogni opzione ha un valore di default� Alcune opzioni sono binarie (on o off)� Altre hanno un valore (int o anche strutture più
complesse)
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
108108108108Livelli
� Sono divise in vari livelli� SOL_SOCKET livello socket� IPPROTO_IP livello IP� IPPROTO_IPV6 livello IP per la versione 6� IPPROTO_ICMPV6 livello messaggi di controllo� IPPROTO_TCP livello TCP
� Ogni livello ha varie opzioni
� Non è detto che tutte siano supportate
� Il programma sockopts-check stampa i valori di default delle opzioni supportate
19
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
109109109109OpzioniSO_BROADCAST permette il broadcastSO_DEBUG abilita le informazioni di debugSO_DONTROUTE salta il lookup nella tavola di routingSO_ERROR legge l’errore corrente SO_KEEPALIVE controlla che la connessione sia attivaSO_LINGER controlla la chiusura della connessioneSO_RCVBUF grandezza del buffer in ricezioneSO_SNDBUF grandezza buffer in spedizioneSO_RCVLOWAT soglia per il buffer in ricezioneSO_SNDLOWAT soglia per il buffer in spedizioneSO_RCVTIMEO timeout per la ricezioneSO_SNDTIMEO timeout per la spedizioneSO_REUSEADDR permette riutilizzo indirizzi localiSO_REUSEPORT permette riutilizzo porte localiSO_TYPE il tipo di socketSO_USELOOPBACK per i socket di routing (copia i pacchetti)
IP_HDRINCL header incluso con i datiIP_OPTIONS opzioni nell’header IPIP_TTL Time-To-Live
TCP_KEEPALIVE tempo per la ritrasmissioneTCP_MAXRT tempo massimo per la ritrasmissioneTCP_MAXSEG MSS (Maximum Segment Size)TCP_NODELAY disabilita algoritmo di Nagle
Livello:Livello:Livello:Livello:
SOL_SOCKETSOL_SOCKETSOL_SOCKETSOL_SOCKET
Livello:Livello:Livello:Livello:
IPPROTO_IPIPPROTO_IPIPPROTO_IPIPPROTO_IP
Livello:Livello:Livello:Livello:
IPPROTO_TCPIPPROTO_TCPIPPROTO_TCPIPPROTO_TCP
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
110110110110getsockopt e setsockopt
#include <sys/socket.h>int getsockopt(int sd, int level, int name, void *value, socklen_t *len);int setsockopt(int sd, int level, int name, void *value, socklen_t len);
Valore di ritorno: -1 se errore, 0 se OK
Parametri:
setsockopt(3,SOL_SOCKET,SO_KEEPALIVE,1,sizeof(int));attiva l’opzione di “keepalive” per mantenere la connessione attiva
void* flag;socklen_t* size;getsockopt(3,SOL_SOCKET,SO_KEEPALIVE,*flag,*size);
ritorna la grandezza di un intero in size e flag sarà 0 se l’opzione è disabilitata, oppure diverso da 0 se l’opzione è abilitata
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
111111111111SO_BROADCAST
� Rende possibile la trasmissione broadcast
� Un applicazione che intende spedire in broadcast deve abilitare questa opzione
� Serve a prevenire broadcast accidentali� Es. un programma prende in input l’indirizzo di
destinazione� L’utente potrebbe inserire un indirizzo di broadcast� Il programma dovrebbe controllare� Se l’opzione è disabilitata il broadcast non viene fatto
comunque� Viene ritornato l’errore EACCES
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
112112112112SO_ERROR
� Quando si verifica un errore, un codice di errore viene memorizzato in una variabile chiamata so_error
� La notifica dell’errore avviene in 2 modi:� Se il processo è in una chiamata a select, sia per la lettura che
per la scrittura, ritorna con il descrittore pronto ad essere letto� Se si sta usando I/O guidato dai segnali, SIGIO viene generato
� Il processo può leggere il valore di so_error tramite questa opzione� L’intero ritornato è il valore di so_error
� Può essere solo letta, non scritta
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
113113113113SO_KEEPALIVE
� Quando è abilitata per un socket TCP spedisce un messaggio di controllo se non c’è scambio di dati per un periodo di 2 ore
� Serve a mantenere attiva la connessione
� 3 possibili casi� ACK di risposta: la connessione è ancora attiva� RST di risposta: la connessione era stata chiusa,
viene generato l’errore ECONNRESET� Non c’è risposta, vengono spediti 8 ulteriori messaggi
ogni 75 secondi� Nessuna riposta dopo 11 minuti e 15 secondi: viene
generato l’errore ETIMEDOUT� Messaggi ICMP di errore sulla rete, viene generato l’errore
EHOSTUNREACH
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
114114114114SO_LINGER
� Specifica il funzionamento della close per protocolli orientati alla connessione (TCP)
struct linger {int l_onoff; /* 0=off, nonzero=on */int l_linger; /* tempo attesa in secondi */
}
� l_onoff = 0, � close ritorna immediatamente ed il kernel spedirà eventuali dati
residui� l_onoff ≠ 0, l_linger = 0
� La connessione viene resettata, i dati residui non spediti� Evita il TIME_WAIT, ma rischia la reincarnazione della
connessione� l_onoff ≠ 0, l_linger ≠ 0
� Close non ritorna subito ma aspetta l_linger secondi affinchè i dati residui vengano spediti
� È importante controllare il valore di ritorno perché dopo l_linger secondi i dati vengono cancellati
20
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
115115115115SO_RCVBUF e SO_SNDBUF
� Ogni socket ha un buffer di ricezione ed uno di spedizione
� Il buffer di ricezione è usato per mantenere i dati nel kernel prima di passarli all’applicazione� Con TCP, lo spazio disponibile è quello pubblicizzato
nella finestra di TCP quindi non dovrebbe esserci overflow
� Con UDP, eventuali pacchetti in overflow vengono cancellati
� Il buffer in spedizione è usato per far scriver i dati dall’applicazione prima di spedirli
� SO_RCVBUF e SO_SNDBUF ci permettono di cambiare la grandezza dei buffer
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
116116116116SO_RCVLOWAT e SO_SNDLOWAT
� Sono le soglie usate dalla funzione select
� Quando in ricezione sono presenti almeno un numero di byte pari al low-watermark allora select ritorna “socket pronto in lettura”� Per TCP e UDP il default è 1
� In modo simile per poter scrivere (socket pronto in scrittura) i byte disponibili nel buffer di spedizione deve essere almeno pari al low-watermark� Il default è 2048
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
117117117117SO_RCVTIMEO e SO_SNDTIMEO
� Ci permettono di usare un timeout per la ricezione e la spedizione sul socket
struct timeval {long tv_sec; /* secondi */long tv_usec; /* microsecondi */
}
� Il timeout in ricezione è per le funzioni� Read, readv, recv, recvfrom, recvmsg
� Il timeout in spedizione è per le funzioni� Write, writev, send, sendto
� Per default il timeout è 0� Timeout disabilitato, quindi la funzione si blocca
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
118118118118SO_TYPE
� Ci dà il tipo di socket� SOCK_STREAM� SOCK_DGRAM
� Viene usata da un processo che eredita un socket
� Può essere solo letta, non scritta
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
119119119119Opzioni di IPv4
� IP_HDRINCL� Se è settata per un raw socket di IP allora l’applicazione
deve costruire anche l’IP header che normalmente è scritto dal kernel
� IP_OPTIONS� Permette di inserire le opzioni dell’IP header
� IP_TTL� Permette di specificare il Time-To-Live (in hops) del
datagram� Il default è 64
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
120120120120Opzioni di TCP
� TCP_KEEPALIVE� Permette di moficare le 2 ore di attesa usate da SO_KEEPALIVE
� TCP_MAXRT� -1, ritrasmetti per sempre� 0, usa il default di sistema� n > 0, ritrasmetti per n secondi prima di abbandonare la
connessione
� TCP_MAXSEG� Permette di modificare il valore dell’MSS della connessione
� TCP_NODELAY� Diabilita l’algoritmo di Nagle che diminuisce i dati spediti in caso di
congestione
21
LABORATORIOLABORATORIOLABORATORIOLABORATORIOAutunno 2007Autunno 2007Autunno 2007Autunno 2007
121121121121Esempio di set e get sockopt
#include "basic.h" #include "basic.h" #include "basic.h" #include "basic.h"
#include <netinet/tcp.h> #include <netinet/tcp.h> #include <netinet/tcp.h> #include <netinet/tcp.h> int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {int main(int argc, char **argv) {
int sockfd, mss, sendbuff, ttl;int sockfd, mss, sendbuff, ttl;int sockfd, mss, sendbuff, ttl;int sockfd, mss, sendbuff, ttl;
socklen_t optlen;socklen_t optlen;socklen_t optlen;socklen_t optlen;
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
err_sys("socket error");err_sys("socket error");err_sys("socket error");err_sys("socket error");
optlen = sizeof(ttl);optlen = sizeof(ttl);optlen = sizeof(ttl);optlen = sizeof(ttl);
if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == ----1 )1 )1 )1 )
err_ret("getsockopt error");err_ret("getsockopt error");err_ret("getsockopt error");err_ret("getsockopt error");
printf("getsockopt: TTL = %dprintf("getsockopt: TTL = %dprintf("getsockopt: TTL = %dprintf("getsockopt: TTL = %d\\\\n", ttl);n", ttl);n", ttl);n", ttl);
ttl = ttl + 16;ttl = ttl + 16;ttl = ttl + 16;ttl = ttl + 16;
if( setsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == if( setsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == if( setsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == if( setsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == ----1 )1 )1 )1 )
err_ret("getsockopt error");err_ret("getsockopt error");err_ret("getsockopt error");err_ret("getsockopt error");
printf("TTL modificatoprintf("TTL modificatoprintf("TTL modificatoprintf("TTL modificato\\\\n", ttl);n", ttl);n", ttl);n", ttl);
if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == if( getsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, &optlen) == ----1 )1 )1 )1 )
err_ret("getsockopt error");err_ret("getsockopt error");err_ret("getsockopt error");err_ret("getsockopt error");
printf("getsockopt: TTL = %dprintf("getsockopt: TTL = %dprintf("getsockopt: TTL = %dprintf("getsockopt: TTL = %d\\\\n", ttl);n", ttl);n", ttl);n", ttl);
exit(0);exit(0);exit(0);exit(0);
}}}}
sockoptssockoptssockoptssockopts----set.cset.cset.cset.c
Recommended