Corretti due errori su segnalazione via email
[gapil.git] / tcpsock.tex
index 4c411cda92de4e63aa72b15961219094db18587d..5c618e563e6a3c263323f1fc185aba765a96f58a 100644 (file)
@@ -8,7 +8,7 @@
 %% license is included in the section entitled "GNU Free Documentation
 %% License".
 %%
-\chapter{Socket TCP}
+\chapter{Socket TCP elementari}
 \label{cha:TCP_socket}
 
 In questo capitolo iniziamo ad approfondire la conoscenza dei socket TCP,
@@ -221,7 +221,7 @@ chiusura attiva.  Nella sequenza indicata i dati verrebbero persi, dato che si
 è chiuso il socket dal lato che esegue la chiusura attiva; esistono tuttavia
 situazioni in cui si vuole poter sfruttare questa possibilità, usando una
 procedura che è chiamata \textit{half-close}; torneremo su questo aspetto e su
-come utilizzarlo in \secref{xxx_shutdown}, quando parleremo della funzione
+come utilizzarlo in \secref{sec:TCP_shutdown}, quando parleremo della funzione
 \func{shutdown}.
 
 La emissione del FIN avviene quando il socket viene chiuso, questo però non
@@ -339,8 +339,8 @@ La MSL 
 sulla rete; questo tempo è limitato perché ogni pacchetto IP può essere
 ritrasmesso dai router un numero massimo di volte (detto \textit{hop limit}).
 Il numero di ritrasmissioni consentito è indicato dal campo TTL dell'header di
-IP (per maggiori dettagli vedi \secref{sec:IP_xxx}), e viene decrementato ad
-ogni passaggio da un router; quando si annulla il pacchetto viene scartato.
+IP (per maggiori dettagli vedi \secref{sec:ip_protocol}), e viene decrementato
+ad ogni passaggio da un router; quando si annulla il pacchetto viene scartato.
 Siccome il numero è ad 8 bit il numero massimo di ``\textsl{salti}'' è di 255,
 pertanto anche se il TTL (da \textit{time to live}) non è propriamente un
 limite sul tempo di vita, si stima che un pacchetto IP non possa restare nella
@@ -365,7 +365,7 @@ durata di questo stato.
 
 Il primo dei due motivi precedenti si può capire tornando a
 \figref{fig:TCP_conn_example}: assumendo che l'ultimo ACK della sequenza
-(quello del capo che ha eseguito la chiusura attiva) vanga perso, chi esegue
+(quello del capo che ha eseguito la chiusura attiva) venga perso, chi esegue
 la chiusura passiva non ricevendo risposta rimanderà un ulteriore FIN, per
 questo motivo chi esegue la chiusura attiva deve mantenere lo stato della
 connessione per essere in grado di reinviare l'ACK e chiuderla correttamente.
@@ -635,8 +635,11 @@ precedente in \secref{sec:sock_socket}.
 \subsection{La funzione \func{bind}}
 \label{sec:TCP_func_bind}
 
-La funzione \funcd{bind} assegna un indirizzo locale ad un socket. È usata
-cioè per specificare la prima parte dalla socket pair. Viene usata sul lato
+La funzione \funcd{bind} assegna un indirizzo locale ad un
+socket.\footnote{nel nostro caso la utilizzeremo per socket TCP, ma la
+  funzione è generica e deve essere usata per qualunque tipo di socket
+  \texttt{SOCK\_STREAM} prima che questo possa accettare connessioni.} È usata
+cioè per specificare la prima parte dalla socket pair.  Viene usata sul lato
 server per specificare la porta (e gli eventuali indirizzi locali) su cui poi
 ci si porrà in ascolto. Il prototipo della funzione è il seguente:
 \begin{prototype}{sys/socket.h}
@@ -746,9 +749,17 @@ staticamente a \const{IN6ADRR\_LOOPBACK\_INIT}.
 \label{sec:TCP_func_connect}
 
 La funzione \funcd{connect} è usata da un client TCP per stabilire la
-connessione con un server TCP, il prototipo della funzione è il seguente:
+connessione con un server TCP,\footnote{di nuovo la funzione è generica e
+  supporta vari tipi di socket, la differenza è che per socket senza
+  connessione come quelli di tipo \texttt{SOCK\_DGRAM} la sua chiamata si
+  limiterà ad impostare l'indirizzo dal quale e verso il quale saranno inviati
+  e ricevuti i pacchetti, mentre per socket di tipo \texttt{SOCK\_STREAM} o
+  \texttt{SOCK\_SEQPACKET}, essa attiverà la procedura di avvio (nel caso del
+  TCP il \textit{three-way-handsjake}) della connessione.}  il prototipo della
+funzione è il seguente:
 \begin{prototype}{sys/socket.h}
-{int connect(int sockfd, const struct sockaddr *servaddr, socklen\_t addrlen)}
+  {int connect(int sockfd, const struct sockaddr *servaddr, socklen\_t
+    addrlen)}
   
   Stabilisce una connessione fra due socket.
   
@@ -848,12 +859,14 @@ necessario effettuare una \func{bind}.
 \label{sec:TCP_func_listen}
 
 La funzione \funcd{listen} serve ad usare un socket in modalità passiva, cioè,
