From a7226c68948cc0819518e0733a731d76e4a0e6a5 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sun, 27 Jul 2003 14:28:19 +0000 Subject: [PATCH] Modifiche varie per i test delle situazioni limite --- elemtcp.tex | 61 +++++++++++++++++++++++++++++++-------------- intro.tex | 6 ++--- sources/ErrCode.c | 4 +-- sources/TCP_echo.c | 19 +++++++++++--- sources/TCP_echod.c | 21 +++++++++++++++- 5 files changed, 82 insertions(+), 29 deletions(-) diff --git a/elemtcp.tex b/elemtcp.tex index 70caf59..1ae67f4 100644 --- a/elemtcp.tex +++ b/elemtcp.tex @@ -1053,12 +1053,12 @@ errori rispetto ad altre implementazioni dei socket BSD, infatti la funzione \func{accept} passa gli errori di rete pendenti sul nuovo socket come codici di errore per \func{accept}, per cui l'applicazione deve tenerne conto ed eventualmente ripetere la chiamata alla funzione come per l'errore di -\errcode{EAGAIN}. Un'altra differenza con BSD è che la funzione non fa -ereditare al nuovo socket i flag del socket originale, come -\const{O\_NONBLOCK},\footnote{ed in generale tutti quelli che si possono - impostare con \func{fcntl}, vedi \secref{sec:file_fcntl}.} che devono essere -rispecificati ogni volta. Tutto questo deve essere tenuto in conto se si -devono scrivere programmi portabili. +\errcode{EAGAIN} (torneremo su questo in \secref{sec:TCP_echo_critical}). +Un'altra differenza con BSD è che la funzione non fa ereditare al nuovo socket +i flag del socket originale, come \const{O\_NONBLOCK},\footnote{ed in generale + tutti quelli che si possono impostare con \func{fcntl}, vedi + \secref{sec:file_fcntl}.} che devono essere rispecificati ogni volta. Tutto +questo deve essere tenuto in conto se si devono scrivere programmi portabili. Il meccanismo di funzionamento di \func{accept} è essenziale per capire il funzionamento di un server: in generale infatti c'è sempre un solo socket in @@ -2078,8 +2078,9 @@ modificata per la seconda versione del programma. \includecodesample{listati/TCP_echod.c} \end{minipage} \normalsize - \caption{Sezione del codice seconda versione del server - per il servizio \textit{echo} modificate rispetto alla prima.} + \caption{La sezione nel codice della seconda versione del server + per il servizio \textit{echo} modificata per tener conto dell'interruzione + delle system call.} \label{fig:TCP_echo_server_code} \end{figure} @@ -2122,19 +2123,24 @@ la rete gestire tutta una serie di situazioni critiche che non esistono per i processi locali. + +\subsection{La terminazione precoce della connessione} +\label{sec:TCP_conn_early_abort} + La prima situazione critica è quella della terminazione precoce, causata da un qualche errore sulla rete, della connessione effettuata da un client. 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}. Di norma questo non è un problema, in quanto non appena completata la connessione, \func{accept} ritorna e l'errore sarà -rilevato in seguito dal processo che gestisce la connessione alla prima -chiamata di una funzione che opera sul socket.. +rilevato in seguito, dal processo che gestisce la connessione, alla prima +chiamata di una funzione che opera sul socket. È però possibile, dal punto di vista teorico, incorrere anche in uno scenario del tipo di quello mostrato in \figref{fig:TCP_early_abort}, in cui la -connessione viene abortita sul lato client con l'invio di un segmento RST, -prima che nel server sia stata chiamata la funzione \func{accept}. +connessione viene abortita sul lato client per un qualche errore di rete con +l'invio di un segmento RST, prima che nel server sia stata chiamata la +funzione \func{accept}. \begin{figure}[htb] \centering @@ -2148,8 +2154,8 @@ con dei server molto occupati. In tal caso, con una struttura del server simile a quella del nostro esempio, in cui la gestione delle singole connessioni è demandata a processi figli, può accadere che il three way handshake venga completato e la relativa connessione abortita subito dopo, -prima che il padre, per via del carico della macchina, abbia fatto in tempo a -rieseguire la chiamata ad \func{accept}. Di nuovo si ha una situazione analoga +prima che il padre, per via del carico della macchina, abbia fatto in tempo ad +eseguire la chiamata ad \func{accept}. Di nuovo si ha una situazione analoga a quella illustrata in \figref{fig:TCP_early_abort}, in cui la connessione viene stabilita, ma subito dopo si ha una condizione di errore che la chiude prima che essa sia stata accettata dal programma. @@ -2160,17 +2166,34 @@ abbiamo trattato in \secref{sec:TCP_child_hand} nel caso particolare di \func{accept}, che come nel caso precedente, necessitano semplicemente la ripetizione della chiamata senza che si debba uscire dal programma. In questo caso anche la versione modificata del nostro server non sarebbe adatta, in -quanto uno di questi errori causerebbe la terminazione dello stesso. +quanto uno di questi errori causerebbe la terminazione dello stesso. In Linux +i possibili errori di rete non fatali, riportati sul socket connesso al +ritorno di \func{accept}, sono \errcode{ENETDOWN}, \errcode{EPROTO}, +\errcode{ENOPROTOOPT}, \errcode{EHOSTDOWN}, \errcode{ENONET}, +\errcode{EHOSTUNREACH}, \errcode{EOPNOTSUPP} e \errcode{ENETUNREACH}. Si tenga presente che questo tipo di terminazione non è riproducibile terminando il client prima della chiamata ad \func{accept}; in tal caso infatti il socket associato alla connessione viene semplicemente chiuso, attraverso la sequenza vista in \secref{sec:TCP_conn_term}, per cui la \func{accept} ritornerà senza errori, e si avrà semplicemente un end-of-file -al primo accesso al socket. Per riprodurre l'errore occorrerebbe usare -l'opzione \const{SO\_LINGER} (vedi \secref{sec:xxx}) per chiedere al client di -chiudere il socket con un segmento RST, invece che con la normale sequenza di -chiusura. +al primo accesso al socket. Nel caso di Linux inoltre anche l'invio da parte +del client di un segmento di RST non provoca nessun errore al ritorno di +\funcd{accept} quanto un errore di \errcode{ECONNRESET} al primo tentativo di +accesso al socket. + + + +\subsection{Il crollo del server} +\label{sec:TCP_server_crash} + +Un secondo caso critico è quello in cui si ha il crollo del server. In tal +caso si suppone che il processo del server termini per un errore fatale, cosa +che potremo simulare inviandogli un segnale di terminazione. La conclusione +del processo comporta la chiusura di tutti i file aperti, compresi tutti i +socket relativi a connessioni stabilite; questo significa che al momento del +crollo del servizio il client riceverà un FIN dal server in corrispondenza +della chiusura del socket. diff --git a/intro.tex b/intro.tex index 1ca91b4..89eb59c 100644 --- a/intro.tex +++ b/intro.tex @@ -701,10 +701,10 @@ esplicitamente definendo la macro \macro{\_GNU\_SOURCE} prima di includere i vari header file. -\subsection{Gli standard di GNU/Linux} -\label{sec:intro_linux_std} +%% \subsection{Gli standard di GNU/Linux} +%% \label{sec:intro_linux_std} -Da fare (o cassare, a seconda del tempo e della voglia). +%% Da fare (o cassare, a seconda del tempo e della voglia). diff --git a/sources/ErrCode.c b/sources/ErrCode.c index 7fc5ab8..d8b6906 100644 --- a/sources/ErrCode.c +++ b/sources/ErrCode.c @@ -26,7 +26,7 @@ * * Usage: errcode -h give all info's * - * $Id: ErrCode.c,v 1.7 2003/05/02 09:55:13 piccardi Exp $ + * $Id: ErrCode.c,v 1.8 2003/07/27 14:28:19 piccardi Exp $ * ****************************************************************/ /* @@ -247,7 +247,7 @@ int main(int argc, char *argv[]) void usage(void) { printf("Print error code message or macro label \n"); printf("Usage:\n"); - printf(" daytime [-h] [-l] [-m] error code \n"); + printf(" errcode [-h] [-l] [-m] error code \n"); printf(" -m print error messages\n"); printf(" -l print error code label\n"); printf(" -h print this help\n"); diff --git a/sources/TCP_echo.c b/sources/TCP_echo.c index 2a6f921..48e0326 100644 --- a/sources/TCP_echo.c +++ b/sources/TCP_echo.c @@ -26,7 +26,7 @@ * * Usage: echo -h give all info's * - * $Id: TCP_echo.c,v 1.2 2003/06/23 18:48:04 piccardi Exp $ + * $Id: TCP_echo.c,v 1.3 2003/07/27 14:28:19 piccardi Exp $ * ****************************************************************/ /* @@ -37,6 +37,8 @@ #include /* IP addresses conversion utiliites */ #include /* socket library */ #include /* include standard I/O library */ +#include /* include error codes */ +#include /* include erroro strings definitions */ #define MAXLINE 256 void usage(void); @@ -113,6 +115,7 @@ int main(int argc, char *argv[]) perror("Cannot set linger"); exit(1); } + return 0; } /* read daytime from server */ ClientEcho(stdin, sock); @@ -135,12 +138,20 @@ void usage(void) { void ClientEcho(FILE * filein, int socket) { char sendbuff[MAXLINE+1], recvbuff[MAXLINE+1]; - int nread; + int nread, nwrite; while (fgets(sendbuff, MAXLINE, filein) != NULL) { - FullWrite(socket, sendbuff, strlen(sendbuff)); + nwrite = FullWrite(socket, sendbuff, strlen(sendbuff)); + if (nwrite < 0) { + printf("Errore in scrittura %s", strerror(errno)); + } nread = FullRead(socket, recvbuff, strlen(sendbuff)); + if (nread < 0) { + printf("Errore in lettura %s", strerror(errno)); + } recvbuff[nread] = 0; - fputs(recvbuff, stdout); + if (fputs(recvbuff, stdout) == EOF) { + perror("Errore in scrittura su terminale"); + } } return; } diff --git a/sources/TCP_echod.c b/sources/TCP_echod.c index 576c47f..0491557 100644 --- a/sources/TCP_echod.c +++ b/sources/TCP_echod.c @@ -26,7 +26,7 @@ * * Usage: echod -h give all info * - * $Id: TCP_echod.c,v 1.7 2003/06/19 14:18:27 piccardi Exp $ + * $Id: TCP_echod.c,v 1.8 2003/07/27 14:28:19 piccardi Exp $ * ****************************************************************/ /* @@ -41,6 +41,7 @@ #include /* syslog system functions */ #include /* signal functions */ #include /* error code */ +#include /* error code */ #include "Gapil.h" #define BACKLOG 10 @@ -198,7 +199,25 @@ void ServEcho(int sockfd) { int size; /* main loop, reading 0 char means client close connection */ while ( (nread = read(sockfd, buffer, MAXLINE)) != 0) { + if (nread < 0) { + snprintf(debug, MAXLINE+20, "Errore in lettura: %s \n", + strerror(errno)); + if (demonize) { /* daemon mode */ + syslog(LOG_DEBUG, debug); + } else { + printf("%s", debug); + } + } nwrite = FullWrite(sockfd, buffer, nread); + if (nwrite < 0) { + snprintf(debug, MAXLINE+20, "Errore in scrittura: %s \n", + strerror(errno)); + if (demonize) { /* daemon mode */ + syslog(LOG_DEBUG, debug); + } else { + printf("%s", debug); + } + } if (debugging) { buffer[nread] = 0; snprintf(debug, MAXLINE+20, "Letti %d byte, %s", nread, buffer); -- 2.30.2