Aggiunta figura sul caso di aborto precoce della connessione. con revisione
authorSimone Piccardi <piccardi@gnulinux.it>
Wed, 18 Jun 2003 21:19:24 +0000 (21:19 +0000)
committerSimone Piccardi <piccardi@gnulinux.it>
Wed, 18 Jun 2003 21:19:24 +0000 (21:19 +0000)
del testo relativo al secondo esempio per il server echo.

README
biblio.bib
elemtcp.tex
img/tcp_client_early_abort.dia [new file with mode: 0644]
listati/TCP_echod.c
sources/TCP_echod.c

diff --git a/README b/README
index 5fa758d..e9cca70 100644 (file)
--- a/README
+++ b/README
@@ -44,13 +44,3 @@ PDF
 pdflatex gapil
 
 (prima si devono generare le figure con make)
-
-Attenzione: a partire da fine Ottobre 2002, per un motivo che non sono
-ancora riuscito a capire, viene generato un errore alla fine del file
-session.tex. Il codice e` corretto, cio` non ostante latex si ferma ed
-occorre farlo proseguire premendo invio.
-
-NB per un presunto problema di licenza il pacchetto listings e` stato
-rimosso da Debian. L'ultima versione dello stesso era stata
-profondamente modificata e non funzionava. Pertanto non e` detto che
-sia possibile "compilare" Gapil su una Sid.
index 0f08eb5..3b6cf3b 100644 (file)
@@ -33,7 +33,7 @@
 @Book{UNP1,
   author =      {W. Richard Stevens},
   editor =      {},
-  title =       {UNIX Network Programming},
+  title =       {UNIX Network Programming, volume 1},
   publisher =   {Prentice Hall PTR},
   year =        {1998},
   OPTkey =      {},
@@ -48,8 +48,8 @@
 }
 @Book{UNP2,
   author =      {W. Richard Stevens},
-  editor =      {UNIX Network Programming},
-  title =       {},
+  editor =      {},
+  title =       {UNIX Network Programming, volume 2},
   publisher =   {Prentice Hall PTR},
   year =        {1998},
   OPTkey =      {},
index 1962edd..585f605 100644 (file)
@@ -1997,39 +1997,45 @@ ricevere opportunamente lo stato di terminazione del processo (si veda
 quanto illustrato in \secref{sec:sig_sigchld}. Una 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} della nostra libreria personale, che abbiamo illustrato in
-\figref{fig:sig_Signal_code}, per installare il gestore che riceve i segnali
-dei processi figli terminati già visto in \figref{fig:sig_sigchld_handl}.
-Basterà allora aggiungere il seguente codice:
-\includecodesnip{listati/sigchildhand.c}
+\code{Signal} (che abbiamo illustrato in \figref{fig:sig_Signal_code}), per
+installare il gestore che riceve i segnali dei processi figli terminati già
+visto in \figref{fig:sig_sigchld_handl}.  Basterà allora aggiungere il
+seguente codice: \includecodesnip{listati/sigchildhand.c}
 \noindent
 all'esempio illustrato in \figref{fig:TCP_echo_server_first_code}.
 
-In questo modo però si introduce un altro problema, il fatto che, come
-spiegato in \secref{sec:sig_gen_beha}, quando un programma si trova in stato
-di \texttt{sleep} durante l'esecuzione di una system call, questa viene
-interrotta alla ricezione di un segnale, per cui alla fine dell'esecuzione del
-gestore il programma, se questo ritorna, l'esecuzione del programma riprenderà
-con l'uscita dalla system call con un errore di \errcode{EINTR}. 
-
-Questo comporta che quando si chiude il client, con la terminazione del
-processo figlio, il padre, che riceve il segnale ed esegue il gestore,
-ritornerà dalla \func{accept} (a meno di un caso fortuito in cui il segnale
-arriva durante l'esecuzione del programma in risposta ad una connessione) con
-un errore di \errcode{EINTR}, terminando a sua volta con un errore del tipo:
+In questo modo però si introduce un altro problema, si ricordi infatti che,
+come spiegato in \secref{sec:sig_gen_beha}, quando un programma si trova in
+stato di \texttt{sleep} durante l'esecuzione di una system call, questa viene
+interrotta alla ricezione di un segnale. Per questo motivo, alla fine
+dell'esecuzione del gestore del segnale, se questo ritorna, il programma
+riprenderà l'esecuzione ritornando dalla system call con un errore di
+\errcode{EINTR}.
+
+Vediamo allora cosa comporta tutto questo nel nostro caso: quando si chiude il
+client, il processo figlio che gestisce la connessione terminerà, ed il padre,
+per evitare la creazione di zombie, riceverà il segnale \const{SIGCHLD}
+eseguendo il relativo gestore. Al ritorno del gestore però l'esecuzione nel
+padre ripartirà subito con il ritorno della funzione \func{accept} (a meno di
+un caso fortuito in cui il segnale arriva durante l'esecuzione del programma
+in risposta ad una connessione) con un errore di \errcode{EINTR}. Non avendo
+previsto questa eventualità il programma considera questo un errore fatale
+terminando a sua volta con un messaggio del tipo:
 \begin{verbatim}
 [root@gont sources]# ./echod -i
 accept error: Interrupted system call
 \end{verbatim}%#
 
 
-Come accennato in \secref{sec:sig_gen_beha} questo ci mette di fronte a due
-possibili soluzioni, la più semplice è quella di modificare il codice di
-\func{Signal} per richiedere il riavvio automatico delle system call
-interrotte secondo la semantica di BSD, usando l'opzione \const{SA\_RESTART}
-di \func{sigaction}; rispetto a quanto visto in \figref{fig:sig_Signal_code}
-definiremo allora la nuova funzione \func{SignalRestart} come mostrato in
-\figref{fig:sig_SignalRestart_code}.
+Come accennato in \secref{sec:sig_gen_beha} le conseguenze di questo
+comportamento delle system call possono essere superate in due modi diversi,
+il più semplice è quello di modificare il codice di \func{Signal} per
+richiedere il riavvio automatico delle system call interrotte secondo la
+semantica di BSD, usando l'opzione \const{SA\_RESTART} di \func{sigaction};
+rispetto a quanto visto in \figref{fig:sig_Signal_code}. Definiremo allora la
+nuova funzione \func{SignalRestart} come mostrato in
+\figref{fig:sig_SignalRestart_code}, ed installeremo il gestore usando
+quest'ultima.
 
 \begin{figure}[!htb]
   \footnotesize  \centering
@@ -2038,17 +2044,18 @@ definiremo allora la nuova funzione \func{SignalRestart} come mostrato in
   \end{minipage}  
   \normalsize 
   \caption{La funzione \funcd{SignalRestart}, che installa un gestore di
-    segnali in semantica BSD per il riavvio delle system call interrotte.}
+    segnali in semantica BSD per il riavvio automatico delle system call
+    interrotte.}
   \label{fig:sig_SignalRestart_code}
 \end{figure}
 
-Come si può notare questa funzione è identica a \func{Signal}, solo che in
-questo caso invece di inizializzare a zero il campo \var{sa\_flags} di
-\struct{sigaction}, lo si inizializza (\texttt{\small 5}) al valore
-\const{SA\_RESTART}. Usando questa funzione al posto di \func{Signal} nel
-server non è necessaria nessuna altra modifica: le system call interrotte
-saranno automaticamente riavviate, e l'errore \errcode{EINTR} non si
-manifesterà più.
+Come si può notare questa funzione è identica alla precedente \func{Signal},
+solo che in questo caso invece di inizializzare a zero il campo
+\var{sa\_flags} di \struct{sigaction}, lo si inizializza (\texttt{\small 5})
+al valore \const{SA\_RESTART}. Usando questa funzione al posto di
+\func{Signal} nel server non è necessaria nessuna altra modifica: le system
+call interrotte saranno automaticamente riavviate, e l'errore \errcode{EINTR}
+non si manifesterà più.
 
 La seconda soluzione è più invasiva e richiede di controllare tutte le volte
 l'errore restituito dalle varie system call, ripetendo la chiamata qualora
@@ -2075,6 +2082,23 @@ una riscrittura parziale del server, secondo quanto mostrato in
   \label{fig:TCP_echo_server_code}
 \end{figure}
 
+In realtà l'unica chiamata critica che può essere interrotta nel server è
+quella ad \func{accept}, dato che questa è l'unica che può mettere il processo
+padre in stato di sleep.\footnote{si noti infatti che le altre \textit{slow
+    system call} o sono chiamate prima di entrare nel ciclo principale, quando
+  ancora non esistono processi figli, o sono chiamate dai figli stessi.}  Per
+questo l'unica modifica nella nuova versione del server, rispetto alla
+versione precedente vista in \figref{fig:TCP_ServEcho}, è nella sezione
+(\texttt{\small 43--48}) in cui si effettua la chiamata di \func{accept}.
+Quest'ultima allora viene effettuata (\texttt{\small 43--44}) 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 è usato un ciclo
+  vuoto che non esegue nessuna istruzione, in questo modo quello che viene
+  ripetuto con il ciclo è soltanto il codice che esprime la condizione
+  all'interno del \code{while}.}  che la ripete indefinitamente qualora in
+caso di errore il valore di \var{errno} sia \errcode{EINTR}. Negli altri casi
+si esce in caso di errore effettivo (\texttt{\small 45--48}), altrimenti il
+programma prosegue esattamente allo stesso modo del precedente.
 
 
 
@@ -2083,11 +2107,30 @@ una riscrittura parziale del server, secondo quanto mostrato in
 \label{sec:TCP_echo_critical}
 
 Con le modifiche viste in \secref{sec:TCP_child_hand} il nostro esempio
-diventa in grado di affrontare correttamente la gestione ordinaria delle
-connessioni, ma un server di rete deve tenere conto che, al contrario di
-quanto avviene per i server che operano nei confronti di processi presenti
-sulla stessa macchina, la rete è di sua natura inaffidabile, per cui è
-necessario essere in grado di gestire tutta una serie di situazioni critiche.
+diventa in grado di affrontare la gestione ordinaria delle connessioni, ma un
+server di rete deve tenere conto che, al contrario di quanto avviene per i
+server che operano nei confronti di processi presenti sulla stessa macchina,
+la rete è di sua natura inaffidabile, per cui è necessario essere in grado di
+gestire tutta una serie di situazioni critiche che non esistono per i processi
+locali.
+
+Come accennato in \secref{sec:TCP_func_accept} la funzione \func{accept}
+riporta tutti gli eventuali errori di rete pendenti su una connessione sul
+\textit{connected socket}; questo significa che, oltre al caso di interruzione
+da parte di un segnale, esistono altri errori non fatali che necessitano
+semplicemente la ripetizione della chiamata senza che si debba uscire dal
+programma. Un esempio possibile è quello mostrato in
+\figref{fig:TCP_early_abort}, in cui la connessione viene abortita dal client
+con l'invio di un segmento RST prima che sia stata chiamata la funzione
+\func{accept}.
+
+
+\begin{figure}[htb]
+  \centering
+  \includegraphics[width=10cm]{img/tcp_client_early_abort}  
+  \caption{Un possibile caso di terminazione precoce della connessione.}
+  \label{fig:TCP_early_abort}
+\end{figure}
 
 
 
diff --git a/img/tcp_client_early_abort.dia b/img/tcp_client_early_abort.dia
new file mode 100644 (file)
index 0000000..6c0472b
Binary files /dev/null and b/img/tcp_client_early_abort.dia differ
index 81085a7..3f17baa 100644 (file)
@@ -40,7 +40,9 @@ int main(int argc, char *argv[])
        exit(1);
     }
     while (1) {                          /* handle echo to client */
-       if ( (conn_fd = accept(list_fd, NULL, NULL)) < 0) { 
+        while (((conn_fd = accept(list_fd, NULL, NULL)) < 0)
+               && (errno == EINTR));     /* accept connection */
+        if ( conn_fd < 0) {
            PrintErr("accept error");
            exit(1);
        }
index 3cc5ba3..08d926f 100644 (file)
@@ -26,7 +26,7 @@
  *
  * Usage: echod -h give all info
  *
- * $Id: TCP_echod.c,v 1.3 2003/05/12 22:52:29 piccardi Exp $ 
+ * $Id: TCP_echod.c,v 1.4 2003/06/18 21:19:24 piccardi Exp $ 
  *
  ****************************************************************/
 /* 
 #include <time.h>
 #include <syslog.h>      /* syslog system functions */
 #include <signal.h>      /* signal functions */
+#include <errno.h>       /* error code */
 #include "Gapil.h"
 
 #define BACKLOG 10
 #define MAXLINE 256
-int demonize  = 1;  /* daemon use option */
-int debugging = 0;  /* debug info printing option */
+int demonize  = 1;  /* daemon use option: default is daemon */
+int debugging = 0;  /* debug info printing option: default is no debug */
 /* Subroutines declaration */
 void usage(void);
 void ServEcho(int sockfd);
@@ -57,6 +58,7 @@ int main(int argc, char *argv[])
  * Variables definition  
  */
     int list_fd, conn_fd;
+    int waiting;
     pid_t pid;
     struct sockaddr_in serv_add;
     /*
@@ -65,7 +67,7 @@ int main(int argc, char *argv[])
      */
     int i;
     opterr = 0;         /* don't want writing to stderr */
-    while ( (i = getopt(argc, argv, "hdi")) != -1) {
+    while ( (i = getopt(argc, argv, "hdiw:")) != -1) {
        switch (i) {
        /* 
         * Handling options 
@@ -81,6 +83,9 @@ int main(int argc, char *argv[])
        case 'd':
            debugging = 1;
            break;
+       case 'w':
+           waiting = strtol(optarg, NULL, 10);
+           break;
        case '?':   /* unrecognized options */
            printf("Unrecognized options -%c\n",optopt);
            usage();
@@ -133,6 +138,7 @@ int main(int argc, char *argv[])
        PrintErr("listen error");
        exit(1);
     }
+    if (waiting) sleep(waiting);
     /* handle echo to client */
     while (1) {
        /* accept connection */