X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=tcpsockadv.tex;h=44986525fc3141b95422a22548adb4ede34b939a;hp=3ac534820006db9fef38c576874a318f6ae2f162;hb=65cdd0498d1d9473110f86ebddede62c298e60dd;hpb=c48d3a056c974898ca18c27a918f2296c18e2c6e diff --git a/tcpsockadv.tex b/tcpsockadv.tex index 3ac5348..4498652 100644 --- a/tcpsockadv.tex +++ b/tcpsockadv.tex @@ -1,4 +1,4 @@ -%% tcpsockadv.tex + %% tcpsockadv.tex %% %% Copyright (C) 2003 Simone Piccardi. Permission is granted to %% copy, distribute and/or modify this document under the terms of the GNU Free @@ -12,28 +12,228 @@ \label{cha:TCP_advanced} Esamineremo in questo capitolo le funzionalità più evolute della gestione dei -socket TCP. +socket TCP, come l'uso del I/O multiplexing (trattato in +\secref{sec:file_multiplexing}) con i socket, l'uso delle opzioni dei socket e +la gestione dei dati urgenti e \textit{out-of-band}. -\section{Socket multiplexing} -\label{sec:TCP_sock_mutiplexing} +\section{Socket I/O multiplexing} +\label{sec:TCP_sock_multiplexing} -Affronteremo in questa sezione l'utilizzo dei socket +Affronteremo in questa sezione l'utilizzo dell'I/O multiplexing, affrontato in +\secref{sec:file_multiplexing}, nell'ambito delle applicazioni di rete. Già in +\secref{sec:TCP_server_crash} era emerso il problema relativo al client del +servizio echo che non era in grado di accorgersi della terminazione precoce +del server, essendo bloccato nella lettura dei dati immessi da tastiera. +Abbiamo visto in \secref{sec:file_multiplexing} quali sono le funzionalità del +sistema che ci permettono di tenere sotto controllo più file descriptor in +contemporanea; in quella occasione non abbiamo fatto esempi, in quanto quando +si tratta con file normali questa tipologia di I/O normalmente non viene +usata, è invece un caso tipico delle applicazioni di rete quello di dover +gestire varie connessioni da cui possono arrivare dati comuni in maniera +asincrona, per cui riprenderemo l'argomento in questa sezione. + + +\subsection{Il comportamento della funzione \func{select} con i socket.} +\label{sec:TCP_sock_select} + +Iniziamo con la prima delle funzioni usate per l'I/O multiplexing, +\func{select}; il suo funzionamento è già stato descritto in dettaglio in +\secref{sec:file_multiplexing} e non staremo a ripetere quanto detto lì; +sappiamo che la funzione ritorna quando uno o più dei file descriptor messi +sotto controllo è pronto per la relativa operazione. + +In quell'occasione non abbiamo però definito cosa si intende per pronto, +infatti per dei normali file, o anche per delle pipe, la condizione di essere +pronti per la lettura o la scrittura è ovvia; invece lo è molto meno nel caso +dei socket, visto che possono intervenire tutte una serie di possibili +condizioni di errore dovute alla rete. Occorre allora specificare chiaramente +quali sono le condizioni per cui un socket risulta essere ``\textsl{pronto}'' +quando viene passato come membro di uno dei tre \textit{file descriptor set} +usati da \func{select}. + +Le condizioni che fanno si che la funzione \func{select} ritorni segnalando +che un socket (che sarà riportato nel primo insieme di file descriptor) è +pronto per la lettura sono le seguenti: +\begin{itemize*} +\item nel buffer di ricezione del socket sono arrivati dei dati in quantità + sufficiente a superare il valore di una \textsl{soglia di basso livello} (il + cosiddetto \textit{low watermark}). Questo valore è espresso in numero di + byte e può essere impostato con l'opzione del socket \const{SO\_RCVLOWAT} + (tratteremo le opzioni dei socket in \secref{sec:TCP_sock_options}); il suo + valore di default è 1 per i socket TCP e UDP. In questo caso una operazione + di lettura avrà successo e leggerà un numero di byte maggiore di zero. +\item il lato in lettura della connessione è stato chiuso; si è cioè ricevuto + un segmento FIN (si ricordi quanto illustrato in \secref{sec:TCP_conn_term}) + sulla connessione. In questo caso una operazione di lettura avrà successo, + ma non risulteranno presenti dati (in sostanza \func{read} ritornerà con un + valore nullo) per indicare la condizione di end-of-file. +\item c'è stato un errore sul socket. In questo caso una operazione di lettura + non si bloccherà ma restituirà una condizione di errore (ad esempio + \func{read} restituirà -1) e imposterà la variabile \var{errno} al relativo + valore. Vedremo in \secref{sec:TCP_sock_options} come sia possibile estrarre + e cancellare errori pendenti su un socket usando l'opzione + \const{SO\_ERROR}. +\item quando si sta utilizzando un \textit{listening socket} ed ci sono delle + connessioni completate. In questo caso la funzione \func{accept} non si + bloccherà.\footnote{in realtà questo non è sempre vero, come accennato in + \secref{sec:TCP_conn_early_abort} una connessione può essere abortita + dalla ricezione di un segmento RST una volta che è stata completata, + allora se questo avviene dopo che \func{select} è ritornata, ma prima + della chiamata ad \func{accept}, quest'ultima, in assenza di altre + connessioni, potrà bloccarsi.} +\end{itemize*} + +Le condizioni che fanno si che la funzione \func{select} ritorni segnalando +che un socket (che sarà riportato nel secondo insieme di file descriptor) è +pronto per la scrittura sono le seguenti: +\begin{itemize*} +\item nel buffer di invio è disponibile una quantità di spazio superiore al + valore della \textsl{soglia di basso livello} in scrittura ed inoltre o il + socket è già connesso o non necessita (ad esempio è UDP) di connessione. Il + valore della soglia è espresso in numero di byte e può essere impostato con + l'opzione del socket \const{SO\_SNDLOWAT}; il suo valore di default è 2048 + per i socket TCP e UDP. In questo caso una operazione di scrittura non si + bloccherà e restituirà un valore positivo pari al numero di byte accettati + dal livello di trasporto. +\item il lato in scrittura della connessione è stato chiuso. In questo caso + una operazione di scrittura sul socket genererà il segnale \const{SIGPIPE}. +\item c'è stato un errore sul socket. In questo caso una operazione di + scrittura non si bloccherà ma restituirà una condizione di errore ed + imposterà opportunamente la variabile \var{errno}. Vedremo in + \secref{sec:TCP_sock_options} come sia possibile estrarre e cancellare + errori pendenti su un socket usando l'opzione \const{SO\_ERROR}. +\end{itemize*} + +Infine c'è una sola condizione che fa si che \func{select} ritorni segnalando +che un socket (che sarà riportato nel terzo insieme di file descriptor) ha una +condizione di eccezione pendente, e cioè la ricezione sul socket di dati +\textsl{fuori banda} (o \textit{out-of-band}), una caratteristica specifica +dei socket TCP su cui torneremo in \secref{sec:TCP_urgent_data}. + +Si noti come nel caso della lettura \func{select} si applichi anche ad +operazioni che non hanno nulla a che fare con l'I/O di dati come il +riconoscimento della presenza di connessioni pronte, in modo da consentire +anche l'utilizzo di \func{accept} in modalità non bloccante. Si noti infine +come in caso di errore un socket venga sempre riportato come pronto sia per la +lettura che per la scrittura. + +Lo scopo dei due valori di soglia per i buffer di ricezione e di invio è +quello di consentire maggiore flessibilità nell'uso di \func{select} da parte +dei programmi, se infatti si sa che una applicazione non è in grado di fare +niente fintanto che non può ricevere o inviare una certa quantità di dati, si +possono utilizzare questi valori per far si che \func{select} ritorni solo +quando c'è la certezza di avere dati a sufficienza.\footnote{questo tipo di + controllo è utile di norma solo per la lettura, in quanto in genere le + operazioni di scrittura sono già controllate dall'applicazione, che sà + sempre quanti dati invia, mentre non è detto possa conoscere la quantità di + dati in ricezione; per cui, nella situazione in cui si conosce almeno un + valore minimo, per evitare la penalizzazione dovuta alla ripetizione delle + operazioni di lettura per accumulare dati sufficienti, si può lasciare al + kernel il compito di impostare un minimo al di sotto del quale il file + descriptor, pur avendo disponibili dei dati, non viene dato per pronto in + lettura.} + + + +\subsection{Un esempio di I/O multiplexing} +\label{sec:TCP_multiplex_example} + +Abbiamo incontrato la problematica tipica che conduce all'uso dell'I/O +multiplexing nella nostra analisi degli errori in +\secref{sec:TCP_conn_early_abort}, quando il nostro client non era in grado di +rendersi conto di errori sulla connessione essendo impegnato nella attesa di +dati in ingresso dallo standard input. + +In questo caso il problema è quello di dover tenere sotto controllo due +diversi file descriptor, lo standard input, da cui viene letto il testo che +vogliamo inviare al server, e il socket connesso con il server su cui detto +testo sarà scritto e dal quale poi si vorrà ricevere la risposta. L'uso +dell'I/O multiplexing consente di tenere sotto controllo entrambi, senza +restare bloccati. + +Nel nostro caso quello che ci interessa è non essere bloccati in lettura sullo +standard input in caso di errori sulla connessione o chiusura della stessa da +parte del server. Entrambi questi casi possono essere rilevati usando +\func{select}, per quanto detto in \secref{sec:TCP_sock_select}, mettendo +sotto osservazione i file descriptor per la condizione di essere pronti in +lettura: sia infatti che si ricevano dati, che la connessione sia chiusa +regolarmente (con la ricezione di un segmento FIN) che si riceva una +condizione di errore (con un segmento RST) il socket connesso sarà pronto in +lettura (nell'ultimo caso anche in scrittura, ma questo non è necessario ai +nostri scopi). + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \includecodesample{listati/ClientEcho_third.c} + \end{minipage} + \normalsize + \caption{La sezione nel codice della terza versione della funzione + \func{ClientEcho} usata dal client per il servizio \textit{echo} + modificata per l'uso di \func{select}.} + \label{fig:TCP_ClientEcho_third} +\end{figure} + +Riprendiamo allora il codice del client, modificandolo per l'uso di +\func{select}. Quello che dobbiamo modificare è la funzione \func{ClientEcho} +di \figref{fig:TCP_ClientEcho_second}, dato che tutto il resto, che riguarda +le modalità in cui viene stabilita la connessione con il server, resta +assolutamente identico. La nostra nuova versione di \func{ClientEcho}, la +terza della serie, è riportata in \figref{fig:TCP_ClientEcho_third}. + +In questo caso la funzione comincia (\texttt{\small 8--9}) con la +cancellazione del file descriptor set \var{fset} e del valore \var{maxfd} da +passare a \func{select} come massimo per il numero dei file descriptor. Per +quest'ultimo si usa la macro \code{max} definita nel nostro file +\file{macro.h} che raccoglie una collezione di macro di preprocessore di varia +utilità. + +La funzione prosegue poi (\texttt{\small 10--41}) con il ciclo principale, che +viene ripetuto indefinitamente. Per ogni ciclo si reinizializza +(\texttt{\small 11--12}) il file descriptor set, impostando i valori per il +file descriptor associato al socket \var{socket} e per lo standard input (il +cui valore si recupera con la funzione \func{fileno}). Questo è necessario in +quanto la successiva (\texttt{\small 13}) chiamata a \func{select} comporta +una modifica dei due bit relativi, che quindi devono essere reimpostati. + +Si noti come la chiamata a \func{select} venga eseguita usando come primo +argomento il valore di \var{maxfd}, precedentemente calcolato, passando poi il +solo file descriptor set per il controllo dell'attività in lettura, gli altri +argomenti sono tutti passati come puntatori nulli non interessando né il +controllo delle altre attività, né l'impostazione di un valore di timeout. + +Al ritorno di \func{select} si provvede a controllare quale dei file +descriptor presneta attività, si comincia (\texttt{\small 14--24}) con il file +descriptor associato allo standard input. In caso di attività (quando cioè +\macro{FD_ISSET} ritorna una valore diverso da zero) si esegue (\texttt{\small + 15}) una \func{fgets} per leggere gli eventuali dati presenti; se non ve ne +sono (e la funzione restituisce pertanto un puntatore nullo) si ritorna +immediatamente (\texttt{\small 16}) dato che questo significa che si è chiuso +lo standard input; altrimenti (\texttt{\small 18--22}) si scrivono i dati sul +socket, uscendo immediatamente in caso di errore di scrittura. + +Controllato lo standard input si passa a controllare (\texttt{\small 25--40}) +il socket connesso, in caso di attività si esegue (\texttt{\small 26}) subito +una \func{read} di cui si controlla il valore di ritorno; se questo è negativo +(\texttt{\small 27--30}) si è avuto un errore e pertanto si esce +immediatamente segnalandolo, se è nullo (\texttt{\small 31--34}) \section{Le opzioni dei socket} \label{sec:TCP_sock_options} Dato che la maggior parte delle opzioni dei socket sono relative ai socket -TCP, ed hanno poi significato analogo quando usate con altri socket, -tratteremo qui l'argomento in generale. +TCP, ed hanno poi significato analogo quando usate con altri socket, abbiamo +preferito trattare l'argomento in generale in questa sezione piuttosto che nel +capitolo dedicato alla trattazione generica dei socket. \section{I dati \textit{out-of-band}} -\label{sec:TCP_outofband} +\label{sec:TCP_urgent_data} Una caratteristica speciale dei socket TCP è quella della presenza dei cosiddetti dati \textit{out-of-band}