-come dice il nome, per metterlo in ascolto di eventuali connessioni; in
-sostanza l'effetto della funzione è di portare il socket dallo stato
-\texttt{CLOSED} a quello \texttt{LISTEN}. In genere si chiama la funzione in
-un server dopo le chiamate a \func{socket} e \func{bind} e prima della
-chiamata ad \func{accept}. Il prototipo della funzione, come definito dalla
-pagina di manuale, è:
+come dice il nome, per metterlo in ascolto di eventuali
+connessioni;\footnote{questa funzione può essere usata con socket che
+  supportino le connessioni, cioè di tipo \texttt{SOCK\_STREAM} o
+  \texttt{SOCK\_SEQPACKET}.} in sostanza l'effetto della funzione è di portare
+il socket dallo stato \texttt{CLOSED} a quello \texttt{LISTEN}. In genere si
+chiama la funzione in un server dopo le chiamate a \func{socket} e \func{bind}
+e prima della chiamata ad \func{accept}. Il prototipo della funzione, come
+definito dalla pagina di manuale, è:
 \begin{prototype}{sys/socket.h}{int listen(int sockfd, int backlog)}
   Pone un socket in attesa di una connessione.
   
@@ -972,10 +985,13 @@ trasparente dal protocollo TCP.
 \label{sec:TCP_func_accept}
 
 La funzione \funcd{accept} è chiamata da un server per gestire la connessione
-una volta che sia stato completato il \textit{three way handshake}, la
-funzione restituisce un nuovo socket descriptor su cui si potrà operare per
-effettuare la comunicazione. Se non ci sono connessioni completate il processo
-viene messo in attesa. Il prototipo della funzione è il seguente:
+una volta che sia stato completato il \textit{three way
+  handshake},\footnote{la funzione è comunque generica ed è utilizzabile su
+  socket di tipo \texttt{SOCK\_STREAM}, \texttt{SOCK\_SEQPACKET} e
+  \texttt{SOCK\_RDM}.} la funzione restituisce un nuovo socket descriptor su
+cui si potrà operare per effettuare la comunicazione. Se non ci sono
+connessioni completate il processo viene messo in attesa. Il prototipo della
+funzione è il seguente:
 \begin{prototype}{sys/socket.h}
 {int accept(int sockfd, struct sockaddr *addr, socklen\_t *addrlen)} 
  
@@ -1034,8 +1050,8 @@ funzione.  Se non ci sono connessioni pendenti da accettare la funzione mette
 in attesa il processo\footnote{a meno che non si sia impostato il socket per
   essere non bloccante (vedi \secref{sec:file_noblocking}), nel qual caso
   ritorna con l'errore \errcode{EAGAIN}.  Torneremo su questa modalità di
-  operazione in \secref{sec:xxx_sock_noblock}.}  fintanto che non ne arriva
-una.
+  operazione in \secref{sec:TCP_sock_multiplexing}.}  fintanto che non ne
+arriva una.
 
 La funzione può essere usata solo con socket che supportino la connessione
 (cioè di tipo \const{SOCK\_STREAM}, \const{SOCK\_SEQPACKET} o
@@ -1083,7 +1099,7 @@ relativi alla socket pair associata ad un certo socket.
 La prima funzione è \funcd{getsockname} e serve ad ottenere l'indirizzo locale
 associato ad un socket; il suo prototipo è:
 \begin{prototype}{sys/socket.h}
-  {int getsockname(int sockfd, struct sockaddr * name, socklen\_t * namelen)}
+  {int getsockname(int sockfd, struct sockaddr *name, socklen\_t *namelen)}
   Legge l'indirizzo locale di un socket.
 
 \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di
@@ -1201,7 +1217,8 @@ si aspetta in una qualunque applicazione client/server.
 
 Per attivare immediatamente l'emissione del FIN e la sequenza di chiusura
 descritta in \secref{sec:TCP_conn_term}, si può invece usare la funzione
-\func{shutdown} su cui torneremo in seguito (vedi \secref{sec:xxx_shutdown}).
+\func{shutdown} su cui torneremo in seguito (vedi
+\secref{sec:TCP_shutdown}).
 
 
 
@@ -1513,33 +1530,33 @@ degli altri esempi.
   \label{fig:TCP_daytime_cunc_server_code}
 \end{figure}
 
-Stavolta (\texttt{\small 21--25}) la funzione \func{accept} è chiamata
+Stavolta (\texttt{\small 21--26}) la funzione \func{accept} è chiamata
 fornendo una struttura di indirizzi in cui saranno ritornati l'indirizzo IP e
 la porta da cui il client effettua la connessione, che in un secondo tempo,
-(\texttt{\small 39--43}), se il logging è abilitato, stamperemo sullo standard
+(\texttt{\small 40--44}), se il logging è abilitato, stamperemo sullo standard
 output.
 
 Quando \func{accept} ritorna il server chiama la funzione \func{fork}
-(\texttt{\small 26--30}) per creare il processo figlio che effettuerà
-(\texttt{\small 31--45}) tutte le operazioni relative a quella connessione,
+(\texttt{\small 27--31}) per creare il processo figlio che effettuerà
+(\texttt{\small 32--46}) tutte le operazioni relative a quella connessione,
 mentre il padre proseguirà l'esecuzione del ciclo principale in attesa di
 ulteriori connessioni.
 
 Si noti come il figlio operi solo sul socket connesso, chiudendo
-immediatamente (\texttt{\small 32}) il socket \var{list\_fd}; mentre il padre
-continua ad operare (\texttt{\small 47}) solo sul socket in ascolto chiudendo
-\var{sock\_fd} al ritorno dalla \func{fork}. Per quanto abbiamo detto in
+immediatamente (\texttt{\small 33}) il socket \var{list\_fd}; mentre il padre
+continua ad operare solo sul socket in ascolto chiudendo (\texttt{\small 48})
+\var{conn\_fd} al ritorno dalla \func{fork}. Per quanto abbiamo detto in
 \secref{sec:TCP_func_close} nessuna delle due chiamate a \func{close} causa
 l'innesco della sequenza di chiusura perché il numero di riferimenti al file
 descriptor non si è annullato.
 
 Infatti subito dopo la creazione del socket \var{list\_fd} ha una referenza, e
-lo stesso vale per \var{sock\_fd} dopo il ritorno di \func{accept}, ma dopo la
+lo stesso vale per \var{conn\_fd} dopo il ritorno di \func{accept}, ma dopo la
 \func{fork} i descrittori vengono duplicati nel padre e nel figlio per cui
 entrambi i socket si trovano con due referenze. Questo fa si che quando il
 padre chiude \var{sock\_fd} esso resta con una referenza da parte del figlio,
 e sarà definitivamente chiuso solo quando quest'ultimo, dopo aver completato
-le sue operazioni, chiamerà (\texttt{\small 44}) la funzione \func{close}.
+le sue operazioni, chiamerà (\texttt{\small 45}) la funzione \func{close}.
 
 In realtà per il figlio non sarebbe necessaria nessuna chiamata a
 \func{close}, in quanto con la \func{exit} finale (\texttt{\small 45}) tutti i
@@ -1558,18 +1575,18 @@ per ogni processo) e soprattutto nessuna delle connessioni con i client
 verrebbe chiusa.
 
 Come per ogni server iterativo il lavoro di risposta viene eseguito
