Ancora message queues
[gapil.git] / simpltcp.tex
index f7260cff817454a95a9cb9d550109a9d2c0c75df..b4b9931413d9a08703352b2b39c54ed6c5b90b5b 100644 (file)
@@ -1,3 +1,13 @@
+%% simpltcp.tex
+%%
+%% Copyright (C) 2000-2002 Simone Piccardi.  Permission is granted to
+%% copy, distribute and/or modify this document under the terms of the GNU Free
+%% Documentation License, Version 1.1 or any later version published by the
+%% Free Software Foundation; with the Invariant Sections being "Prefazione",
+%% with no Front-Cover Texts, and with no Back-Cover Texts.  A copy of the
+%% license is included in the section entitled "GNU Free Documentation
+%% License".
+%%
 \chapter{Un esempio completo di client/server TCP}
 \label{cha:simple_TCP_sock}
 
@@ -8,13 +18,13 @@ comunicazione in entrambe le direzioni.
 Inoltre prenderemo in esame, oltre al comportamento in condizioni normali,
 anche tutti i possibili scenari particolari (errori, sconnessione della rete,
 crash del client o del server durante la connessione) che possono avere luogo
-durante l'impiego di unapplicazione di rete.
+durante l'impiego di un'applicazione di rete.
 
 
 \section{Il servizio \texttt{echo}}
 \label{sec:TCPsimp_echo}
 
-L'applicazione scelta come esempio sarà unimplementazione elementare, ma
+L'applicazione scelta come esempio sarà un'implementazione elementare, ma
 completa, del servizio \texttt{echo}. Il servizio \texttt{echo} è uno dei
 servizi standard solitamente provvisti direttamente dal superserver
 \cmd{inetd}, ed è definito dall'RFC~862. Come dice il nome il servizio deve
@@ -29,16 +39,16 @@ caratteri dallo standard input e la scrive sul server, il server legger
 linea dalla connessione e la riscriverà all'indietro; sarà compito del client
 leggere la risposta del server e stamparla sullo standard output.
 
-Si è scelto di usare questo servizio, seguendo lo Stevens, perché costituisce
-il prototipo ideale di una generica applicazione di rete in cui un server
-risponde alle richieste di un client; tutto quello che cambia nel caso si una
-applicazione più complessa è la elaborazione dell'input del client da parte
-del server nel fornire le risposte in uscita. 
+Si è scelto di usare questo servizio, seguendo l'esempio di \cite{UNP1},
+perché costituisce il prototipo ideale di una generica applicazione di rete in
+cui un server risponde alle richieste di un client; tutto quello che cambia
+nel caso si una applicazione più complessa è la elaborazione dell'input del
+client da parte del server nel fornire le risposte in uscita.
 
-Partiremo da unimplementazione elementare che dovrà essere rimaneggiata di
+Partiremo da un'implementazione elementare che dovrà essere rimaneggiata di
 volta in volta per poter tenere conto di tutte le evenienze che si possono
-manifestare nella vita reale di unapplicazione di rete, fino ad arrivare ad
-unimplementazione completa.
+manifestare nella vita reale di un'applicazione di rete, fino ad arrivare ad
+un'implementazione completa.
 
 \subsection{La struttura del server}
 \label{sec:TCPsimp_server_main}
@@ -47,8 +57,8 @@ La prima versione del server, \file{ElemEchoTCPServer.c}, si compone di un
 corpo principale, costituito dalla funzione \code{main}.  Questa si incarica
 di creare il socket, metterlo in ascolto di connessioni in arrivo e creare un
 processo figlio a cui delegare la gestione di ciascuna connessione.  Questa
-parte, riportata in \nfig, è analoga a quella vista nel precedente esempio
-esaminato in \secref{sec:TCPel_cunc_serv}.
+parte, riportata in \figref{fig:TCPsimpl_serv_code}, è analoga a quella vista
+nel precedente esempio esaminato in \secref{sec:TCPel_cunc_serv}.
 
 \begin{figure}[!htb]
   \footnotesize
@@ -61,7 +71,7 @@ int main(int argc, char *argv[])
     int list_fd, conn_fd;
     pid_t pid;
     struct sockaddr_in serv_add;
