 %% 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
%% Documentation License, Version 1.1 or any later version published by the
%% Free Software Foundation; with the Invariant Sections being "Prefazione",
%% with no Front-Cover Texts, and with no Back-Cover Texts.  A copy of the
%% license is included in the section entitled "GNU Free Documentation
%% License".
%%
\chapter{Socket TCP avanzati}
\label{cha:TCP_advanced}

Esamineremo in questo capitolo le funzionalità più evolute della gestione dei
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 I/O multiplexing}
\label{sec:TCP_sock_multiplexing}

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 l'azzeramento
del file descriptor set \var{fset} e l'impostazione del valore \var{maxfd}, da
passare a \func{select} come massimo per il numero di file descriptor. Per
determinare 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
all'inizio di ogni ciclo.

Si noti come la chiamata a \func{select} venga eseguita usando come primo
argomento il valore di \var{maxfd}, precedentemente calcolato, e passando poi
il solo file descriptor set per il controllo dell'attività in lettura, negli
altri argomenti sono passati tutti 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 due file
descriptor presenta attività in lettura, cominciando (\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 e quindi concluso l'utilizzo del client; altrimenti
(\texttt{\small 18--22}) si scrivono i dati appena letti sul socket,
prevedendo una uscita immediata 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à (\texttt{\small 26}) si esegue 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}) significa che
il server ha chiuso la connessione, e di nuovo si esce con stampando prima un
messaggio di avviso, altrimenti (\texttt{\small 35--39}) si effettua la
terminazione della stringa e la si stampa a sullo standard output (uscendo in
caso di errore), per ripetere il ciclo da capo.

Con questo meccanismo il programma invece di essere bloccato in lettura sullo
standard input resta bloccato sulla \func{select}, che ritorna soltanto quando
viene rilevata attività su uno dei due file descriptor posti sotto controllo.
Questo di norma avviene solo quando si è scritto qualcosa sullo standard
input, o quando si riceve dal socket la risposta a quanto si era appena
scritto. Ma adesso il client diventa capace di accorgersi immediatamente della
terminazione del server; in tal caso infatti il server chiuderà il socket
connesso, ed alla ricezione del FIN la funzione \func{select} ritornerà (come
illustrato in \secref{sec:TCP_sock_select}) segnalando una condizione di end
of file, per cui il nostro client potrà uscire immediatamente.

Riprendiamo la situazione affrontata in \secref{sec:TCP_server_crash},
terminando il server durante una connessione, in questo caso quello che
otterremo, una volta scritta una prima riga ed interrotto il server con un
\texttt{C-c}, sarà:
\begin{verbatim}
[piccardi@gont sources]$ ./echo 192.168.1.1
Prima riga
Prima riga
EOF sul socket
\end{verbatim}%$
dove l'ultima riga compare immediatamente dopo aver interrotto il server. Il
nostro client infatti è in grado di accorgersi immediatamente che il socket
connesso è stato chiuso ed uscire immediatamente.

Veniamo allora agli altri scenari di terminazione anomala visti in
\secref{sec:TCP_conn_crash}. Il primo di questi è l'interruzione fisica della
connessione; in questo caso avremo un comportamento analogo al precedente, in
cui si scrive una riga e non si riceve risposta dal server e non succede
niente fino a quando non si riceve un errore di \errcode{EHOSTUNREACH} o
\errcode{ETIMEDOUT} a seconda dei casi.

La differenza è che stavolta potremo scrivere più righe dopo l'interruzione,
in quanto il nostro client dopo aver inviato i dati non si bloccherà più nella
lettura dal socket, ma nella \func{select}; per questo potrà accettare
ulteriore dati che scriverà di nuovo sul socket, fintanto che c'è spazio sul
buffer di uscita (ecceduto il quale si bloccherà in scrittura). Si ricordi
infatti che il client non ha modo di determinare se la connessione è attiva o
meno (dato che in molte situazioni reali l'inattività può essere temporanea).
Tra l'altro se si ricollega la rete prima della scadenza del timeout, potremo
anche verificare come tutto quello che si era scritto viene poi effettivamente
trasmesso non appena la connessione ridiventa attiva, per cui otterremo
qualcosa del tipo:
\begin{verbatim}
[piccardi@gont sources]$ ./echo 192.168.1.1
Prima riga
Prima riga
Seconda riga dopo l'interruzione
Terza riga
Quarta riga
Seconda riga dopo l'interruzione
Terza riga
Quarta riga
\end{verbatim}
in cui, una volta riconnessa la rete, tutto quello che abbiamo scritto durante
il periodo di disconnessione restituito indietro e stampato immediatamente.

Lo stesso comportamento visto in \secref{sec:TCP_server_crash} si riottiene
nel caso di un crollo completo della macchina su cui sta il server. In questo
caso di nuovo il client non è in grado di accorgersi di niente dato che si
suppone che il programma server non venga terminato correttamente, ma si
blocchi tutto senza la possibilità di avere l'emissione di un segmento FIN che
segnala la terminazione della connessione. Di nuovo fintanto che la
connessione non si riattiva )con il riavvio della macchina del server) il
client non è in grado di fare altro che accettare dell'input e tentare di
inviarlo. La differenza in questo caso è che non appena la connessione
ridiventa attiva i dati verranno sì trasmessi, ma essendo state perse tutte le
informazioni relative alle precedenti connessioni ai tentativi di scrittura
del client sarà risposto con un segmento RST che provocherà il ritorno di
\func{select} per la ricezione di un errore di \errcode{ECONNRESET}.




\subsection{La funzione \func{shutdown}}
\label{sec:TCP_shutdown}

Come spiegato in \secref{sec:TCP_conn_term} il procedimento di chiusura di un
socket TCP prevede che da entrambe le parti venga emesso un segmento FIN. È
pertanto del tutto normale dal punto di vista del protocollo che uno dei due
capi chiuda la connessione, quando l'altro capo la lascia
aperta.\footnote{abbiamo incontrato questa situazione nei vari scenari critici
  di \secref{sec:TCP_echo_critical}.}

È pertanto possibile avere una situazione in cui un capo della connessione non
avendo più nulla da scrivere, possa chiudere il socket, segnalando così
l'avvenuta terminazione della trasmissione (l'altro capo riceverà infatti un
end-of-file in lettura) mentre dall'altra parte si potrà proseguire la
trasmissione dei dati scrivendo sul socket che da quel lato è ancora aperto.
Questa è quella situazione in cui si dice che il socket è \textit{half
  closed}.

Il problema che si pone è che se la chiusura del socket è effettuata con la
funzione \func{close}, come spiegato in \secref{sec:TCP_func_close}, si perde
ogni possibilità di poter rileggere quanto l'altro capo può continuare a
scrivere. Per poter permettere allora 



\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, 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_urgent_data}

Una caratteristica speciale dei socket TCP è quella della presenza dei
cosiddetti dati \textit{out-of-band}




%%% Local Variables: 
%%% mode: latex
%%% TeX-master: "gapil"
%%% End: 