-interamente dal processo figlio. Questo si incarica (\texttt{\small 33}) di
+interamente dal processo figlio. Questo si incarica (\texttt{\small 34}) di
 chiamare \func{time} per leggere il tempo corrente, e di stamparlo
-(\texttt{\small 34}) sulla stringa contenuta in \var{buffer} con l'uso di
+(\texttt{\small 35}) sulla stringa contenuta in \var{buffer} con l'uso di
 \func{snprintf} e \func{ctime}. Poi la stringa viene scritta (\texttt{\small
-  35--38}) sul socket, controllando che non ci siano errori. Anche in questo
+  36--39}) sul socket, controllando che non ci siano errori. Anche in questo
 caso si è evitato il ricorso a \func{FullWrite} in quanto la stringa è
 estremamente breve e verrà senz'altro scritta in un singolo segmento.
 
 Inoltre nel caso sia stato abilitato il \textit{logging} delle connessioni, si
-provvede anche (\texttt{\small 39--42}) a stampare sullo standard output
-l'indirizzo e la porta da cui il client ha effettuato la connessione, usando
-valori contenuti nelle strutture restituite da \func{accept}, eseguendo le
+provvede anche (\texttt{\small 40--43}) a stampare sullo standard output
+l'indirizzo e la porta da cui il client ha effettuato la connessione, usando i
+valori contenuti nelle strutture restituite da \func{accept}, eseguendo le
 opportune conversioni con \func{inet\_ntop} e \func{atohs}.
 
 Ancora una volta l'esempio è estremamente semplificato, si noti come di nuovo
@@ -1632,7 +1649,7 @@ compito del client leggere la risposta del server e stamparla sullo standard
 output.
 
 
-\subsection{Il client: prima versione}
+\subsection{Il client \textit{echo}: prima versione}
 \label{sec:TCP_echo_client}
 
 Il codice della prima versione del client per il servizio \textit{echo},
@@ -1693,7 +1710,7 @@ scriverli su \file{stdout}.
 \begin{figure}[!htb]
   \footnotesize \centering
   \begin{minipage}[c]{15.6cm}