-     ....
+     ...
     /* create socket */
     if ( (list_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
         perror("Socket creation error");
@@ -114,22 +124,22 @@ int main(int argc, char *argv[])
 La struttura di questa prima versione del server è sostanzialmente identica a
 quella dell'esempio citato, ed ad esso si applicano le considerazioni fatte in
 \secref{sec:TCPel_cunc_daytime}. Le uniche differenze rispetto all'esempio in
-\figref{fig:TCPel_serv_code} sono che in questo caso per il socket in
-ascolto viene usata la porta 7 e tutta la gestione della comunicazione è
-delegata alla funzione \code{ServEcho}.
+\figref{fig:TCPel_serv_code} sono che in questo caso per il socket in ascolto
+viene usata la porta 7 e che tutta la gestione della comunicazione è delegata
+alla funzione \code{ServEcho}.
 %  Per ogni connessione viene creato un
 % processo figlio, il quale si incarica di lanciare la funzione
 % \texttt{SockEcho}.
 
-Il codice della funzione \code{ServEcho} è invece mostrata in \nfig, la
-comunicazione viene gestita all'interno del ciclo (linee \texttt{\small
-  6--8}).  I dati inviati dal client vengono letti dal socket con una semplice
-\func{read} (che ritorna solo in presenza di dati in arrivo), la riscrittura
-viene invece gestita dalla funzione \func{SockWrite} (descritta a suo tempo
-in \figref{fig:sock_SockWrite_code}) che si incarica di tenere conto
-automaticamente della possibilità che non tutti i dati di cui è richiesta la
-scrittura vengano trasmessi con una singola \func{write}.
-
+Il codice della funzione \code{ServEcho} è invece mostrata in
+\figref{fig:TCPsimpl_server_elem_sub}, la comunicazione viene gestita
+all'interno del ciclo (linee \texttt{\small 6--8}).  I dati inviati dal client
+vengono letti dal socket con una semplice \func{read} (che ritorna solo in
+presenza di dati in arrivo), la riscrittura viene invece gestita dalla
+funzione \func{FullWrite} (descritta in \figref{fig:sock_FullWrite_code}) che
+si incarica di tenere conto automaticamente della possibilità che non tutti i
+dati di cui è richiesta la scrittura vengano trasmessi con una singola
+\func{write}.
 
 \begin{figure}[!htb]
   \footnotesize
@@ -140,7 +150,7 @@ void ServEcho(int sockfd) {
     
     /* main loop, reading 0 char means client close connection */
     while ( (nread = read(sockfd, buffer, MAXLINE)) != 0) {
-        nwrite = SockWrite(sockfd, buffer, nread);
+        nwrite = FullWrite(sockfd, buffer, nread);
     }
     return;
 }
@@ -159,11 +169,11 @@ del processo figlio.
 \subsection{Il client}
 \label{sec:TCPsimp_client_main}
 
-Il codice del client è riportato in \nfig, anche esso ricalca la struttura del
-precedente client per il servizio \texttt{daytime} (vedi
-\secref{sec:net_cli_sample}) ma, come per il server, lo si è diviso in due
-parti, inserendo la parte relativa alle operazioni specifiche previste per il
-protocollo \texttt{echo} in una funzione a parte.
+Il codice del client è riportato in \figref{fig:TCPsimpl_client_elem}, anche
+esso ricalca la struttura del precedente client per il servizio
+\texttt{daytime} (vedi \secref{sec:net_cli_sample}) ma, come per il server, lo
+si è diviso in due parti, inserendo la parte relativa alle operazioni
+specifiche previste per il protocollo \texttt{echo} in una funzione a parte.
 \begin{figure}[!htb]
   \footnotesize
   \begin{lstlisting}{}
@@ -210,11 +220,11 @@ in \secref{sec:net_cli_sample}, il client si connette sulla porta 7
 all'indirizzo specificato dalla linea di comando (a cui si è aggiunta una
 elementare gestione delle opzioni non riportata in figura).
 
-Completata la connessione (quando la funzione \func{connect} ritorna) la
-funzione \code{ClientEcho}, riportata in \nfig, si preoccupa di gestire la
-comunicazione, leggendo una riga alla volta dallo \file{stdin}, scrivendola
-sul socket e ristampando su \file{stdout} quanto ricevuto in risposta dal
-server. 
+Completata la connessione, al ritorno di \func{connect}, la funzione
+\code{ClientEcho}, riportata in \figref{fig:TCPsimpl_client_echo_sub}, si
+preoccupa di gestire la comunicazione, leggendo una riga alla volta dallo
+\file{stdin}, scrivendola sul socket e ristampando su \file{stdout} quanto
+ricevuto in risposta dal server.
 
 \begin{figure}[!htb]
   \footnotesize
