X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=elemtcp.tex;h=1ae67f4a24b6bc29b1806b2b3e737b08ac5c01dc;hp=585f6053803cc296a121b2e9fee9408a76ff069a;hb=a0baefcdc1505ecb62f6336acef1153bb56eaf73;hpb=dc185040ca2e11aa51540bca530914f6bc11619d diff --git a/elemtcp.tex b/elemtcp.tex index 585f605..1ae67f4 100644 --- a/elemtcp.tex +++ b/elemtcp.tex @@ -1,6 +1,6 @@ %% elemtcp.tex %% -%% Copyright (C) 2000-2002 Simone Piccardi. Permission is granted to +%% Copyright (C) 2000-2003 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", @@ -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 @@ -1328,7 +1328,7 @@ Il primo passo (\texttt{\small 14--18}) \func{socket} ritorna il descrittore che viene usato per identificare il socket in tutte le chiamate successive. Nel caso la chiamata fallisca si stampa un errore (\texttt{\small 16}) con la funzione \func{perror} e si esce -(\texttt{\small 16}) con un codice di errore. +(\texttt{\small 17}) con un codice di errore. Il passo seguente (\texttt{\small 19--27}) è quello di costruire un'apposita struttura \struct{sockaddr\_in} in cui sarà inserito l'indirizzo del server ed @@ -1635,7 +1635,7 @@ output. \label{sec:TCP_echo_client} Il codice della prima versione del client per il servizio \textit{echo}, -disponibile nel file \file{TCP\_echo1.c}, è riportato in +disponibile nel file \file{TCP\_echo\_first.c}, è riportato in \figref{fig:TCP_echo_client_1}. Esso ricalca la struttura del precedente client per il servizio \textit{daytime} (vedi \secref{sec:TCP_daytime_client}), e la prima parte (\texttt{\small 10--27}) è @@ -1644,7 +1644,7 @@ sostanzialmente identica, a parte l'uso di una porta diversa. \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15.6 cm} - \includecodesample{listati/TCP_echo1.c} + \includecodesample{listati/TCP_echo_first.c} \end{minipage} \normalsize \caption{Codice della prima versione del client \textit{echo}.} @@ -2069,16 +2069,18 @@ Inoltre in certi casi,\footnote{Stevens in \cite{UNP1} accenna che la maggior di Linux questa è disponibile.} anche quando questa è presente, non è detto possa essere usata con \func{accept}. La portabilità però viene al costo di una riscrittura parziale del server, secondo quanto mostrato in -\figref{fig:TCP_echo_server_code}. +\figref{fig:TCP_echo_server_code}, dove si è riportata la sezione di codice +modificata per la seconda versione del programma. -\begin{figure}[!htbp] +\begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15.6cm} \includecodesample{listati/TCP_echod.c} \end{minipage} \normalsize - \caption{Codice del corpo principale della seconda versione del server - per il servizio \textit{echo}.} + \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} @@ -2087,19 +2089,26 @@ quella ad \func{accept}, dato che questa 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. - +questo l'unica modifica sostanziale nella nuova versione del server, rispetto +alla versione precedente vista in \figref{fig:TCP_ServEcho}, è nella sezione +(\texttt{\small 9--14}) in cui si effettua la chiamata di \func{accept}. +Quest'ultima viene effettuata (\texttt{\small 9--10}) 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 11--14}), altrimenti il programma prosegue. + +Si noti che in questa nuova versione si è aggiunta una ulteriore sezione +(\texttt{\small 15--28}) per il debugging, che stampa l'indirizzo da cui si è +avuta la connessione, e la gestione (\texttt{\small 5}) di una nuova opzione, +specificabile con \code{-w Nsec}, che inizializza la variabile \var{waiting} +al numero di secondi da aspettare. Questa opzione permette di specificare un +tempo di attesa prima di passare alla chiamata \func{accept}, il cui scopo +sarà più chiaro facendo riferimento al primo degli scenari critici di +\secref{sec:TCP_echo_critical}. @@ -2114,16 +2123,24 @@ la rete 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}. +\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. + +È 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 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 @@ -2132,6 +2149,55 @@ con l'invio di un segmento RST prima che sia stata chiamata la funzione \label{fig:TCP_early_abort} \end{figure} +Benché questo non sia un fatto comune, un evento simile può essere osservato +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 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. + +Questo significa che, oltre alla interruzione da parte di un segnale, che +abbiamo trattato in \secref{sec:TCP_child_hand} nel caso particolare di +\const{SIGCHLD}, si possono ricevere altri errori non fatali all'uscita 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. 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. 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. + + + + %%% Local Variables: