1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R....

65
1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright © 2006-2013 by D. Romagnoli & C. Salati Alma Mater Studiorum - Universita' di Bologna Sede di Cesena II Facolta' di Ingegneria

Transcript of 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R....

Page 1: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

1

Reti di Calcolatori

Esercitazione 1

Implementazione di un superserver Unix di rete

Vedi:

• W.R. Stevens, Unix Network Programming, Prentice Hall

Copyright © 2006-2013 by D. Romagnoli & C. Salati

Alma Mater Studiorum - Universita' di BolognaSede di Cesena

II Facolta' di Ingegneria

Page 2: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

2

Servizi nel mondo di Unix

• Uno host internet supporta tipicamente svariati servizi di rete (e.g. ftp, telnet, TFTP, ...).

• Ogni servizio di rete, concorrente o sequenziale, dovrebbe

• comportarsi secondo uno degli schemi visti a lezione.

• essere attivato dal sistema alla partenza e rimanere in permanenza attivo in attesa di richieste di clienti;richieste che potrebbero pero’ anche non arrivare mai!(in gergo Unix un processo che si comporta in questo modo e’ chiamato daemon, tradotto impropriamente, come demone)

• In realta’ la struttura dei servizi standard nel mondo Unix non e’ quella degli schemi visti a lezione per i servizi di rete.

• Un servizio standard Unix ha la struttura di un filtro Unix.

• Quindi, quello che vorremmo davvero e’ che anche i servizi di rete fossero strutturati come dei normali filtri Unix.

• Cosi’ potremmo anche esportare automaticamente in rete tutti i servizi locali implementati come filtri Unix (e.g. il servizio cat).

Page 3: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

33

Struttura canonica di un server CO concorrente

// trascuriamo la trattazione degli erroriint sockfd, newSockfd;sockfd = socket(. . .);bind(sockfd, . . .);listen(sockfd, 5);for (;;) { newSockfd = accept(sockfd, . . .); if (fork() == 0) { // processo figlio/clone close(sockfd); doYourJob(newSockfd); close(newSockfd); exit(0); } else { // processo padre close (newSockfd); }}

Page 4: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

44

Struttura canonica di un server CO sequenziale

// trascuriamo la trattazione degli errori

int sockfd, newSockfd;

sockfd = socket(. . .);

bind(sockfd, . . .);

listen(sockfd, 5);

for (;;) {

newSockfd = accept(sockfd, . . .);

doYourJob(newSockfd);

close(newSockfd);

}

•Il server sa che opera su risorse reali di tipo socket

•Il server sa su quali risorse opera

•Il server e’ attivo in permanenza in attesa di nuovi clienti

Page 5: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

55

Struttura canonica di un server CO: in realta’ …• Il server e’ composto di 2 parti:

1. Una parte generica, indipendente dal particolare servizio che viene offerto, di attesa di nuovi clienti

2. Una parte specifica, dipendente dal particolare servizio che viene offerto, e che riguarda il servizio del (l’interazione con il) singolo cliente

• Parte generica del server:

• Vede e gestisce esplicitamente l’accesso al Servizio di Trasposto tramite system call specifiche dell’API socket

• E’ sempre viva, in attesa di nuovi clienti (nuove richieste di servizio)

• E’ lei che determina se il servizio e’ sequenziale o concorrente

• Parte specifica del server:

• Vede l’accesso al Servizio di Trasporto soltanto in modo opaco, tramite l’uso del file descriptor associato al socket di comunicazione con il cliente, e utilizzando solo le system call generiche read() e write()

• Il file descriptor tramite cui interagire con il cliente e’ l’unico parametro di input della parte specifica

• La durata della sua vita e’ collegata a quella del servizio offerto al cliente cui e’ associata

La parte generica potrebbe essere messa a fattor comune tra tutti i server

Page 6: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

6

Struttura di un filtro Unix .1• E’ creato dinamicamente, quando e’ invocato dal cliente.

Non e’ attivo in permanenza in attesa di nuovi clienti. E’ chi attiva il servizio (e.g. una shell) che determina se il servizio

stesso sara’ eseguito in modo concorrente o sequenziale.

• Serve un singolo cliente, poi muore.

• Interagisce con il mondo esterno tramite

• standard input (in realta’ il file descriptor = 0) e

• standard output (in realta’ il file descriptor = 1) ,

• che accede tramite operazioni di read() e write().

• Al momento della sua attivazione i file descriptor 0 e 1 sono gia’ aperti e collegati a risorse reali del sistema:

e’ su queste risorse che il filtro opera, rispettivamente, in lettura (read()) e in scrittura (write()).

• E’ chi attiva il servizio che nel momento in cui lo fa (e.g. tramite command line/shell) definisce il significato dei file descriptor 0 e 1.

• Il processo filtro eredita dal processo padre i file descriptor 0 e 1 gia’ aperti sulle risorse di sistema che esso dovra’ utilizzare.

Page 7: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

7

Struttura di un filtro Unix .2

• Il filtro ignora quali siano le risorse che accede tramite i file descriptor 0 e 1.

• Il filtro ignora anche quale sia il tipo delle risorse che accede tramite i file descriptor 0 e 1:

• File vero e proprio

• Device driver

• Pipe

• . . .

E quindi, anche: socket!

• Il filtro sa che puo’ operare su queste risorse tramite operazioni di read() e write().

• Se la risorsa di sistema e’ un TSAP, questo deve essere:

• Gia’ bind-ato.

• Gia’ connesso ad un pari remoto.

Page 8: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

8

Struttura di un filtro Unix: esempio d’uso

#cat copia standard input in standard output

#cat > dstFile copia standard input in dstFile

#cat srcFile copia srcFile in standard outputvisualizza il contenuto di srcFile

#cat srcFile > dstFile copia srcFile in dstFile

#cat > dstFile

Questo testo sara’ inserito in dstFile.