@@ -224,8 +234,8 @@ void ClientEcho(FILE * filein, int socket)
     char sendbuff[MAXLINE], recvbuff[MAXLINE];
     int nread; 
     while (fgets(sendbuff, MAXLINE, filein) != NULL) {
-        SockWrite(socket, sendbuff, strlen(sendbuff)); 
-        nread = SockRead(socket, recvbuff, strlen(sendbuff));        
+        FullWrite(socket, sendbuff, strlen(sendbuff)); 
+        nread = FullRead(socket, recvbuff, strlen(sendbuff));        
         recvbuff[nread] = 0;
         fputs(recvbuff, stdout);
     }
@@ -242,12 +252,12 @@ La funzione utilizza due buffer per gestire i dati inviati e letti sul socket
 (linee \texttt{\small 5--10}), i dati da inviare sulla connessione vengono
 presi dallo \file{stdin} usando la funzione \func{fgets} che legge una
 linea di testo (terminata da un \texttt{CR} e fino al massimo di
-\macro{MAXLINE} caratteri) e la salva sul buffer di invio, la funzione
-\func{SockWrite} (\texttt{\small 3}) scrive detti dati sul socket (gestendo
+\const{MAXLINE} caratteri) e la salva sul buffer di invio, la funzione
+\func{FullWrite} (\texttt{\small 3}) scrive detti dati sul socket (gestendo
 l'invio multiplo qualora una singola \func{write} non basti, come spiegato
 in \secref{sec:sock_io_behav}).
 
-I dati che vengono riletti indietro con una \func{SockRead} sul buffer di
+I dati che vengono riletti indietro con una \func{FullRead} sul buffer di
 ricezione e viene inserita la terminazione della stringa (\texttt{\small
   7--8}) e per poter usare la funzione \func{fputs} per scriverli su
 \file{stdout}. 
@@ -262,11 +272,11 @@ il client esce.
 
 Benché il codice dell'esempio precedente sia molto ridotto, esso ci permetterà
 di considerare in dettaglio tutte le problematiche che si possono incontrare
-nello scrivere unapplicazione di rete. Infatti attraverso l'esame delle sue
+nello scrivere un'applicazione di rete. Infatti attraverso l'esame delle sue
 modalità di funzionamento normali, all'avvio e alla terminazione, e di quello
 che avviene nelle varie situazioni limite, da una parte potremo approfondire
 la comprensione del protocollo TCP/IP e dall'altra ricavare le indicazioni
-necessarie per essere in gradi di scrivere applicazioni robuste, in grado di
+necessarie per essere in grado di scrivere applicazioni robuste, in grado di
 gestire anche i casi limite.
 
 
@@ -326,13 +336,15 @@ un risultato del tipo:
  2359 pts/0    S      0:00 ./echod
 \end{verbatim} %$
 (dove si sono cancellate le righe inutili) da cui si evidenzia la presenza di
-tre processi, tutti in stato di \textit{sleep} (S).
-
-Se a questo punto si inizia a scrivere qualcosa sul client niente sarà
-trasmesso fin tanto che non si prema il ritorno carrello, allora la
-\func{fgets} ritornerà e a questo punto il client scriverà quanto immesso
-sul socket, poi rileggerà quanto gli viene inviato all'indietro dal server, e
-questo sarà inviato sullo standard output, che nel caso ne provoca
+tre processi, tutti in stato di \textit{sleep} (vedi
+\tabref{tab:proc_proc_states}).
+
+Se a questo punto si inizia a scrivere qualcosa sul client non sarà trasmesso
+niente fin tanto che non si prema il tasto di a capo (si ricordi quanto detto
+in \secref{sec:file_line_io} a proposito dell'I/O su terminale), solo allora
+\func{fgets} ritornerà ed il client scriverà quanto immesso sul socket, per
+poi passare a rileggere quanto gli viene inviato all'indietro dal server, che
+a sua volta sarà inviato sullo standard output, che nel caso ne provoca
 l'immediatamente stampa a video.
 
 
@@ -360,13 +372,13 @@ tcp        0      0 localhost:33032         localhost:echo          TIME_WAIT
 con il client che entra in \texttt{TIME\_WAIT}.
 
 Esaminiamo allora in dettaglio la sequenza di eventi che porta alla
-terminazione normale della connessione, che ci servirà poi da riferimento nei
-casi seguenti:
+terminazione normale della connessione, che ci servirà poi da riferimento
+quando affronteremo il comportamento in caso di conclusioni anomale:
 
 \begin{enumerate}
 \item inviando un carattere di EOF da terminale la \func{fgets} ritorna
   restituendo un puntatore nullo che causa l'uscita dal ciclo di
-  while, così la \code{ClientEcho} ritorna.
+  \code{while}, così la \code{ClientEcho} ritorna.
 \item al ritorno di \code{ClientEcho} ritorna anche la funzione \code{main}, e
   come parte del processo terminazione tutti i file descriptor vengono chiusi
   (si ricordi quanto detto in \secref{sec:proc_term_conclusion}); questo causa
@@ -377,14 +389,14 @@ casi seguenti:
   \secref{sec:TCPel_conn_term}).
 \item quando il server riceve il FIN la \func{read} del processo figlio che
   gestisce la connessione ritorna restituendo 0 causando così l'uscita dal
