Correzione vanga->venga
[gapil.git] / tcpsockadv.tex
index 44986525fc3141b95422a22548adb4ede34b939a..d2e58fe5c0e123eaf7d4379458442e8679fef3f1 100644 (file)
@@ -182,12 +182,14 @@ Riprendiamo allora il codice del client, modificandolo per l'uso di
 di \figref{fig:TCP_ClientEcho_second}, dato che tutto il resto, che riguarda
 le modalità in cui viene stabilita la connessione con il server, resta
 assolutamente identico. La nostra nuova versione di \func{ClientEcho}, la
 di \figref{fig:TCP_ClientEcho_second}, dato che tutto il resto, che riguarda
 le modalità in cui viene stabilita la connessione con il server, resta
 assolutamente identico. La nostra nuova versione di \func{ClientEcho}, la
-terza della serie, è riportata in \figref{fig:TCP_ClientEcho_third}.
-
-In questo caso la funzione comincia (\texttt{\small 8--9}) con la
-cancellazione del file descriptor set \var{fset} e del valore \var{maxfd} da
-passare a \func{select} come massimo per il numero dei file descriptor. Per
-quest'ultimo si usa la macro \code{max} definita nel nostro file
+terza della serie, è riportata in \figref{fig:TCP_ClientEcho_third}, il codice
+completo si trova nel file \file{TCP\_echo\_third.c} dei sorgenti allegati alla
+guida.
+
+In questo caso la funzione comincia (\texttt{\small 8--9}) con l'azzeramento
+del file descriptor set \var{fset} e l'impostazione del valore \var{maxfd}, da
+passare a \func{select} come massimo per il numero di file descriptor. Per
+determinare quest'ultimo si usa la macro \code{max} definita nel nostro file
 \file{macro.h} che raccoglie una collezione di macro di preprocessore di varia
 utilità.
 
 \file{macro.h} che raccoglie una collezione di macro di preprocessore di varia
 utilità.
 
@@ -197,47 +199,106 @@ viene ripetuto indefinitamente. Per ogni ciclo si reinizializza
 file descriptor associato al socket \var{socket} e per lo standard input (il
 cui valore si recupera con la funzione \func{fileno}). Questo è necessario in
 quanto la successiva (\texttt{\small 13}) chiamata a \func{select} comporta
 file descriptor associato al socket \var{socket} e per lo standard input (il
 cui valore si recupera con la funzione \func{fileno}). Questo è necessario in
 quanto la successiva (\texttt{\small 13}) chiamata a \func{select} comporta
-una modifica dei due bit relativi, che quindi devono essere reimpostati.
+una modifica dei due bit relativi, che quindi devono essere reimpostati
+all'inizio di ogni ciclo.
 
 Si noti come la chiamata a \func{select} venga eseguita usando come primo
 
 Si noti come la chiamata a \func{select} venga eseguita usando come primo
-argomento il valore di \var{maxfd}, precedentemente calcolato, passando poi il
-solo file descriptor set per il controllo dell'attività in lettura, gli altri
-argomenti sono tutti passati come puntatori nulli non interessando né il
+argomento il valore di \var{maxfd}, precedentemente calcolato, e passando poi
+il solo file descriptor set per il controllo dell'attività in lettura, negli
+altri argomenti sono passati tutti puntatori nulli, non interessando né il
 controllo delle altre attività, né l'impostazione di un valore di timeout.
 
 controllo delle altre attività, né l'impostazione di un valore di timeout.
 