^D

#

# e’ il prompt della shell in esecuzione

Page 9: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

99

Struttura canonica di un filtro Unix

// l’interazione con il mondo esterno avviene// tramite i due file descriptor 0 e 1// che il server-filtro eredita gia’ aperti e// associati alle risorse di sistema opportune// dal processo padre

doYourJob(0, 1);exit(0);

N.B.:

• I file descriptor 0 e 1 sono di norma associati a risorse reali diverse ma niente impedisce che esse siano associati ad una stessa risorsa (che in questo caso deve essere capace di supportare contemporaneamente operazioni di read e di write!).

• In realta’ poi, un socket rappresenta davvero 2 risorse separate:

• Uno stream di byte in ingresso

• Uno stream di byte in uscita

Page 10: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

1010

Struttura di un server CO e filtri Unix

int sockfd, newSockfd;sockfd = socket(. . .);bind(sockfd, . . .);listen(sockfd, 5);for (;;) { newSockfd = accept(sockfd, . . .); if (fork() == 0) { // processo figlio/clone close(sockfd); // la parte specifica del server e’ come un // filtro Unix, con 2 fd come parametri di // ingresso, input fd e output fd doYourJob(newSockfd /*in*/, newSockfd /*out*/); close(newSockfd); exit(0); } else { // processo padre close (newSockfd); }}

Page 11: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

11

Server di rete e filtri Unix .1

• La parte generica dei server di rete puo’ essere messa a fattor comune:

• Gestisce l’attesa di nuovi clienti per tutti i servizi di rete.

• Determina se l’attivazione di ciascuno di questi servizi deve avvenire in modo sequenziale o concorrente.

• Attiva un server specifico quando vede arrivare dalla rete una richiesta di nuovo servizio indirizzata a lui.

(quindi deve conoscere a priori il file name del programma eseguibile che implementa il servizio!)

• Si comporta rispetto ai clienti sulla rete come fa una shell rispetto all’operatore seduto al terminale!

• I servizi specifici di rete sono implementati come filtri Unix cosi’ come i servizi specifici locali.

A questo punto, in realta’, non c’e’ nessuna differenza tra un servizio di rete e un servizio locale.

cat puo’ ad esempio essere utilizzato per realizzare un servizio di eco TCP! Come?

Page 12: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

12

Server di rete e filtri Unix .2

• Quando si interagisce con i servizi tramite shell, si identifica il servizio che si vuole attivare con il suo nome.

Indicando il file name del programma eseguibile (filtro Unix) che implementa il servizio e che deve essere messo in esecuzione.

• Quando si interagisce con i servizi tramite il superserver (generico) di rete, si identifica il servizio che si vuole attivare attraverso la sua porta (il suo TSAP) well known.

Ovviamente rimane comunque necessario, per il superserver di rete, conoscere quale e’ il file name del programma eseguibile (filtro Unix) che implementa il servizio che e’ stato richiesto, altrimenti come potrebbe attivarlo?

• E’ ovvio che il superserver non puo’ implementare direttamente nessun servizio, nemmeno se questo e’ sequenziale.

Come la shell il superserver non conosce la semantica e il protocollo dei diversi servizi che offre.

Il superserver deve comunque rimanere in attesa di nuove richieste di servizio, relative ad altri servizi.

Page 13: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

13

File e file descriptor in Unix

• Nel mondo Unix tutte le risorse del sistema, indipendentemente da quale sia il loro tipo reale, sono accedute in modo uniforme:

1. Tramite l’uso di un file descriptor (sono viste come dei file).

2. Tramite uno stesso insieme di system call (circa).

• Un file descriptor e’ una handle che consente ad un processo di accedere alla risorsa reale associata a quel file descriptor.

1. L’associazione di una risorsa reale ad un file descriptor avviene (di norma) tramite l’invocazione della system call open() (ma un filtro eredita i file descriptor su cui operare dal processo padre).

2. La disassociazione di una risorsa reale da un file descriptor avviene tramite l’invocazione della system call close().

• Implementativamente un file descriptor e’ costituito da un intero non negativo di piccole dimensioni (“a small, nonnegative integer”, Linux man page):

attualmente con valore compreso tra 0 e 1023.

• Dal punto di vista del sistema operativo il file descriptor e’ un indice nel vettore User File Descriptor Table contenuto nel descrittore di ciascun processo.

Page 14: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

14

La User File Descriptor Table

• Nella User File Descriptor Table sono contenuti i riferimenti a tutte le risorse reali utilizzate (accedibili) dal processo in un dato momento.

• Quando un processo esegue una system call open() Unix cerca nella User File Descriptor Table la prima entry libera a partire da 0 e la associa alla risorsa reale riferita nella system call open().

• Quando un processo esegue una system call close() Unix dichiara libero il file descriptor riferito nella chiamata della system call.

• Eseguendo la system call fork() tutte le risorse del sistema che erano accedibili dal processo padre rimangono accedibili anche dal processo figlio, e cio’ utilizzando gli stessi valori di file descriptor.

Al momento dell’esecuzione della system call fork() la User File Descriptor Table del processo padre viene copiata nella User File Descriptor Table del processo figlio.

• Dal punto di vista dell’utente e’ possibile clonare un file descriptor su un altro file descriptor tramite una delle due system call dup() e dup2().

Page 15: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

15

Unix system programming• int dup(int fd);

• fd è il file descriptor da duplicare

• L’effetto di una invocazione di dup() è di copiare l’elemento fd della tabella dei file aperti nella prima posizione libera (quella con l’indice minimo tra quelle disponibili).

• Restituisce il nuovo file descriptor (quello destinazione dell’operazione di copiatura), oppure -1 (in caso di errore).

• int dup2(int oldfd, int newfd);

• oldfd è il file descriptor da duplicare.

• newfd è il file descriptor in cui deve essere duplicato.

• L’effetto di una invocazione di dup2() è di copiare l’elemento oldfd della tabella dei file aperti nell’elemento newfd della stessa tabella.

• N.B. se newfd era gia’ in uso al momento dell’invocazione di dup2() esso viene implicitamente chiuso (tramite close()).

• Restituisce newfd oppure -1 (in caso di errore).

Page 16: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

16

Protocollo di attivazione di un filtro Unix

• Per attivare un servizio standard (un filtro Unix), il processo padre (e.g. una shell) deve duplicare se’ stesso tramite l’invocazione della system call fork() per poi mettere in esecuzione nel suo clone figlio il codice eseguibile del programma filtro.

• Per attivare correttamente un filtro Unix e’ quindi sufficiente che il processo padre:

• Si cloni tramite chiamata alla system call fork().

• Nel processo figlio dup-lichi sui file descriptor 0 e 1 i file descriptor (le risorse reali) su cui vuole fare lavorare il server-figlio.

(e chiuda tutti gli altri file descriptor che non interessano al server-figlio)

• Nel processo clone figlio metta in esecuzione il codice del filtro tramite invocazione della system call exec().

• N.B.: I file descriptor 0 e 1 sono come due parametri formali a cui il chiamante associa come parametri attuali le risorse reali che il filtro Unix deve utilizzare per input e output.

Page 17: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

17

La system call fork()

int fork(void);

• Crea un nuovo processo che e’ un clone esatto del processo chiamante (padre).

• Il nuovo processo (figlio):

• Esegue lo stesso codice del processo padre e, al termine della system call, ha il program counter posizionato all’istruzione successiva a quella contenente l’invocazione della system call fork().

• Come spazio dati ha una copia di quello del processo padre.

• Condivide le risorse di sistema accedibili dal processo padre: la sua User File Descriptor Table e’ una copia di quella del processo padre.

• La system call fork() ritorna:

• In caso di errore, un numero negativo al processo padre(il processo figlio non e’ nemmeno stato creato).

• In caso di terminazione corretta (esecuzione con successo), • Il PID del processo figlio al processo padre,• 0 al processo figlio.

Cio’ permette di capire, al ritorno dalla fork(), se si e’ il processo padre oppure il processo figlio.

Page 18: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

18

Il superserver inetd di Unix• Il superserver inetd e’ un demone (un processo attivato dal sistema alla

partenza e che rimane sempre attivo).

• inetd ha 3 scopi (il terzo conseguenza del secondo):

1. Evitare che tutti i diversi server di rete del sistema debbano essere attivi in permanenza. inetd rimane in attesa delle richieste dei clienti al posto dei (di tutti i)

singoli server, e attiva un server solo al momento in cui c’e’ effettivamente una richiesta di servizio pendente per lui.

2. Consentire ai singoli server di ignorare l’interazione con la rete, comportandosi (essendo implementati) come normali filtri Unix.

3. Consentire quindi di esportare sulla rete qualunque servizio implementato come filtro Unix (in particolare, servizi gia’ esistenti).

• Per fare questo inetd deve conoscere quali sono i servizi di rete definiti sul sistema:

• Su quale servizio di trasporto (TCP o UDP) e su quale porta well known e’ offerto un servizio.

• Quale e’ il file eseguibile (filtro) che implementa il servizio.

• Il superserver acquisisce le informazioni sui servizi che deve supportare da un file di configurazione.

Page 19: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

19

Il file di configurazione del superserver inetdAssumeremo che la sintassi del file di configurazione sia la seguente:

• Ogni riga del file descrive un servizio.

• Ogni servizio e’ descritto da una riga che ha la sintassi seguente:

<servizio> ::= <filter pathName><transport protocol><port number><wait flag><argument list>

• <filter pathName> e’ il pathname del codice eseguibile che implementa il servizio (come filtro).

• <transport protocol> puo’ assumere i valori tcp o udp.

• <port number> e’ il numero della porta well known che identifica il servizio sulla rete.

• <wait flag> indica se il servizio deve essere offerto in forma sequenziale (wait) o concorrente (nowait).

• <argument list> specifica una lista di parametri (N.B.: uguali per ogni invocazione) che il superserver deve passare al filtro che implementa il servizio nel momento in cui lo attiva.

Page 20: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

20

Il file di configurazione del superserver: esercizio

• Nota che ci potrebbero essere diversi parametri che caratterizzano un Servizio Applicativo dal punto di vista del suo utilizzo del Servizio di Trasporto.

• Quali potrebbero essere ad esempio dei parametri significativi di Trasporto specifici di ciascun Servizio Applicativo nel caso di

• servizi Applicativi basati sul Servizio di Trasporto TCP?

• servizi Applicativi basati sul Servizio di Trasporto UDP?

• Come dovrebbe essere modificata di conseguenza la struttura del file di configurazione del superserver?

Page 21: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

21

Esempio di file di configurazione del superserver inetd

/bin/cat tcp 10001 nowait

/bin/csh tcp 10002 nowait -i

/bin/java tcp 10003 wait prog1 arg1

• La prima linea descrive il servizio cat (copiatura da file a file) su TCP che utilizza la porta 10001 e funziona in modalita’ concorrente.N.B.: questo servizio essendo file di input e di output associati ad un unico socket implementa sulla rete un servizio di eco.

• La seconda linea descrive il servizio shell (csh) su TCP che utilizza la porta 10002, funziona in modalita’ concorrente e ha come parametro di ingresso “-i”.

• La terza linea descrive il servizio java prog1 su TCP che utilizza la porta 10003, funziona in modalita’ sequenziale e ha come parametro di ingresso “arg1”. Attenzione: per i servizi implementati in Java il nome dell’eseguibile

che deve essere messo in esecuzione dal superserver e’ quello della Java VM, alla quale occorre passare il nome del servizio effettivo (prog1 nel nostro caso), piu’ gli eventuali parametri.

Page 22: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

22

Servizi sequenziali e servizi concorrenti .1

• Ogni servizio, implementato come un filtro, ignora la nozione di servizio sequenziale/concorrente.

• E’ il superserver che e’ responsabile, per ogni servizio, di implementare questa nozione, in base all’indicazione relativa presente nel file di configurazione.

N.B.: e’ ovvio che il superserver non puo’ comunque entrare nei dialoghi applicativi relativi ai diversi servizi, visto che non ne conosce nemmeno le regole.

• Se un servizio e’ definito come concorrente il superserver, anche dopo avere attivato una istanza del servizio, continuera’ ad attendere sulla porta well known del servizio richieste di altri clienti.

• Se un servizio e’ definito come sequenziale il superserver, una volta attivata una istanza del servizio, non attendera’ piu’ alcun evento (nuova richiesta di servizio) sulla porta well known del servizio fino a che l’istanza precedente non sia terminata.

Nel caso di un servizio sequenziale il superserver deve avere la possibilita’ di capire quando il filtro che ha attivato per implementare il servizio ha terminato la sua esecuzione.

Page 23: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

23

Servizi sequenziali e servizi concorrenti .2

• Il superserver, dopo avere attivato una istanza di un servizio S, deve comunque rimanere attivo, anche se S e’ un servizio sequenziale: Il superserver deve comunque gestire nuove richieste, provenienti

dalla rete, destinate ad S o ad un altro servizio.

• Se S e’ un servizio concorrente, l’istanza di S e il superserver saranno eseguiti in parallelo e in modo completamente indipendente. Nessuno dei due ha piu’ bisogno di avere a che fare con l’altro.

• Se pero’ S e’ un servizio sequenziale il superserver deve essere informato della terminazione dell’istanza di S che ha attivato.• S e’ implementato da un normale filtro, sia che esso sia concorrente

sia che esso sia sequenziale!• In Unix ci sono normali meccanismi di sistema operativo che

consentono ad un processo figlio di informare il processo padre della propria terminazione e anche di ritornargli un valore (intero)!

• L ’esecuzione della system call exit(n) o dell’istruzione return(n) nella funzione main() del processo figlio lo terminano e consentono di informare il padre di cio’ e di passargli il valore n.

Page 24: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

24

Sincronizzazione del superserver con i processi figli .1

• In realta’ il superserver deve comunque gestire in qualche modo anche la morte di figli che implementano un servizio concorrente, altrimenti questi rimarrebbero nel sistema come zombi. Basta pero’ che il processo padre prenda atto della morte di un

processo figlio per evitare che questo diventi uno zombi.

• Il superserver puo’ prendere atto della morte di un processo figlio invocando la system call wait().

• La system call wait() permette di ricavare il PID (quindi l’identita’) di un figlio che muore e anche la ragione di questa morte. L’interfaccia della funzione e’:

int wait(int* err);

• Il valore di ritorno e’ il PID del processo figlio che e’ morto.

• Il parametro di ritorno err fornisce lo stato di terminazione del figlio (in errore e, se del caso, quale, o meno). err e’ il valore ritornato dal processo figlio come argomento

della system call exit() o dell’istruzione return() che lo ha terminato.

Page 25: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

25

Sincronizzazione del superserver con i processi figli .2

• Poiche’ la system call wait() e’ bloccante, il superserver deve chiamarla solo quando e’ sicuro della morte (gia’ avvenuta) di un processo figlio.

• Altrimenti l’intero superserver rimarrebbe bloccato in attesa della morte di un figlio, e verrebbe ad assumere il comportamento di superserver super-sequenziale!

• Il superserver e’ informato (in modo asincrono) della morte di un processo figlio perche’, quando questo evento accade, il sistema operativo gli invia il segnale (interrupt software) SIGCLD (detto anche SIGCHLD).

• Per poter catturare effettivamente il segnale, il superserver deve registrare sul sistema operativo, tramite chiamata della system call signal(), il suo interesse per l’evento (se non lo fa, il segnale non gli e’ inviato).

Page 26: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

26

Unix system programming• void (* signal(int sig, void (*func)()))(int);

• sig è l’intero (o il nome simbolico) che individua il segnale da gestire

• il parametro func è un puntatore a una funzione che indica l’azione da associare al segnale; in particolare func può:

• puntare alla routine di gestione dell’interruzione (handler)

• valere SIG_IGN (nel caso di segnale ignorato)

• valere SIG_DFL (nel caso di azione di default)

• ritorna un puntatore a funzione:

• al precedente gestore del segnale

• SIG_ERR (-1), nel caso di errore

• Una funzione che e’ uno handler di segnali per un processo deve avere il seguente prototipo:

void signalHandler(int sig);

Page 27: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

27

Struttura di uno handler di segnali• Il parametro di ingresso della di un signal handler e’ l’identificatore

numerico del segnale che e’ scattato: cio’ permette di registrare la stessa funzione per piu’ segnali e di gestire poi uno switch in funzione del segnale che si e’ manifestato.

• Nel nostro caso lo handler deve gestire solo il segnale SIGCLD.

• Per fare questo nel proprio corpo invochera’ la system call wait() che, dato il contesto della chiamata, sara’ non bloccante.

• Nel caso di morte di un figlio relativo ad un servizio concorrente la presa d’atto del fatto, tramite chiamata a wait(), e’ gia’ sufficiente ad evitare che il figlio morto diventi uno zombi.

• Nel caso di morte di un figlio relativo ad un servizio sequenziale il superserver dovra’ anche riattivare il servizio, dovra’ cioe’ tornare ad aspettare richieste di nuovi clienti sulla relativa porta well known.

• Lo handler viene attivato in seguito alla ricezione di uno dei segnali a cui e’ stato associato, e viene visto dal sistema come una thread secondaria del processo, in aggiunta a quella principale (che potrebbe eventualmente essere stata interrotta!).

• In uscita un signal handler non ritorna alcun valore.

Page 28: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

28

Aspetti particolari dei segnali e di SIGCLD

• Un aspetto particolare della chiamata alla funzione wait() e’ che essa permette di rimuovere i processi terminati dallo stato di zombi.

Infatti tutti i processi, quando terminano, vengono posti dal sistema operativo nello stato di zombi, in cui rimangono o fino alla morte del processo che li ha creati o fino a che questo prende atto dell’evento tramite la chiamata della funzione wait().

• Bisogna anche osservare che la ricezione di un segnale da parte di un processo interrompe l’esecuzione di una system call eventualmente in corso (bloccata): in questo caso la system call termina con codice di errore EINTR.

• Poiche’ il superserver deve gestire il segnale di SIGCLD deve prendere in considerazione e gestire questa possibilita’.

La cosa e’ vera in particolare per la system call select(), che e’ bloccante e che costituisce il cuore del superserver.

Infatti il superserver deve rimanere in attesa di richieste, contemporaneamente, sulle porte well known di tutti i serviziche esso supporta (e che sono attivi in quel momento).

Page 29: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

29

Gestione del ritorno da select()

. . .

rs = select(. . ., NULL);

if (rs<0) {

if (errno == EINTR) {

// morte di un figlio, non e’ un

// errore vero, ma non c’e’ neanche

// nessun socket pronto

. . .

} else {

// e’ un errore vero

. . .

}

}

// tutto OK e ci sono socket pronti

. . .

Page 30: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

30

socket()

bind()

listen()

(solo per TCP)

Per ogni servizio listato nel file di configurazione

Test per letturaselect()

accept()

(solo per TCP)

fork()

close socketfd (solo per TCP)

Se wait-flag==wait elimina dalla select il fd associato al servizio

padre close di tutti i fd diversi da quello del socket;

dup(sfd), dup(sfd) e dup(sfd);

figlio

execle() del server-program

Per gestire la morte di un figlio. Alla morte di un figlio se il servizio era di tipo wait occorre reinserire nella select il socket-descriptor associato al servizio

signal()

Ciclo infinito

Schema del superserver inetd

Page 31: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

31

La system call exec()

• Negli schemi standard dei server di rete concorrenti visti a lezione il server padre, quando deve servire un nuovo cliente, esegue il fork() di un processo figlio, che puo’ immediatamente procedere a fornire il servizio al cliente perche’ il suo codice e’ identico a quello del padre.

• Nel caso del superserver, pero’, il padre non contiene il codice capace di implementare ciascuno dei singoli servizi che esso offre, ma solo quello per la loro attivazione.

• Come puo’ il superserver arrivare a fornire il servizio richiesto dal cliente?

• Ci riesce perche’ (come una shell) il clone figlio del superserver non continua ad eseguire lo stesso codice del superserver ma, dopo una fase iniziale di housekeeping, mette in esecuzione nel proprio contesto un nuovo programma, quello del filtro Unix che implementa effettivamente il servizio desiderato dal cliente.

N.B.: e’ evidente che tutto questo meccanismo si basa sulla nozione di porta well known (che indentifica il servizio desiderato)!

Page 32: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

32

La system call exec()

• La funzione execle() permette di mettere in esecuzione, nel contesto del processo chiamante (cioe’ mantenendo il possesso delle stesse risorse reali possedute dal processo chiamante), un ben determinato programma.

• Noi la useremo nel processo clone-figlio per specializzarlo a fornire il servizio specifico per il quale e’ stato creato (sia per servizi concorrenti che per servizi sequenziali).

• La system call execle() e’ una funzione ad argomenti variabili di cui i primi due sono obbligatori e sono:

• nome del file contenente il programma eseguibile che si vuole eseguire, comprensivo dell’intero path;

• nome del programma.

segue poi la lista degli argomenti terminata da un NULL, infine l’ultimo argomento e’ un puntatore all’enviroment.

• Quindi la chiamata:

execle(“/bin/csh”, “csh”, “-i”, NULL, env);

mette in esecuzione il programma csh passandogli il parametro “-i”.

Page 33: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

33

La system call execle()

int execle(char *pathname, char *arg0, …,

char *argn, (char *)NULL, char **envp);

• La funzione, in effetti, ritorna al chiamante solo in caso di errore!

• Se non c’e’ errore, l’effetto della system call e’ di passare il controllo alla prima istruzione del programma contenuto nel file pathname.

• Dal punto di vista C la modalita’ di passaggio degli argomenti arg0 .. argn equivale a passare un array NULL-terminato di puntatori, come e’ in effetti il parametro di ingresso argv della funzione main().

• arg0 per convenzione deve essere il nome del programma, come indicato alla pagina precedente.

• L’ultimo parametro e’ un puntatore ad una esplicita environment list, che e’ a sua volta implementata come un array NULL-terminato di puntatori a stringa.

• Quello che faremo e’ fare ereditare al processo figlio lo stesso environment del superserver.

Page 34: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

34

Struttura del superserver .1

1. Il parametro di ingresso envp della funzione main()int main (int argc, char **argv, char **envp); ci consente di procurarci il riferimento envp all’environment da utilizzare in execle().

2. La funzione main() del programma apre in lettura il file di configurazione.

3. Per ogni linea di tale file ricava i parametri del servizio descritto in quella linea e li salva in una opportuna struttura dati:

tutti i servizi sono inizialmente definiti come attivi.

4. Per ogni servizio, crea un socket e lo bind()-a alla porta well known su cui quel servizio e’ offerto (quella che e’ indicata nel file di configurazione, e il cui numero deve essere congruente con le indicazioni IANA).

5. Se il servizio utilizza il trasporto TCP il superserver mette anche in stato listen()-ing il socket relativo.

Page 35: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

35

Struttura del superserver .2

7. Anche il file descriptor del socket well known deve essere salvato nella struttura dati di cui sopra.

8. Il superserver chiama la system call signal() per registrare il proprio interesse a gestire il segnale SIGCLD.

9. Terminata l’inizializzazione il superserver entra in un ciclo infinito in cui, tramite chiamata della system call select(), attende l’arrivo di nuove richieste di servizio da nuovi clienti;

quando queste arrivano, attiva per ciascuna di esse (per il tramite di un corrispondente processo figlio) l’opportuno server specifico (che e’/deve essere strutturato come un filtro Unix).

7. All’interno del ciclo (reinizializza e) riempie il parametro di ingresso readfds della select() con tutti e soli i file descriptor dei servizi attivi.

Se un servizio nowait e’ gia’ in corso di fornitura, esso non puo’ essere nuovamente attivato in questo momento, e quindi non e’ attivo.

Page 36: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

36

Struttura del superserver .3

11. Al termine della select(), trascurando i casi di errore, scandisce il parametro di ritorno readfds (N.B. quindi readfds e’ un parametro value-result!) che lista tutti i file descriptor sui quali e’ pendente una nuova richiesta di servizio da parte di un nuovo cliente. Per ciascuna di queste richieste mette in funzione il server specifico.

12. Se il servizio specifico utilizza il servizio di trasporto TCP, chiama la system call accept() per prendere in carico la nuova connessione e ricavare il corrispondente socket descriptor nfd: ad esso e’ associata la nuova richiesta di servizio.

13. Se il servizio specifico utilizza il servizio di trasporto UDP, registra come file descriptor nfd sul quale interagire con il cliente il file descriptor della relativa porta well known (ma e’ possibile? No!).

11.Ma nel caso di un servizio UDP concorrente lo schema visto a lezione prevede che il servizio sia effettivamente fornito su una porta diversa da quella well known, altrimenti sarebbe impossibile utilizzare la porta well known per aspettare nuovi clienti! Allora?

Page 37: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

37

Struttura del superserver .414. Chiama la system call fork() per generare il processo che fornira’

effettivamente il servizio alla richiesta corrente.

15. Nel ramo del padre, se il servizio considerato e’ di tipo TCP :

• Chiude il socket nfd.

• Se il servizio considerato e’ di tipo wait allora registra nella struttura dati di registrazione dei servizi il PID del processo figlio e marca il servizio come non attivo(se il servizio considerato e’ di tipo nowait non c’e’ niente di specifico da fare).

• Passa a considerare la prossima richiesta pendente o, se non ce ne sono piu’, ritorna all’inizio del ciclo principale.

16. Nel ramo del figlio:

14.Chiude i file descriptor 0 e 1.

15.Duplica sui file descriptor 0 e 1 il file descriptor nfd.

16.Chiude tutti i file descriptor del processo ad eccezione di 0, 1 e 2.

17.Chiama la system call execle() per attivare il servizio richiesto.

Page 38: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

38

Struttura dati di registrazione dei servizi

• Ci sono svariate modalita’ per salvare le informazioni relative ai servizi che devono essere supportati.

• La migliore e’ quella di utilizzare una coda linkata (doppiamente) di strutture (ogni struttura descrive un singolo servizio).

Questa soluzione ha il vantaggio di non porre limiti al numero massimo di servizi supportabili dal superserver

• Un’altra possibilita’ e’ quella di utilizzare un vettore di strutture.

Questa soluzione (quella consigliata) ha lo svantaggio di limitare ad un valore massimo il numero di servizi supportabili.

Page 39: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

39

Esempio di struttura dati di registrazione dei servizi .1

typedef struct SrvElmT {

char tr[4];

// tcp se il servizio usa il trasporto TCP,

// udp se usa il trasporto UDP

char conc[7];

// wait se il servizio e' di tipo sequenziale,

// nowait altrimenti

char port[8];

char srvFullName[500];

// pathname completo del file di codice eseguibile

// del programma

char srvName[20];

// nome del programma

// continua . . .

Page 40: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

40

Esempio di struttura dati di registrazione dei servizi .2

// . . . Continua:

int numArgs;

// numero di argomenti da passare alla execle,

// 0 in caso di assenza di essi

char argv1[20];

char argv2[20];

char argv3[20];

char argv4[20];

char argv5[20];

int fd;

// socket descriptor associato alla porta well

// known del servizio

int pid;

// significativo solo per servizi di tipo wait (?)

} SrvElmT, *SrvElmTP;

SrvElmT srvArray[20]; // massimo 20 servizi

Page 41: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

41

Servizi UDP

• I servizi UDP non possono essere trattati in modo analogo a quelli TCP.

• La ragione di fondo e’ che mentre in TCP un socket rappresenta una connessione (e quindi una relazione gia’ instaurata con un singolo cliente), in UDP esso rappresenta una porta (e quindi c’e’ l’eventualita’ di potere/dovere interagire su quella porta con piu’ clienti, vecchi e nuovi).

• Diverse conseguenze sul superserver:

• Un servizio concorrente UDP non puo’ essere trattato in modo analogo ad un servizio concorrente TCP. Se mantenesse il servizio attivo, alla prossima select() il

superserver potrebbe trovare gia’ pendente come nuova richiesta quella del servizio appena attivato, perche’ il datagram che aveva richiesto il servizio all’iterazione precedente potrebbe essere ancora da consumare.

• Per relizzare un servizio UDP sequenziale come un filtro Unix (ma con interazioni a messaggio, non a stream!) bisogna gestire il fatto che il server filtro deve interagire solo con un singolo cliente e non puo’ farlo esplicitamente (cioe’ eliminando lui le interazioni non volute sulla porta well known: i filtri Unix non lo fanno!).

Page 42: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

42

Servizio CL concorrente .1

SuperServerp

socket server di associazione

porta UDP server well-known

Client UDP

socket client

porta UDP effimera

Page 43: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

43

Servizio CL concorrente .2

SuperServerp

socket server di associazione

porta UDP server well-known

Client UDP

socket client

porta UDP effimera

socket server di comunicazione

porta UDP server effimera

Page 44: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

44

Servizio CL concorrente .3

SuperServerp

socket server di associazione

porta UDP server well-known

Client UDP

socket client

porta UDP effimera

Server Figlio

socket server di comunicazione

porta UDP server effimera

fork()

• Il server figlio, appena attivato, dopo la exec(), potrebbe andare subito a leggere il datagram pendente nel socket well known, ma chi dice che arriverebbe a farlo prima della nuova select() di SuperServerp

• E comunque, questo non sarebbe il comportamento di un filtro Unix!

Page 45: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

45

Servizi UDP implementati tramite filtri Unix .1

• Il servizio deve interagire con un singolo cliente, senza conoscerne necessariamente l’identita’.

Non deve nemmeno sapere che sta interagendo con il cliente tramite un socket UDP!

• Poiche’ deve poter ignorare l’identita’ del cliente remoto (infatti opera sui file descriptor 0 e 1 tramite operazioni di read() e write()) bisogna che il socket UDP che e’ associato ai file descriptor 0 e 1 sia gia’ connesso al cliente!

• E’ il superserver che deve passare al server specifico una porta UDP gia’ connessa!

• Ma per un server UDP concorrente la porta con cui interagire con il cliente non puo’ essere la porta well known, deve essere una porta effimera.

• Quindi e’ questa porta che deve essere passata al server specifico come associata ai file descriptor 0 e 1, non la porta well known.

• Nota che la disciplina di utilizzo delle porte che va bene per un server UDP concorrente si puo’ applicare altrettanto bene ad un server sequenziale!

Page 46: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

46

Servizio CL concorrente .1

SuperServerp

socket server di associazione

porta UDP server well-known

Client UDP

socket client

porta UDP effimera

Page 47: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

47

Servizio CL concorrente .2

SuperServerp

socket server di associazione

porta UDP server well-known

Client UDP

socket client

porta UDP effimera

socket server di comunicazione

porta UDP server effimera

Page 48: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

48

Servizio CL concorrente .3

SuperServerp ?

socket server di associazione

porta UDP server well-known ?

Client UDP

socket client

porta UDP effimera

socket server di comunicazione

porta UDP server effimera

recvfrom(MSG_PEEK?)

Page 49: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

49

Servizio CL concorrente .4

SuperServerp ?

socket server di associazione

porta UDP server well-known ?

Client UDP

socket client

porta UDP effimera

socket server di comunicazione

porta UDP server effimera

connect(socket client)

Page 50: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

50

Servizio CL concorrente .5

SuperServerp ?

socket server di associazione

porta UDP server well-known ?

Client UDP

socket client

porta UDP effimera

SuperServerf ?

socket server di comunicazione

porta UDP server effimera

fork()

Page 51: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

51

Servizio CL concorrente .6

SuperServerp ?

socket server di associazione

porta UDP server well-known ?

Client UDP

socket client

porta UDP effimera

Server Figlio

socket server di comunicazione

porta UDP server effimera

• Il server figlio, dopo la exec(), non ha comunque piu’ il messaggio nella sua memoria dati centrale

• E ovviamente, secondo il protocollo di attivazione di un server-filtro Unix, non ha nemmeno piu’ accesso alla porta well known

• Come fa a vedere il messaggio?

exec()

Page 52: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

52

Servizi UDP implementati tramite filtri Unix .2• Pero’ il datagram del cliente che ha portato all’attivazione del servizio

UDP e’ registrato nella porta well known, non nella porta effimera che vogliamo passare al server specifico.

Come puo’ il server specifico andarlo ad accedere?

Oltretutto la sua presenza nella porta well known puo’ costituire un problema per il superserver che, nel caso di un servizio UDP concorrente, deve utilizzare la porta well known per aspettare richieste di nuovi clienti e non deve considerare 2 volte una stessa richiesta.

• Quindi bisogna che il superserver estragga il datagram UDP dalla porta well known e lo inserisca nella porta effimera prima di attivare il servizio.

• N.B.: L’utilizzo di una seconda porta (effimera) per interagire con il client e’ strettamente necessario solo per realizzare un servizio UDP concorrente, ma torna comodo anche nel caso di un servizio UDP sequenziale.

Page 53: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

53

Servizio CL concorrente .1

SuperServerp

socket server di associazione

porta UDP server well-known

Client UDP

socket client

porta UDP effimera

Page 54: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

54

Servizio CL concorrente .2

SuperServerp

socket server di associazione

porta UDP server well-known

Client UDP

socket client

porta UDP effimera

socket server di comunicazione

porta UDP server effimera

Page 55: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

55

Servizio CL concorrente .3

SuperServerp

socket server di associazione

porta UDP server well-known

Client UDP

socket client

porta UDP effimera

socket server di comunicazione

porta UDP server effimera

recvfrom()

Page 56: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

56

Servizio CL concorrente .4

SuperServerp

socket server di associazione

porta UDP server well-known

Client UDP

socket client

porta UDP effimera

socket server di comunicazione

porta UDP server effimera

sendto(porta server effimera)

Page 57: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

57

Servizio CL concorrente .5

SuperServerp

socket server di associazione

porta UDP server well-known

Client UDP

socket client

porta UDP effimera

socket server di comunicazione

porta UDP server effimera

connect(socket client)

Page 58: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

58

Servizio CL concorrente .6

SuperServerp

socket server di associazione

porta UDP server well-known

Client UDP

socket client

porta UDP effimera

SuperServerf

socket server di comunicazione

porta UDP server effimera

fork()

Page 59: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

59

Servizio CL concorrente .7

SuperServerp

socket server di associazione

porta UDP server well-known

Client UDP

socket client

porta UDP effimera

Server Figlio

socket server di comunicazione

porta UDP server effimera

exec()

Page 60: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

60

bind(), INADDR_ANY, e porte effimere

• Quando il superserver utilizza la system call bind() per asssociare il socket server UDP di comunicazione ad una porta effimera, assegna al parametro *myaddr il valore 0.0.0.0:0 (cioe’ INADDR_ANY:0).

• Notare che l’indirizzo 0.0.0.0:0 non e’ un indirizzo valido (vero), e che non e’ utilizzabile come indirizzo destinazione in una operazione di rete:

INADDR_ANY non e’ un indirizzo di rete valido. La porta numero 0 non esiste.

• Effettuare il bind all’indirizzo 0.0.0.0:0 non significa chiedere effettivamente l’associazione a questo indirizzo (che non e’ un indirizzo valido), ma chiedere l’associazione a una qualunque porta effimera libera e a tutti gli indirizzi IP della macchina.

• Nella system call bind() il parametro *myaddr e’ di ingresso, non di ingresso uscita.

• Ma allora come posso sapere a quale porta effimera il socket e’ stato effettivamente associato, cosi’ da potergli inviare il datagram UDP del cliente?

Vedi dispense, parte teoria.

• E quale indirizzo IP devo utilizzare come destinazione della sendto()? Ovviamente 127.0.0.1! (perche’?)

Page 61: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

61

Servizi UDP implementati tramite filtri Unix .3

1. Il superserver acquisisce l’indirizzo del cliente e il datagram UDP che ha attivato il servizio eseguendo una system call recvfrom() sul socket della porta well known.

2. Il superserver crea un nuovo socket e lo bind-a ad una porta effimera.

3. Il superserver invia il datagram UDP che ha portato all’attivazione del servizio alla porta effimera appena creata.

N.B.: per inviare il datagram il superserver puo’ utilizzare sia la porta effimera appena creata (la stessa che e’ destinazione del datagram!) che la porta well known del servizio UDP!

4. Il superserver connette la porta effimera appena creata all’indirizzo del cliente.

N.B.: l’ordine di queste due operazioni e’ fondamentale perche’ se il superserver connettesse la porta effimera prima di inviarle il datagram vedrebbe il suo datagram venire cestinato!

Page 62: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

62

Servizi UDP implementati tramite filtri Unix .4

5. A questo punto il superserver puo’ comportarsi con un servizio UDP cosi’ come si comporta con un servizio TCP, utilizzando il socket descriptor della porta effimera come nfd.

• Vedi punto 13 della struttura del superserver.

6. Nota che avendo gia’ estratto dalla porta well known del servizio UDP il datagram che ha portato a questa attivazione del servizio, il superserver puo’, nel caso di un servizio UDP concorrente, continuare a considerare attivo il servizio anche nelle iterazioni successive, andando ad attendere immediatamente l’arrivo di nuovi datagram di richiesta.

Page 63: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

63

Servizi UDP implementati tramite filtri Unix .5

• Dal punto di vista del server specifico l’unico vincolo che c’e’, nel caso di utilizzo del servizio di trasporto UDP, e’ che bisogna inviare o ricevere un intero PDU applicativo tramite ogni singola operazione di write() e read().

• Il servizio specifico non puo’ per esempio leggere da standard input singoli caratteri se il pari gliene manda molti in un singolo datagram.

• Ma se un server specifico avesse bisogno di sapere chi c’e’ dall’altra parte della rete?

• Ovvio che in questo caso dovrebbe anche sapere a priori che sta interagendo con un cliente remoto tramite il servizio di trasporto UDP.

• Basterebbe che chiamasse la funzione getpeername().

• N.B.: lo stesso discorso si applica ovviamente anche ad un server TCP (che ovviamente interagisce con il pari tramite una connessione. La cosa e’ meno scontata per un server UDP!).

Page 64: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

64

Servizi TCP e setsockopt()

• Immaginiamo il seguente scenario:

• Il superserver riceve una richiesta per il servizio concorrente TCP S definito sulla port well known WK, e di conseguenza attiva il processo figlio PS per offrire il servizio.

• Il servizio e’ ovviamente offerto tramite una connessione che ha come end-point locale la porta WK.

• Mentre il processo PS e’ ancora attivo il superserver termina, per un motivo o per l’altro, e viene riattivato.

• Alla partenza il superserver deve ovviamente appropriarsi di tutte le porte well known che gestisce.

• Questo costituisce un problema per la rete? Ci sono ambiguita’?

• Questo scenario vi ricorda qualcosa descritto parlando di setsockopt()?

• Durante l’implementazione del superserver tenete conto di quanto detto qui.

Page 65: 1 Reti di Calcolatori Esercitazione 1 Implementazione di un superserver Unix di rete Vedi: W.R. Stevens, Unix Network Programming, Prentice Hall Copyright.

65

Esercizio 1: superserver• Realizzare un superserver di rete.

• Il superserver deve essere configurato tramite file di configurazione.

• Il superserver deve essere in grado di gestire sia servizi TCP che servizi UDP, sia servizi concorrenti (nowait) che servizi sequenziali (wait), indipendentemente dal tipo di servizio di trasporto utilizzato.

• Verificare il superserver interagendo con i servizi TCP cat e csh, e con un servizio UDP di echo (da realizzare a partire dagli esempi presentati nelle dispense).

• Come modulo cliente per il test dei servizi TCP si puo’ utilizzare un normale Hyperterm di Windows (o un client TELNET).

• Come modulo cliente per il test dei servizi UDP si puo’ utilizzare il client di echo visto a lezione con gli opportuni adattamenti.

• Attenzione: esiste il pericolo di una corsa critica tra la morte di un server sequenziale figlio e la registrazione di questo server come gia’ attivo da parte del superserver!

Opzionale: Cercate in qualche modo di gestire questa situazione!