-  ciclo di while e il ritorno di \code{ServEcho}, a questo punto il processo
-  figlio termina chiamando \func{exit}.
+  ciclo e il ritorno di \code{ServEcho}, a questo punto il processo figlio
+  termina chiamando \func{exit}.
 \item all'uscita del figlio tutti i file descriptor vengono chiusi, la
   chiusura del socket connesso fa sì che venga effettuata la sequenza finale
   di chiusura della connessione, viene emesso un FIN dal server che riceverà
   un ACK dal client, a questo punto la connessione è conclusa e il client
   resta nello stato \texttt{TIME\_WAIT}.
-\item 
+
 \end{enumerate}
 
 
@@ -394,20 +406,45 @@ casi seguenti:
 Tutto questo riguarda la connessione, c'è però da tenere conto dell'effetto
 del procedimento di chiusura del processo figlio nel server (si veda quanto
 esaminato in \secref{sec:proc_termination}). In questo caso avremo l'invio del
-segnale \macro{SIGCHILD} al padre, ma dato che non si è installato un
-manipolatore e che l'azione di default per questo segnale è quella di essere
+segnale \const{SIGCHLD} al padre, ma dato che non si è installato un
+gestore e che l'azione predefinita per questo segnale è quella di essere
 ignorato, non avendo predisposto la ricezione dello stato di terminazione,
-otterremo che il processo figlio entrerà nello stato di zombie, come risulterà
+otterremo che il processo figlio entrerà nello stato di zombie\index{zombie}
+(si riveda quanto illustrato in \secref{sec:sig_sigchld}), come risulterà
 ripetendo il comando \cmd{ps}:
 \begin{verbatim}
  2356 pts/0    S      0:00 ./echod
  2359 pts/0    Z      0:00 [echod <defunct>]
 \end{verbatim}
 
-Poiché non è possibile lasciare processi zombie che pur inattivi occupano
-spazio nella tabella dei processi e a lungo andare saturerebbero le risorse
-del kernel, occorrerà ricevere opportunamente lo stato di terminazione del
-processo (si veda \secref{sec:proc_wait}), cosa che faremo utilizzando il
-segnale, per questo installeremo un manipolatore usando la funzione
-\func{Signal} (trattata in dettaglio in \secref{sec:sig_signal}).
+Poiché non è possibile lasciare processi zombie\index{zombie} che pur inattivi
+occupano spazio nella tabella dei processi e a lungo andare saturerebbero le
+risorse del kernel, occorrerà ricevere opportunamente lo stato di terminazione
+del processo (si veda \secref{sec:proc_wait}), cosa che faremo utilizzando
+\const{SIGCHLD} secondo quanto illustrato in \secref{sec:sig_sigchld}.
+
+La prima modifica al nostro server è pertanto quella di inserire la gestione
+della terminazione dei processi figli attraverso l'uso di un gestore.
+Per questo useremo la funzione \code{Signal}, illustrata in
+\figref{fig:sig_Signal_code}, per installare il semplice gestore che
+riceve i segnali dei processi figli terminati già visto in 
+\figref{fig:sig_sigchld_handl}; aggiungendo il seguente codice:
+\begin{lstlisting}{}
+    ...
+    /* install SIGCHLD handler */
+    Signal(SIGCHLD, sigchld_hand);  /* establish handler */
+    /* create socket */
+    ...
+\end{lstlisting}
+
+\noindent
+all'esempio illustrato in \figref{fig:TCPsimpl_serv_code}, e linkando il tutto
+alla funzione \code{sigchld\_hand}, si risolverà completamente il problema
+degli zombie\index{zombie}.
+
+
 
+%%% Local Variables: 
+%%% mode: latex
+%%% TeX-master: "gapil"
+%%% End: