\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
\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}
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
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.
\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.
*
* 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 $
*
****************************************************************/
/*
#include <arpa/inet.h> /* IP addresses conversion utiliites */
#include <sys/socket.h> /* socket library */
#include <stdio.h> /* include standard I/O library */
+#include <errno.h> /* include error codes */
+#include <string.h> /* include erroro strings definitions */
#define MAXLINE 256
void usage(void);
perror("Cannot set linger");
exit(1);
}
+ return 0;
}
/* read daytime from server */
ClientEcho(stdin, sock);
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;
}
*
* 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 $
*
****************************************************************/
/*
#include <syslog.h> /* syslog system functions */
#include <signal.h> /* signal functions */
#include <errno.h> /* error code */
+#include <string.h> /* error code */
#include "Gapil.h"
#define BACKLOG 10
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);