-    \includecodesample{listati/ClientEcho.c}
+    \includecodesample{listati/ClientEcho_first.c}
   \end{minipage} 
   \normalsize
   \caption{Codice della prima versione della funzione \texttt{ClientEcho} per 
@@ -1714,7 +1731,7 @@ programma, usato per
 illustriamo immediatamente.
 
 
-\subsection{Il server: prima versione}
+\subsection{Il server \textit{echo}: prima versione}
 \label{sec:TCPsimp_server_main}
 
 La prima versione del server, contenuta nel file \file{TCP\_echod\_first.c}, è
@@ -1778,15 +1795,15 @@ alla funzione \code{PrintErr}, riportata in \figref{fig:TCP_PrintErr}, al
 posto di \func{perror} per la stampa degli errori. 
 
 Si inizia con il porre (\texttt{\small 37--41}) in ascolto il socket, e poi si
-esegue indefinitamente il ciclo principale (\texttt{\small 42--58}).
-All'interno di questo si ricevono (\texttt{\small 43--46}) le connessioni,
-creando (\texttt{\small 47--50}) un processo figlio per ciascuna di esse.
-Quest'ultimo (\texttt{\small 51--55}), chiuso (\texttt{\small 52}) il
-\textit{listening socket}, esegue (\texttt{\small 53}) la funzione di gestione
-del servizio \code{ServEcho}, ed al ritorno di questa (\texttt{\small 54})
-esce.
-
-Il padre invece si limita (\texttt{\small 56}) a chiudere il \textit{connected
+esegue indefinitamente il ciclo principale (\texttt{\small 42--59}).
+All'interno di questo si ricevono (\texttt{\small 43--47}) le connessioni,
+creando (\texttt{\small 48--51}) un processo figlio per ciascuna di esse.
+Quest'ultimo (\texttt{\small 52--56}), chiuso (\texttt{\small 53}) il
+\textit{listening socket}, esegue (\texttt{\small 54}) la funzione di gestione
+del servizio \code{ServEcho}, ed al ritorno di questa esce (\texttt{\small
+  55}).
+
+Il padre invece si limita (\texttt{\small 57}) a chiudere il \textit{connected
   socket} per ricominciare da capo il ciclo in attesa di nuove connessioni. In
 questo modo si ha un server concorrente. La terminazione del padre non è
 gestita esplicitamente, e deve essere effettuata inviando un segnale al
@@ -2081,8 +2098,8 @@ riscrittura parziale del server, la nuova versione di questo, in cui si sono
 introdotte una serie di nuove opzioni che ci saranno utili per il debug, è
 mostrata in \figref{fig:TCP_echo_server_code_second}, dove si sono riportate
 la sezioni di codice modificate nella seconda versione del programma, il
-sorgente completo di quest'ultimo si trova nel file
-\file{TCP\_echod\_second.c} dei sorgenti allegati alla guida.
+codice completo di quest'ultimo si trova nel file \file{TCP\_echod\_second.c}
+dei sorgenti allegati alla guida.
 
 La prima modifica effettuata è stata quella di introdurre una nuova opzione a
 riga di comando, \texttt{-c}, che permette di richiedere il comportamento
@@ -2142,7 +2159,7 @@ figli stessi e non risentono di \const{SIGCHLD}.
 
 Per questo l'unica modifica sostanziale nel ciclo principale (\texttt{\small
   23--42}), rispetto precedente versione di \figref{fig:TCP_ServEcho_first}, è
-nella sezione (\texttt{\small 26--30}) in cui si effettua la chiamata di
+nella sezione (\texttt{\small 25--31}) in cui si effettua la chiamata di
 \func{accept}.  Quest'ultima viene effettuata (\texttt{\small 26--27})
 all'interno di un ciclo di \code{while}\footnote{la sintassi del C relativa a
   questo ciclo può non essere del tutto chiara. In questo caso infatti si è
@@ -2154,15 +2171,41 @@ altri casi si esce in caso di errore effettivo (\texttt{\small 27--29}),
 altrimenti il programma prosegue.
 
 Si noti che in questa nuova versione si è aggiunta una ulteriore sezione
-(\texttt{\small 31--39}) di aiuto per il debug del programma, che eseguita con
-un controllo (\texttt{\small 31}) sul valore della variabile \var{debugging}
+(\texttt{\small 32--40}) di aiuto per il debug del programma, che eseguita con
+un controllo (\texttt{\small 33}) sul valore della variabile \var{debugging}
 impostato dall'opzione \texttt{-d}. Qualora questo sia nullo, come
-preimpostato, non accade nulla. altrimenti (\texttt{\small 32}) l'indirizzo
+preimpostato, non accade nulla. altrimenti (\texttt{\small 33}) l'indirizzo
 ricevuto da \var{accept} viene convertito in una stringa che poi
-(\texttt{\small 33--38}) viene opportunamente stampata o sullo schermo o nei
+(\texttt{\small 34--39}) viene opportunamente stampata o sullo schermo o nei
 log.
 
+Infine come ulteriore miglioria si è perfezionata la funzione \code{ServEcho},
+sia per tenere conto della nuova funzionalità di debugging, che per effettuare
+un controllo in caso di errore; il codice della nuova versione è mostrato in
+\figref{fig:TCP_ServEcho_second}.
 