-Al ritorno di \func{select} si provvede a controllare quale dei file
-descriptor presneta attività, si comincia (\texttt{\small 14--24}) con il file
-descriptor associato allo standard input. In caso di attività (quando cioè
-\macro{FD_ISSET} ritorna una valore diverso da zero) si esegue (\texttt{\small
-  15}) una \func{fgets} per leggere gli eventuali dati presenti; se non ve ne
-sono (e la funzione restituisce pertanto un puntatore nullo) si ritorna
-immediatamente (\texttt{\small 16}) dato che questo significa che si è chiuso
-lo standard input; altrimenti (\texttt{\small 18--22}) si scrivono i dati sul
-socket, uscendo immediatamente in caso di errore di scrittura.
+Al ritorno di \func{select} si provvede a controllare quale dei due file
+descriptor presenta attività in lettura, cominciando (\texttt{\small 14--24})
+con il file descriptor associato allo standard input. In caso di attività
+(quando cioè \macro{FD\_ISSET} ritorna una valore diverso da zero) si esegue
+(\texttt{\small 15}) una \func{fgets} per leggere gli eventuali dati presenti;
+se non ve ne sono (e la funzione restituisce pertanto un puntatore nullo) si
+ritorna immediatamente (\texttt{\small 16}) dato che questo significa che si è
+chiuso lo standard input e quindi concluso l'utilizzo del client; altrimenti
+(\texttt{\small 18--22}) si scrivono i dati appena letti sul socket,
+prevedendo una uscita immediata in caso di errore di scrittura.
 
 Controllato lo standard input si passa a controllare (\texttt{\small 25--40})
 
 Controllato lo standard input si passa a controllare (\texttt{\small 25--40})
-il socket connesso, in caso di attività si esegue (\texttt{\small 26}) subito
+il socket connesso, in caso di attività (\texttt{\small 26}) si esegue subito
 una \func{read} di cui si controlla il valore di ritorno; se questo è negativo
 (\texttt{\small 27--30}) si è avuto un errore e pertanto si esce
 una \func{read} di cui si controlla il valore di ritorno; se questo è negativo
 (\texttt{\small 27--30}) si è avuto un errore e pertanto si esce
-immediatamente segnalandolo, se è nullo (\texttt{\small 31--34}) 
-
-
-\section{Le opzioni dei socket}
-\label{sec:TCP_sock_options}
-
-Dato che la maggior parte delle opzioni dei socket sono relative ai socket
-TCP, ed hanno poi significato analogo quando usate con altri socket, abbiamo
-preferito trattare l'argomento in generale in questa sezione piuttosto che nel
-capitolo dedicato alla trattazione generica dei socket.
-
-
-
-\section{I dati \textit{out-of-band}}
-\label{sec:TCP_urgent_data}
-
-Una caratteristica speciale dei socket TCP è quella della presenza dei
-cosiddetti dati \textit{out-of-band}
-
+immediatamente segnalandolo, se è nullo (\texttt{\small 31--34}) significa che
+il server ha chiuso la connessione, e di nuovo si esce con stampando prima un
+messaggio di avviso, altrimenti (\texttt{\small 35--39}) si effettua la
+terminazione della stringa e la si stampa a sullo standard output (uscendo in
+caso di errore), per ripetere il ciclo da capo.
+
+Con questo meccanismo il programma invece di essere bloccato in lettura sullo
+standard input resta bloccato sulla \func{select}, che ritorna soltanto quando
+viene rilevata attività su uno dei due file descriptor posti sotto controllo.
+Questo di norma avviene solo quando si è scritto qualcosa sullo standard
+input, o quando si riceve dal socket la risposta a quanto si era appena
+scritto. Ma adesso il client diventa capace di accorgersi immediatamente della
+terminazione del server; in tal caso infatti il server chiuderà il socket
+connesso, ed alla ricezione del FIN la funzione \func{select} ritornerà (come
+illustrato in \secref{sec:TCP_sock_select}) segnalando una condizione di end
+of file, per cui il nostro client potrà uscire immediatamente.
+
+Riprendiamo la situazione affrontata in \secref{sec:TCP_server_crash},
+terminando il server durante una connessione, in questo caso quello che
+otterremo, una volta scritta una prima riga ed interrotto il server con un
+\texttt{C-c}, sarà:
+\begin{verbatim}
+[piccardi@gont sources]$ ./echo 192.168.1.1
+Prima riga
+Prima riga
+EOF sul socket
+\end{verbatim}%$
+dove l'ultima riga compare immediatamente dopo aver interrotto il server. Il
+nostro client infatti è in grado di accorgersi immediatamente che il socket
+connesso è stato chiuso ed uscire immediatamente.
+
+Veniamo allora agli altri scenari di terminazione anomala visti in
+\secref{sec:TCP_conn_crash}. Il primo di questi è l'interruzione fisica della
+connessione; in questo caso avremo un comportamento analogo al precedente, in
+cui si scrive una riga e non si riceve risposta dal server e non succede
+niente fino a quando non si riceve un errore di \errcode{EHOSTUNREACH} o
+\errcode{ETIMEDOUT} a seconda dei casi.
+
+La differenza è che stavolta potremo scrivere più righe dopo l'interruzione,
+in quanto il nostro client dopo aver inviato i dati non si bloccherà più nella
+lettura dal socket, ma nella \func{select}; per questo potrà accettare
+ulteriore dati che scriverà di nuovo sul socket, fintanto che c'è spazio sul
+buffer di uscita (ecceduto il quale si bloccherà in scrittura). Si ricordi
+infatti che il client non ha modo di determinare se la connessione è attiva o
+meno (dato che in molte situazioni reali l'inattività può essere temporanea).
+Tra l'altro se si ricollega la rete prima della scadenza del timeout, potremo
+anche verificare come tutto quello che si era scritto viene poi effettivamente
+trasmesso non appena la connessione ridiventa attiva, per cui otterremo
+qualcosa del tipo:
+\begin{verbatim}
+[piccardi@gont sources]$ ./echo 192.168.1.1
+Prima riga
+Prima riga
+Seconda riga dopo l'interruzione
+Terza riga
+Quarta riga
+Seconda riga dopo l'interruzione
+Terza riga
+Quarta riga
+\end{verbatim}
+in cui, una volta riconnessa la rete, tutto quello che abbiamo scritto durante
+il periodo di disconnessione restituito indietro e stampato immediatamente.
+
+Lo stesso comportamento visto in \secref{sec:TCP_server_crash} si riottiene
+nel caso di un crollo completo della macchina su cui sta il server. In questo
+caso di nuovo il client non è in grado di accorgersi di niente dato che si
+suppone che il programma server non venga terminato correttamente, ma si
+blocchi tutto senza la possibilità di avere l'emissione di un segmento FIN che
+segnala la terminazione della connessione. Di nuovo fintanto che la
+connessione non si riattiva )con il riavvio della macchina del server) il
+client non è in grado di fare altro che accettare dell'input e tentare di
+inviarlo. La differenza in questo caso è che non appena la connessione
+ridiventa attiva i dati verranno sì trasmessi, ma essendo state perse tutte le
+informazioni relative alle precedenti connessioni ai tentativi di scrittura
+del client sarà risposto con un segmento RST che provocherà il ritorno di
+\func{select} per la ricezione di un errore di \errcode{ECONNRESET}.
 
 
 \subsection{La funzione \func{shutdown}}
 
 
 \subsection{La funzione \func{shutdown}}