+\begin{figure}[!htb] 
+  \footnotesize \centering
+  \begin{minipage}[c]{15.6cm}
+    \includecodesample{listati/ServEcho_second.c}
+  \end{minipage} 
+  \normalsize
+  \caption{Codice della seconda versione della funzione \code{ServEcho} per la
+    gestione del servizio \textit{echo}.}
+  \label{fig:TCP_ServEcho_second}
+\end{figure}
+
+Rispetto alla precedente versione di \figref{fig:TCP_ServEcho_first} in questo
+caso si è provveduto a controllare (\texttt{\small 7--10}) il valore di
+ritorno di \func{read} per rilevare un eventuale errore, in modo da stampare
+(\texttt{\small 8}) un messaggio di errore e ritornare (\texttt{\small 9})
+concludendo la connessione.
+
+Inoltre qualora sia stata attivata la funzionalità di debug (avvalorando
+\var{debugging} tramite l'apposita opzione \texttt{-d}) si provvederà a
+stampare (tenendo conto della modalità di invocazione del server, se
+interattiva o in forma di demone) il numero di byte e la stringa letta dal
+client (\texttt{\small 16--24}).
 
 
 \section{I vari scenari critici}
@@ -2236,7 +2279,7 @@ attraverso la sequenza vista in \secref{sec:TCP_conn_term}, per cui la
 al primo accesso al socket. Nel caso di Linux inoltre, anche qualora si
 modifichi il client per fargli gestire l'invio di un segmento di RST alla
 chiusura dal socket (come suggerito da Stevens in \cite{UNP1}), non si ha
-nessun errore al ritorno di \funcd{accept} quanto un errore di
+nessun errore al ritorno di \funcd{accept}, quanto un errore di
 \errcode{ECONNRESET} al primo tentativo di accesso al socket.
 
 
@@ -2244,7 +2287,7 @@ nessun errore al ritorno di \funcd{accept} quanto un errore di
 \subsection{La terminazione precoce del server}
 \label{sec:TCP_server_crash}
 
-Un secondo caso critico è quello in cui si ha una terminazione prococe del
+Un secondo caso critico è quello in cui si ha una terminazione precoce del
 server, ad esempio perché il programma ha un crash. In tal caso si suppone che
 il processo termini per un errore fatale, cosa che potremo simulare
 inviandogli un segnale di terminazione. La conclusione del processo comporta
@@ -2273,9 +2316,9 @@ errore.
 
 Per capire meglio cosa è successo conviene analizzare il flusso dei pacchetti
 utilizzando un analizzatore di traffico come \cmd{tcpdump}. Il comando
-permette di selezionare, nel treffico di rete generato su una macchina, i
+permette di selezionare, nel traffico di rete generato su una macchina, i
 pacchetti che interessano, stampando a video (o salvando su disco) il loro
-conteuto. Non staremo qui ad entrare nei dettagli dell'uso del programma, che
+contenuto. Non staremo qui ad entrare nei dettagli dell'uso del programma, che
 sono spiegati dalla pagina di manuale; per l'uso che vogliamo farne quello che
 ci interessa è, posizionandosi sulla macchina che fa da client, selezionare
 tutti i pacchetti che sono diretti o provengono dalla macchina che fa da
@@ -2324,7 +2367,7 @@ compattezza.  Il campo \texttt{win} in ogni riga indica la \textit{advertising
   window} di cui parlavamo in \secref{sec:TCP_TCP_opt}. Allora si può
 verificare dall'output del comando come venga appunto realizzata la sequenza
 di pacchetti descritta in \secref{sec:TCP_conn_cre}: prima viene inviato dal
-clinet un primo pacchetto con il SYN che inizia la connessione, a cui il
+client un primo pacchetto con il SYN che inizia la connessione, a cui il
 server risponde dando il ricevuto con un secondo pacchetto, che a sua volta
 porta un SYN, cui il client risponde con un il terzo pacchetto di ricevuto.
 
@@ -2351,7 +2394,7 @@ pacchetti, contraddistinto dalla lettera \texttt{F}, cui seguir
 ACK da parte del client.  
 
 A questo punto la connessione dalla parte del server è chiusa, ed infatti se
-usiamo \cmd{netstat} per controllarne lo stato sul server otteremo che:
+usiamo \cmd{netstat} per controllarne lo stato otterremo che sul server si ha:
 \begin{verbatim}
 anarres:/home/piccardi# netstat -ant
 Active Internet connections (servers and established)
@@ -2359,8 +2402,9 @@ Proto Recv-Q Send-Q Local Address           Foreign Address         State
 ...      ...    ... ...                     ...                     ...
 tcp        0      0 192.168.1.141:7         192.168.1.2:34626       FIN_WAIT2
 \end{verbatim}
-cioè, come aspettato, essa è nello stato \texttt{FIN_WAIT2}, mentre sul client
-otterremo che essa è andata nello stato \texttt{CLOSE\_WAIT}:
+cioè essa è andata nello stato \texttt{FIN\_WAIT2}, che indica l'avvenuta
+emissione del segmento FIN, mentre sul client otterremo che essa è andata
+nello stato \texttt{CLOSE\_WAIT}:
 \begin{verbatim}
 [root@gont gapil]# netstat -ant
 Active Internet connections (servers and established)
@@ -2369,8 +2413,6 @@ Proto Recv-Q Send-Q Local Address           Foreign Address         State
 tcp        1      0 192.168.1.2:34582       192.168.1.141:7         CLOSE_WAIT 
 \end{verbatim}
 
-
-
 Il problema è che in questo momento il client è bloccato dentro la funzione
 \texttt{ClientEcho} nella chiamata a \func{fgets}, e sta attendendo dell'input
 dal terminale, per cui non è in grado di accorgersi di nulla. Solo quando
@@ -2380,19 +2422,31 @@ pacchetti riportati da \cmd{tcpdump}: il primo, inviato dal client contenente
 i 25 caratteri della riga appena letta, e ad esso la macchina server
 risponderà, non essendoci più niente in ascolto sulla porta 7, con un segmento
 di RST, contraddistinto dalla lettera \texttt{R}, che causa la conclusione
-definitiva della connessione anche nel client, dove non comparrà più
+definitiva della connessione anche nel client, dove non comparirà più
 nell'output di \cmd{netstat}.
 
-Non avendo controllato lo stato di uscita della nostra funzione di scrittura
-(si riveda il codice illustrato in \secref{fig:TCP_client_echo_sub}) che a
-questo punto riporterebbe un errore, il client proseguirà leggendo dal socket,
-e dato che questo è stato chiuso avremo che, come spiegato in
-\secref{sec:TCP_conn_term}, la funzione \func{read} ritorna normalmente con un
-valore nullo. Questo comporta che la seguente chiamata a \func{fputs} non ha
-effetto (viene stampata una stringa nulla) ed il client si blocca di nuovo
-nella successiva chiamata a \func{fgets}. Per questo diventa possibile
-inserire una terza riga e solo dopo averlo fatto si avrà la terminazione del
-programma.
+Come abbiamo accennato in \secref{sec:TCP_conn_term} e come vedremo più avanti
+in \secref{sec:TCP_shutdown} la chiusura di un solo capo di un socket è
+una operazione lecita, per cui la nostra scrittura avrà comunque successo
+(come si può constatare lanciando usando \cmd{strace}\footnote{il comando
+  \cmd{strace} è un comando di debug molto utile che prende come parametro un
+  altro comando e ne stampa a video tutte le invocazioni di una system call,
+  coi relativi parametri e valori di ritorno, per cui usandolo in questo
+  contesto potremo verificare che effettivamente la \func{write} ha scritto la
+  riga, che in effetti è stata pure trasmessa via rete.}), in quanto il nostro
+programma non ha a questo punto alcun modo di sapere che dall'altra parte non
+c'è più nessuno processo in grado di leggere quanto scriverà. Questo sarà
+chiaro solo dopo il tentativo di scrittura, e la ricezione del segmento RST di
+risposta che indica che dall'altra parte non si è semplicemente chiuso un capo
+del socket, ma è completamente terminato il programma.
+
+Per questo motivo il nostro client proseguirà leggendo dal socket, e dato che
+questo è stato chiuso avremo che, come spiegato in \secref{sec:TCP_conn_term},
+la funzione \func{read} ritorna normalmente con un valore nullo. Questo
+comporta che la seguente chiamata a \func{fputs} non ha effetto (viene
+stampata una stringa nulla) ed il client si blocca di nuovo nella successiva
+chiamata a \func{fgets}. Per questo diventa possibile inserire una terza riga
+e solo dopo averlo fatto si avrà la terminazione del programma.
 
 Per capire come questa avvenga comunque, non avendo inserito nel codice nessun
 controllo di errore, occorre ricordare che, a parte la bidirezionalità del
@@ -2405,6 +2459,248 @@ esattamente quello che avviene in questo caso, e siccome non abbiamo un
 gestore per questo segnale, viene eseguita l'azione preimpostata, che è quella
 di terminare il processo.
 
+Per gestire in maniera più corretta questo tipo di evento dovremo allora
+modificare il nostro client perché sia in grado di trattare le varie tipologie
+di errore, per questo dovremo riscrivere la funzione \func{ClientEcho}, in
+modo da controllare gli stati di uscita delle varie chiamate. Si è riportata
+la nuova versione della funzione in \figref{fig:TCP_ClientEcho_second}.
+
+\begin{figure}[!htb]
+  \footnotesize \centering
+  \begin{minipage}[c]{15.6cm}
+    \includecodesample{listati/ClientEcho_second.c}
+  \end{minipage} 
+  \normalsize
+  \caption{La sezione nel codice della seconda versione della funzione
+    \func{ClientEcho} usata dal client per il servizio \textit{echo}
+    modificata per tener conto degli eventuali errori.}
+  \label{fig:TCP_ClientEcho_second}
+\end{figure}
+
+Come si può vedere in questo caso si controlla il valore di ritorno di tutte
+le funzioni, ed inoltre si verifica la presenza di un eventuale end of file in
+caso di lettura. Con questa modifica il nostro client echo diventa in grado di
+accorgersi della chiusura del socket da parte del server, per cui ripetendo la
+sequenza di operazioni precedenti stavolta otterremo che:
+\begin{verbatim}
+[piccardi@gont sources]$ ./echo 192.168.1.141
+Prima riga
+Prima riga
+Seconda riga dopo il C-c
+EOF sul socket
+\end{verbatim}%$
+ma di nuovo si tenga presente che non c'è modo di accorgersi della chiusura
+del socket fin quando non si esegue la scrittura della seconda riga; il
+protocollo infatti prevede che ci debba essere una scrittura prima di ricevere
+un RST che confermi la chiusura del file, e solo alle successive scritture si
+potrà ottenere un errore.
+
+Questa caratteristica dei socket ci mette di fronte ad un altro problema
+relativo al nostro client, e che cioè esso non è in grado di accorgersi di
+nulla fintanto che è bloccato nella lettura del terminale fatta con
+\func{gets}. In questo caso il problema è minimo, ma esso riemergerà più
+avanti, ed è quello che si deve affrontare tutte le volte quando si ha a che
+fare con la necessità di lavorare con più descrittori, nel qual caso diventa
+si pone la questione di come fare a non restare bloccati su un socket quando
+altri potrebbero essere liberi. Vedremo come affrontare questa problematica in
+\secref{sec:TCP_sock_multiplexing}.
+
+\subsection{Altri scenari di terminazione della connessione}
+\label{sec:TCP_conn_crash}
+
+La terminazione del server è solo uno dei possibili scenari di terminazione
+della connessione, un altro caso è ad esempio quello in cui si ha un crollo
+della rete, cosa che potremo simulare facilmente staccando il cavo di rete.
+Un'altra condizione è quella di un blocco della macchina completo della su cui
+gira il server che deve essere riavviata, cosa che potremo simulare sia
+premendo il bottone di reset,\footnote{un normale shutdown non va bene; in tal
+  caso infatti il sistema provvede a terminare tutti i processi, per cui la
+  situazione sarebbe sostanzialmente identica alla precedente.} che, in
+maniera più gentile, riavviando la macchina dopo aver interrotto la
+connessione di rete.
+
+Cominciamo ad analizzare il primo caso, il crollo della rete. Ripetiamo la
+nostra sessione di lavoro precedente, lanciamo il client, scriviamo una prima
+riga, poi stacchiamo il cavo e scriviamo una seconda riga. Il risultato che
+otterremo è:
+\begin{verbatim}
+[piccardi@gont sources]$ ./echo 192.168.1.141
+Prima riga
+Prima riga
+Seconda riga dopo l'interruzione
+Errore in lettura: No route to host
+\end{verbatim}%$
+
+Quello che succede in questo è che il programma, dopo aver scritto la seconda
+riga, resta bloccato per un tempo molto lungo, prima di dare l'errore
+\errcode{EHOSTUNREACH}.  Se andiamo ad osservare con \cmd{strace} cosa accade
+nel periodo in cui il programma è bloccato vedremo che stavolta, a differenza
+del caso precedente, il programma è bloccato nella lettura dal socket.
+
+Se poi, come nel caso precedente, usiamo l'accortezza di analizzare il
+traffico di rete fra client e server con \cmd{tcpdump}, otterremo il seguente
+risultato:
+\begin{verbatim}
+[root@gont sources]# tcpdump src 192.168.1.141 or dst 192.168.1.141 -N -t
+tcpdump: listening on eth0
+gont.34685 > anarres.echo: S 1943495663:1943495663(0) win 5840
+anarres.echo > gont.34685: S 1215783131:1215783131(0) ack 1943495664 win 5792 
+gont.34685 > anarres.echo: . ack 1 win 5840
+gont.34685 > anarres.echo: P 1:12(11) ack 1 win 5840 
+anarres.echo > gont.34685: . ack 12 win 5792 
+anarres.echo > gont.34685: P 1:12(11) ack 12 win 5792 
+gont.34685 > anarres.echo: . ack 12 win 5840 
+gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 
+gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 
+gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 
+gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 
+gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 
+gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 
+gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 
+gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 
+gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 
+arp who-has anarres tell gont
+arp who-has anarres tell gont
+arp who-has anarres tell gont
+arp who-has anarres tell gont
+arp who-has anarres tell gont
+arp who-has anarres tell gont
+...
+\end{verbatim}
+
+In questo caso l'andamento dei primi sette pacchetti è esattamente lo stesso
+di prima. Solo che stavolta, non appena inviata la seconda riga, il programma
+si bloccherà nella successiva chiamata a \func{read}, non ottendo nessuna
+risposta. Quello che succede è che nel frattempo il kernel provvede, come
+richiesto dal protocollo TCP, a tentare la ritrasmissione della nostra riga un
+certo numero di volte, con tempi di attesa crescente fra un tentativo ed il
+successivo, per tentare di ristabilire la connessione.
+
+Il risultato finale qui dipende dall'implementazione dello stack TCP, e nel
+caso di Linux anche dall'impostazione di alcuni dei parametri di sistema che
+si trovano in \file{/proc/sys/net/ipv4}, che ne controllano il comportamento:
+in questo caso in particolare da \file{tcp\_retries2}. Questo parametro
+infatti specifica il numero di volte che deve essere ritentata la
+ritrasmissione di un pacchetto nel mezzo di una connessione prima di riportare
+un errore di timeout.  Il valore preimpostato è pari a 15, il che
+comporterebbe 15 tentativi di ritrasmissione, ma nel nostro caso le cose sono
+andate diversamente, dato che le ritrasmissioni registrate da \cmd{tcpdump}
+sono solo 8; inoltre l'errore riportato all'uscita del client non è stato
+\errcode{ETIMEDOUT}, come dovrebbe essere in questo caso, ma
+\errcode{EHOSTUNREACH}.
+
+Per capire l'accaduto continuiamo ad analizzare l'output di \cmd{tcpdump}:
+esso ci mostra che a un certo punto i tentativi di ritrasmissione del
+pacchetto sono cessati, per essere sostituiti da una serie di richieste di
+protocollo ARP in cui il client richiede l'indirizzo del server.
+
+Come abbiamo accennato in \secref{sec:net_tcpip_general} ARP è il protocollo
+che si incarica di trovare le corrispondenze corrispondenze fra indirizzo IP e
+indirizzo hardware sulla scheda di rete. È evidente allora che nel nostro
+caso, essendo client e server sulla stessa rete, è scaduta la voce nella
+\textit{ARP cache}\footnote{la \textit{ARP chache} è una tabella mantenuta
+  internamente dal kernel che contiene tutte le corrispondenze fra indirizzi
+  IP e indirizzi fisici, ottenute appunto attraverso il protocollo ARP; le
+  voci della tabella hanno un tempo di vita limitato, passato il quale scadono
+  e devono essere nuovamente richieste.} relativa ad \texttt{anarres}, ed il
+nostro client ha iniziato ad effettuare richieste ARP sulla rete per sapere
+l'IP di quest'ultimo, che essendo scollegato non poteva rispondere. Anche per
+questo tipo di richieste esiste un timeout, per cui dopo un certo numero di
+tentativi il meccanismo si è interrotto, e l'errore riportato al programma a
+questo punto è stato \errcode{EHOSTUNREACH}, in quanto non si era più in grado
+di contattare il server.
+
+Un altro errore possibile in questo tipo di situazione, che si può avere
+quando la macchina è su una rete remota, è \errcode{ENETUNREACH}; esso viene
+riportato alla ricezione di un pacchetto ICMP di \textit{destination
+  unreachable} da parte del router che individua l'interruzione della
+connessione. Di nuovo anche qui il risultato finale dipende da quale è il
+meccanismo più veloce ad accorgersi del problema.
+
+Se però agiamo sui parametri del kernel, e scriviamo in \file{tcp\_retries2}
+un valore di tentativi più basso, possiamo evitare la scadenza della
+\textit{ARP cache} e vedere cosa succede. Così se ad esempio richiediamo 4
+tentativi di ritrasmissione, l'analisi di \cmd{tcpdump} ci riporterà il
+seguente scambio di pacchetti:
+\begin{verbatim}
+[root@gont gapil]# tcpdump src 192.168.1.141 or dst 192.168.1.141 -N -t
+tcpdump: listening on eth0
+gont.34752 > anarres.echo: S 3646972152:3646972152(0) win 5840
+anarres.echo > gont.34752: S 2735190336:2735190336(0) ack 3646972153 win 5792 
+gont.34752 > anarres.echo: . ack 1 win 5840 
+gont.34752 > anarres.echo: P 1:12(11) ack 1 win 5840
+anarres.echo > gont.34752: . ack 12 win 5792 
+anarres.echo > gont.34752: P 1:12(11) ack 12 win 5792 
+gont.34752 > anarres.echo: . ack 12 win 5840 
+gont.34752 > anarres.echo: P 12:45(33) ack 12 win 5840 
+gont.34752 > anarres.echo: P 12:45(33) ack 12 win 5840 
+gont.34752 > anarres.echo: P 12:45(33) ack 12 win 5840 
+gont.34752 > anarres.echo: P 12:45(33) ack 12 win 5840 
+gont.34752 > anarres.echo: P 12:45(33) ack 12 win 5840 
+\end{verbatim}
+e come si vede in questo caso i tentativi di ritrasmissione del pacchetto
+iniziale sono proprio 4 (per un totale di 5 voci con quello trasmesso la prima
+volta), ed in effetti, dopo un tempo molto più breve rispetto a prima ed in
+corrispondenza dell'invio dell'ultimo tentativo, quello che otterremo come
+errore all'uscita del client sarà diverso, e cioè:
+\begin{verbatim}
+[piccardi@gont sources]$ ./echo 192.168.1.141
+Prima riga
+Prima riga
+Seconda riga dopo l'interruzione
+Errore in lettura: Connection timed out
+\end{verbatim}%$
+che corrisponde appunto, come ci aspettavamo, alla ricezione di un
+\errcode{ETIMEDOUT}.
+
+Analizziamo ora il secondo scenario, in cui si ha un crollo della macchina che
+fa da server. Al solito lanciamo il nostro client, scriviamo una prima riga
+per verificare che sia tutto a posto, poi stacchiamo il cavo e riavviamo il
+server. A questo punto, ritornato attivo il server, scriviamo una seconda
+riga. Quello che otterremo in questo caso è:
+\begin{verbatim}
+[piccardi@gont sources]$ ./echo 192.168.1.141
+Prima riga
+Prima riga
+Seconda riga dopo l'interruzione
+Errore in lettura Connection reset by peer
+\end{verbatim}%$
+e l'errore ricevuti da \func{read} stavolta è \errcode{ECONNRESET}. Se al
+solito riportiamo l'analisi dei pacchetti effettuata con \cmd{tcpdump},
+avremo:
+\begin{verbatim}
+[root@gont gapil]# tcpdump src 192.168.1.141 or dst 192.168.1.141 -N -t
+tcpdump: listening on eth0
+gont.34756 > anarres.echo: S 904864257:904864257(0) win 5840 
+anarres.echo > gont.34756: S 4254564871:4254564871(0) ack 904864258 win 5792
+gont.34756 > anarres.echo: . ack 1 win 5840 
+gont.34756 > anarres.echo: P 1:12(11) ack 1 win 5840
+anarres.echo > gont.34756: . ack 12 win 5792 
+anarres.echo > gont.34756: P 1:12(11) ack 12 win 5792
+gont.34756 > anarres.echo: . ack 12 win 5840 
+gont.34756 > anarres.echo: P 12:45(33) ack 12 win 5840
+anarres.echo > gont.34756: R 4254564883:4254564883(0) win 0 
+\end{verbatim}
+
+Ancora una volta i primi sette pacchetti sono gli stessi; ma in questo caso
+quello che succede dopo lo scambio iniziale è che, non avendo inviato nulla
+durante il periodo in cui si è riavviato il server, il client è del tutto
+ignaro dell'accaduto per cui quando effettuerà una scrittura, dato che la
+macchina server è stata riavviata e che tutti gli stati relativi alle
+precedenti connessioni sono completamente persi, anche in presenza di una
+nuova istanza del server echo non sarà possibile consegnare i dati in arrivo,
+per cui alla loro ricezione il kernel risponderà con un segmento di RST.
+
+Il client da parte sua, dato che neanche in questo caso non è stato emesso un
+FIN, dopo aver scritto verrà bloccato nella successiva chiamata a \func{read},
+che però adesso ritornerà immediatamente alla ricezione del segmento RST,
+riportando appunto come errore \errcode{ECONNRESET}. Occorre precisare che se
+si vuole che il client sia in grado di accorgersi del crollo del server anche
+quando non sta effettuando uno scambio di dati, è possibile usare una
+impostazione speciale del socket (ci torneremo in
+\secref{sec:TCP_sock_options}) che provvede all'esecuzione di questo
+controllo.