@@ -261,7 +322,240 @@ Questa 
 Il problema che si pone è che se la chiusura del socket è effettuata con la
 funzione \func{close}, come spiegato in \secref{sec:TCP_func_close}, si perde
 ogni possibilità di poter rileggere quanto l'altro capo può continuare a
 Il problema che si pone è che se la chiusura del socket è effettuata con la
 funzione \func{close}, come spiegato in \secref{sec:TCP_func_close}, si perde
 ogni possibilità di poter rileggere quanto l'altro capo può continuare a
-scrivere. Per poter permettere allora 
+scrivere. Per poter permettere allora di segnalare che si è concluso con la
+scrittura, continuando al contempo a leggere quanto può provenire dall'altro
+capo del socket si può allora usare la funzione \funcd{shutdown}, il cui
+prototipo è:
+\begin{prototype}{sys/socket.h}
+{int shutdown(int sockfd, int how)}
+
+Chiude un lato della connessione fra due socket.
+  
+  \bodydesc{La funzione restituisce zero in caso di successo e -1 per un
+    errore, nel qual caso \var{errno} assumerà i valori:
+  \begin{errlist}
+  \item[\errcode{ENOTSOCK}] il file descriptor non corrisponde a un socket.
+  \item[\errcode{ENOTCONN}] il socket non è connesso.
+  \end{errlist}
+  ed inoltre \errval{EBADF}.}
+\end{prototype}
+
+La funzione prende come primo argomento il socket \param{sockfd} su cui si
+vuole operare e come secondo argomento un valore intero \param{how} che indica
+la modalità di chiusura del socket, quest'ultima può prendere soltanto tre
+valori: 
+\begin{basedescript}{\desclabelwidth{2.2cm}\desclabelstyle{\nextlinelabel}}
+\item[\macro{SHUT\_RD}] chiude il lato in lettura del socket, non sarà più
+  possibile leggere dati da esso, tutti gli eventuali dati trasmessi
+  dall'altro capo del socket saranno automaticamente scartati dal kernel, che,
+  in caso di socket TCP, provvederà comunque ad inviare i relativi segmenti di
+  ACK.
+\item[\macro{SHUT\_WR}] chiude il lato in scrittura del socket, non sarà più
+  possibile scrivere dati su di esso. Nel caso di socket TCP la chiamata causa
+  l'emissione di un segmento FIN, secondo la procedura chiamata
+  \textit{half-close}. Tutti i dati presenti nel buffer di scrittura prima
+  della chiamata saranno inviati, seguiti dalla sequenza di chiusura
+  illustrata in \secref{sec:TCP_conn_term}.
+\item[\macro{SHUT\_RDWR}] chiude sia il lato in lettura che quello in
+  scrittura del socket. È equivalente alla chiamata in sequenza con
+  \macro{SHUT\_RD} e \macro{SHUT\_WR}.
+\end{basedescript}
+
+Ci si può chiedere quale sia l'utilità di avere introdotto \macro{SHUT\_RDWR}
+quando questa sembra rendere \funcd{shutdown} del tutto equivalente ad una
+\func{close}. In realtà non è così, esiste infatti un'altra differenza con
+\func{close}, più sottile. Finora infatti non ci siamo presi la briga di
+sottolineare in maniera esplicita che, come per i file e le fifo, anche per i
+socket possono esserci più riferimenti contemporanei ad uno stesso socket. Per
+cui si avrebbe potuto avere l'impressione che sia una corrispondenza univoca
+fra un socket ed il file descriptor con cui vi si accede. Questo non è
+assolutamente vero, (e lo abbiamo già visto nel codice del server di
+\figref{fig:TCP_echo_server_first_code}), ed è invece assolutamente normale
+che, come per gli altri oggetti, ci possano essere più file descriptor che
+fanno riferimento allo stesso socket.
+
+Allora se avviene uno di questi casi quello che succederà è che la chiamata a
+\func{close} darà effettivamente avvio alla sequenza di chiusura di un socket
+soltanto quando il numero di riferimenti a quest'ultimo diventerà nullo.
+Fintanto che ci sono file descriptor che fanno riferimento ad un socket l'uso
+di \func{close} si limiterà a deallocare nel processo corrente il file
+descriptor utilizzato, ma il socket resterà pienamente accessibile attraverso
+tutti gli altri riferimenti. Se torniamo all'esempio originale del server di
+\figref{fig:TCP_echo_server_first_code} abbiamo infatti che ci sono due
+\func{close}, una sul socket connesso nel padre, ed una sul socket in ascolto
+nel figlio, ma queste non effettuano nessuna chiusura reale di detti socket,
+dato che restano altri riferimenti attivi, uno al socket connesso nel figlio
+ed uno a quello in ascolto nel padre.
+
+Questo non avviene affatto se si usa \func{shutdown} con argomento
+\macro{SHUT\_RDWR} al posto di \func{close}; in questo caso infatti la
+chiusura del socket viene effettuata immediatamente, indipendentemente dalla
+presenza di altri riferimenti attivi, e pertanto sarà efficace anche per tutti
+gli altri file descriptor con cui, nello stesso o in altri processi, si fa
+riferimento allo stesso socket.
+
+Il caso più comune di uso di \func{shutdown} è comunque quello della chiusura
+del lato in scrittura, per segnalare all'altro capo della connessione che si è
+concluso l'invio dei dati, restando comunque in grado di ricevere quanto
+questi potrà ancora inviarci. Questo è ad esempio l'uso che ci serve per
+rendere finalmente completo il nostro esempio sul servizio \textit{echo}. Il
+nostro client infatti presenta ancora un problema, che nell'uso che finora ne
+abbiamo fatto non è emerso, ma che ci aspetta dietro l'angolo non appena
+usciamo dall'uso interattivo e proviamo ad eseguirlo redirigendo standard
+input e standard output. Così se eseguiamo:
+\begin{verbatim}
+[piccardi@gont sources]$ ./echo 192.168.1.1 < ../fileadv.tex  > copia
+\end{verbatim}%$
+vedremo che il file \texttt{copia} risulta mancare della parte finale.
+
+Per capire cosa avviene in questo caso occorre tenere presente come avviene la
+comunicazione via rete; quando redirigiamo lo standard input il nostro client
+inizierà a leggere il contenuto del file \texttt{../fileadv.tex} a blocchi di
+dimensione massima pari a \texttt{MAXLINE} per poi scriverlo, alla massima
+velocità consentitagli dalla rete, sul socket. Dato che la connessione è con
+una macchina remota occorre un certo tempo perché i pacchetti vi arrivino,
+vengano processati, e poi tornino indietro. Considerando trascurabile il tempo
+di processo, questo tempo è quello impiegato nella trasmissione via rete, che
+viene detto RTT (da \textit{Round Trip Time}, e può essere stimato con l'uso
+del comando \cmd{ping}. 
+
+A questo punto, se torniamo al codice mostrato in
+\figref{fig:TCP_ClientEcho_third}, possiamo vedere che mentre i pacchetti sono
+in transito sulla rete il client continua a leggere e a scrivere fintanto che
+il file in ingresso finisce. Però che non appena viene ricevuto un end-of-file
+in ingresso il nostro client termina. Nel caso interattivo, in cui si
+inviavano brevi stringhe una alla volta, c'era sempre il tempo di eseguire la
+lettura completa di quanto il server rimandava indietro. In questo caso
+invece, quando il client termina, essendo la comunicazione saturata e a piena
+velocità, ci saranno ancora pacchetti in transito sulla rete che devono
+arrivare al server e poi tornare indietro, ma siccome il client esce
+immediatamente dopo la fine del file in ingresso, questi non faranno a tempo a
+completare il percorso e verranno persi.
+
+Per evitare questo tipo di problema, invece di uscire occorre usare
+\func{shutdown} per effettuare la chiusura del lato in scrittura del socket,
+una volta completata la lettura del file in ingresso. In questo modo il client
+segnalerà al server la chiusura del flusso dei dati, ma potrà continuare a
+leggere quanto il server gli sta ancora inviando indietro fino a quando anche
+lui, riconosciuta la chiusura del socket in scrittura da parte del client,
+effettuerà la chiusura dalla sua parte. Solo alla ricezione della chiusura del
+socket da parte del server si potrà essere sicuri della ricezione di tutti i
+dati e della terminazione effettiva della connessione.
+
+\begin{figure}[!htb]
+  \footnotesize \centering
+  \begin{minipage}[c]{15.6cm}
+    \includecodesample{listati/ClientEcho.c}
+  \end{minipage} 
+  \normalsize
+  \caption{La sezione nel codice della versione finale della funzione
+    \func{ClientEcho}, che usa \func{shutdown} per una conclusione corretta
+    della connessione.}
+  \label{fig:TCP_ClientEcho}
+\end{figure}
+
+Si è allora riportato in \figref{fig:TCP_ClientEcho} la versione finale della
+nostra funzione \func{ClientEcho}, in grado di gestire correttamente l'intero
+flusso di dati fra client e server. Il codice completo del client,
+comprendente la gestione delle opzioni a riga di comando e le istruzioni per
+la creazione della connessione, si trova nel file \file{TCP\_echo.c},
+distribuito coi sorgenti allegati alla guida.
+
+La nuova versione è molto simile alla precedente di
+\figref{fig:TCP_ClientEcho_third}; la prima differenza è l'introduzione
+(\texttt{\small 7}) della variabile \var{eof}, inizializzata ad un valore
+nullo, che serve a mantenere traccia dell'avvenuta conclusione della lettura
+del file in ingresso.
+
+La seconda modifica (\texttt{\small 12--15}) è stata quella di rendere
+subordinato ad un valore nullo di \var{eof} l'impostazione del file descriptor
+set per l'osservazione dello standard input. Se infatti il valore di \var{eof}
+è non nullo significa che si è già raggiunta la fine del file in ingresso ed è
+pertanto inutile continuare a tenere sotto controllo lo standard input nella
+successiva (\texttt{\small 16}) chiamata a \func{select}.
+
+Le maggiori modifiche rispetto alla precedente versione sono invece nella
+gestione (\texttt{\small 18--22}) del caso in cui la lettura con \func{fgets}
+restituisce un valore nullo, indice della fine del file. Questa nella
+precedente versione causava l'immediato ritorno della funzione; in questo caso
+prima (\texttt{\small 19}) si imposta opportunamente \var{eof} ad un valore
+non nullo, dopo di che (\texttt{\small 20}) si effettua la chiusura del lato
+in scrittura del socket con \func{shutdown}. Infine (\texttt{\small 21}) si
+usa la macro \macro{FD\_CLR} per togliere lo standard input dal file
+descriptor set.
+
+In questo modo anche se la lettura del file in ingresso è conclusa, la
+funzione non esce dal ciclo principale (\texttt{\small 11--50}), ma continua
+ad eseguirlo ripetendo la chiamata a \func{select} per tenere sotto controllo
+soltanto il socket connesso, dal quale possono arrivare altri dati, che
+saranno letti (\texttt{\small 31}), ed opportunamente trascritti
+(\texttt{\small 44--48}) sullo standard output.
+
+Il ritorno della funzione, e la conseguente terminazione normale del client,
+viene invece adesso gestito all'interno (\texttt{\small 30--49}) della lettura
+dei dati dal socket; se infatti dalla lettura del socket si riceve una
+condizione di end-of-file, la si tratterà (\texttt{\small 36--43}) in maniera
+diversa a seconda del valore di \var{eof}. Se infatti questa è diversa da zero
+(\texttt{\small 37--39}), essendo stata completata la lettura del file in
+ingresso, vorrà dire che anche il server ha concluso la trasmissione dei dati
+restanti, e si potrà uscire senza errori, altrimenti si stamperà
+(\texttt{\small 40--42}) un messaggio di errore per la chiusura precoce della
+connessione.
+
+
+\subsection{Un server basato sull'I/O multiplexing}
+\label{sec:TCP_serv_select}
+
+Seguendo di nuovo le orme di Stevens in \cite{UNP1} vediamo ora come con
+l'utilizzo dell'I/O multiplexing diventi possibile riscrivere completamente il
+nostro server \textit{echo} con una architettura completamente diversa, in
+modo da evitare di dover creare un nuovo processo tutte le volte che si ha una
+connessione.
+
+La struttura del nuovo server è illustrata in \figref{fig:TCP_echo_multiplex},
+in questo caso avremo un solo processo che ad ogni nuova connessione da parte
+del client sul socket in ascolto inserirà il socket connesso ad essa relativo
+in una opportuna tabella, poi utilizzerà \func{select} per rilevare la
+presenza di dati in arrivo su ciascun socket connesso, riutilizzandolo per
+inviare i dati in risposta.
+
+
+\begin{figure}[htb]
+  \centering
+  \includegraphics[width=13cm]{img/TCPechoMult}
+  \caption{Schema del nuovo server echo basato sull'I/O multiplexing.}
+  \label{fig:TCP_echo_multiplex}
+\end{figure}
+
+
+
+
+\subsection{Un esempio di I/O multiplexing con \func{poll}}
+\label{sec:TCP_serv_poll}
+
+Abbiamo visto in \secref{sec:TCP_serv_select} come creare un server che
+utilizzi l'I/O multiplexing attraverso l'impiego della funzione \func{select},
+ma in \secref{sec:file_multiplexing} abbiamo visto come la funzione
+\func{poll} costituisca una alternativa a \func{select} con delle funzionalità
+migliori, vediamo allora come reimplementare il nostro servizio usando questa
+funzione.
+
+
+\section{Le opzioni dei socket}
+\label{sec:TCP_sock_options}
+
+Dato che la maggior parte delle opzioni dei socket sono relative ai socket
+TCP, ed hanno poi significato analogo quando usate con altri socket, abbiamo
+preferito trattare l'argomento in generale in questa sezione piuttosto che nel
+capitolo dedicato alla trattazione generica dei socket.
+
+
+
+\section{I dati \textit{out-of-band}}
+\label{sec:TCP_urgent_data}
+
+Una caratteristica speciale dei socket TCP è quella della presenza dei
+cosiddetti dati \textit{out-of-band}