Dopo aver trattato in cap.~\ref{cha:TCP_socket} i socket TCP, che costituiscono
l'esempio più comune dell'interfaccia dei socket, esamineremo in questo
capitolo gli altri tipi di socket, a partire dai socket UDP, e i socket
-\textit{Unix domain} già incontrati in \secref{sec:ipc_socketpair}.
+\textit{Unix domain} già incontrati in sez.~\ref{sec:ipc_socketpair}.
\section{I socket UDP}
\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{15.6cm}
- \includecodesample{listati/UDP_echo.c}
+ \includecodesample{listati/UDP_echo_first.c}
\end{minipage}
\normalsize
- \caption{Sezione principale del client per il servizio \textit{echo} su
- UDP.}
+ \caption{Sezione principale della prima versione client per il servizio
+ \textit{echo} su UDP.}
\label{fig:UDP_echo_client}
\end{figure}
\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{15.6cm}
- \includecodesample{listati/UDP_ClientEcho.c}
+ \includecodesample{listati/UDP_ClientEcho_first.c}
\end{minipage}
\normalsize
\caption{Codice della funzione \func{ClientEcho} usata dal client per il
servizio \textit{echo} su UDP.}
- \label{fig:UDP_echo_clientecho}
+ \label{fig:UDP_echo_client_echo}
\end{figure}
Ovviamente in questo caso il funzionamento della funzione, il cui codice è
-riportato in fig.~\ref{fig:UDP_echo_clientecho}, è completamente diverso
+riportato in fig.~\ref{fig:UDP_echo_client_echo}, è completamente diverso
rispetto alla analoga del server TCP, e dato che non esiste una connessione
questa necessita anche di un terzo argomento, che è l'indirizzo del server cui
inviare i pacchetti.
Ci si può chiedere allora perché, benché la situazione di errore sia
rilevabile, questa non venga segnalata. Il luogo più naturale in cui
riportarla sarebbe la chiamata di \func{sendto}, in quanto è a causa dell'uso
-di indirizzo sbagliato che il pacchetto non può essere inviato; farlo in
+di un indirizzo sbagliato che il pacchetto non può essere inviato; farlo in
questo punto però è impossibile, dato che l'interfaccia di programmazione
-richiede che la funzione ritorni non appena il kernel invia il pacchetto, e
-non può bloccarsi in una attesa di una risposta che potrebbe essere molto
-lunga (si noti infatti che il pacchetto ICMP arriva qualche decimo di secondo
-più tardi) o non esserci affatto.
+richiede che la funzione ritorni non appena il kernel invia il
+pacchetto,\footnote{questo è il classico caso di \textsl{errore asincrono},
+ una situazione cioè in cui la condizione di errore viene rilevata in maniera
+ asincrona rispetto all'operazione che l'ha causata, una eventualità
+ piuttosto comune quando si ha a che fare con la rete, tutti i pacchetti ICMP
+ che segnalano errori rientrano in questa tipologia.} e non può bloccarsi in
+una attesa di una risposta che potrebbe essere molto lunga (si noti infatti
+che il pacchetto ICMP arriva qualche decimo di secondo più tardi) o non
+esserci affatto.
Si potrebbe allora pensare di riportare l'errore nella \func{recvfrom} che è
-comunque bloccata in attesa di una risposta che nel caso non non arriverà mai.
-La ragione di tutto questo è piuttosto sottile e viene trattata da Stevens in
-\cite{UNP2} con il seguente esempio: si consideri un client che invia tre
-pacchetti a tre diverse macchine, due quali vengono regolarmente ricevuti,
-mentre al terzo, non essendo presente un server sulla relativa macchina, viene
-risposto con un messaggio ICMP come il precedente. Detto messaggio conterrà
-anche le informazioni relative ad indirizzo e porta del pacchetto che ha
-fallito, però tutto quello che il kernel può restituire al programma è un
-codice di errore in \var{errno}, e pertanto è stata fatta la scelta di non
-riportare l'errore, a meno che, come vedremo in sez.~\ref{sec:UDP_connect}, il
-socket non sia connesso.
+comunque bloccata in attesa di una risposta che nel caso non arriverà mai. La
+ragione per cui non viene fatto è piuttosto sottile e viene spiegata da
+Stevens in \cite{UNP2} con il seguente esempio: si consideri un client che
+invia tre pacchetti a tre diverse macchine, due dei quali vengono regolarmente
+ricevuti, mentre al terzo, non essendo presente un server sulla relativa
+macchina, viene risposto con un messaggio ICMP come il precedente. Detto
+messaggio conterrà anche le informazioni relative ad indirizzo e porta del
+pacchetto che ha fallito, però tutto quello che il kernel può restituire al
+programma è un codice di errore in \var{errno}, con il quale è impossibile di
+distinguere per quale dei pacchetti inviati si è avuto l'errore; per questo è
+stata fatta la scelta di non riportare un errore su un socket UDP, a meno che,
+come vedremo in sez.~\ref{sec:UDP_connect}, questo non sia connesso.
Come illustrato in sez.~\ref{sec:UDP_characteristics} essendo i socket UDP
privi di connessione non è necessario per i client usare \func{connect} prima
-di iniziare una comunicazione con un server.
+di iniziare una comunicazione con un server. Ciò non di meno abbiamo accennato
+come questa possa essere utilizzata per gestire la presenza di errori
+asincroni.
+
+Quando si chiama \func{connect} su di un socket UDP tutto quello che succede è
+che l'indirizzo passato alla funzione viene registrato come indirizzo di
+destinazione del socket. A differenza di quanto avviene con TCP non viene
+scambiato nessun pacchetto, tutto quello che succede è che da quel momento in
+qualunque cosa si scriva sul socket sarà inviata a quell'indirizzo; non sarà
+più necessario usare l'argomento \param{to} di \func{sendto} per specificare
+la destinazione dei pacchetti, che potranno essere inviati e ricevuti usando
+le normali funzioni \func{read} e \func{write}.\footnote{in realtà si può
+ anche continuare ad usare la funzione \func{sendto}, ma in tal caso
+ l'argomento \param{to} deve essere inizializzato a \const{NULL}, e
+ \param{tolen} deve essere inizializzato a zero, pena un errore.}
+
+Una volta che il socket è connesso cambia però anche il comportamento in
+ricezione; prima infatti il kernel avrebbe restituito al socket qualunque
+pacchetto ricevuto con un indirizzo di destinazione corrispondente a quello
+del socket, senza nessun controllo sulla sorgente; una volta che il socket
+viene connesso saranno riportati su di esso solo i pacchetti con un indirizzo
+sorgente corrispondente a quello a cui ci si è connessi.
+
+Infine quando si usa un socket connesso, venendo meno l'ambiguità segnalata
+alla fine di sez.~\ref{sec:UDP_problems}, tutti gli eventuali errori asincroni
+vengono riportati alle funzioni che operano su di esso; pertanto potremo
+riscrivere il nostro client per il servizio \textit{echo} con le modifiche
+illustrate in fig.~\ref{fig:UDP_echo_conn_cli}.
+
+\begin{figure}[!htb]
+ \footnotesize \centering
+ \begin{minipage}[c]{15.6cm}
+ \includecodesample{listati/UDP_echo.c}
+ \end{minipage}
+ \normalsize
+ \caption{Seconda versione del client del servizio \textit{echo} che utilizza
+ socket UDP connessi.}
+ \label{fig:UDP_echo_conn_cli}
+\end{figure}
+
+Ed in questo caso rispetto alla precedente versione, il solo cambiamento è
+l'utilizzo (\texttt{\small 17}) della funzione \func{connect} prima della
+chiamata alla funzione di gestione del protocollo, che a sua volta è stata
+modificata eliminando l'indirizzo passato come parametro e sostituendo le
+chiamata a \func{sendto} e \func{recvfrom} con chiamate a \func{read} e
+\func{write} come illustrato dal nuovo codice riportato in
+fig.~\ref{fig:UDP_echo_conn_echo_client}.
+
+\begin{figure}[!htb]
+ \footnotesize \centering
+ \begin{minipage}[c]{15.6cm}
+ \includecodesample{listati/UDP_ClientEcho.c}
+ \end{minipage}
+ \normalsize
+ \caption{Seconda versione della funzione \func{ClientEcho}.}
+ \label{fig:UDP_echo_conn_echo_client}
+\end{figure}
+
+Utilizzando questa nuova versione del client si può verificare che quando ci
+si rivolge verso un indirizzo inesistente o su cui non è in ascolto un server
+si è in grado rilevare l'errore, se infatti eseguiamo il nuovo programma
+otterremo un qualcosa del tipo:
+\begin{verbatim}
+[piccardi@gont sources]$ ./echo 192.168.1.1
+prova
+Errore in lettura: Connection refused
+\end{verbatim}%$
+
+Ma si noti che a differenza di quanto avveniva con il client TCP qui l'errore
+viene rilevato soltanto dopo che si è tentato di inviare qualcosa, ed in
+corrispondenza al tentativo di lettura della risposta. Questo avviene perché
+con UDP non esiste una connessione, e fintanto che non si invia un pacchetto
+non c'è traffico sulla rete. In questo caso l'errore sarà rilevato alla
+ricezione del pacchetto ICMP \textit{destination unreachable} emesso dalla
+macchina cui ci si è rivolti, e questa volta, essendo il socket UDP connesso,
+il kernel potrà riportare detto errore in user space in maniera non ambigua,
+ed esso apparirà alla successiva lettura sul socket.
+
+Si tenga presente infine che l'uso dei socket connessi non risolve l'altro
+problema del client, e cioè il fatto che in caso di perdita di un pacchetto
+questo resterà bloccato permanentemente in attesa di una risposta. Per
+risolvere questo problema l'unico modo sarebbe quello di impostare un
+\textit{timeout} o riscrivere il client in modo da usare l'I/O non bloccante.
\section{I socket \textit{Unix domain}}
\label{sec:unix_socket}
-Benché i socket Unix domain non siano strettamente attinenti alla rete, in
-quanto definiscono una interfaccia di comunicazione locale alla singola
-macchina, li tratteremo comunque in questa sezione in quanto la loro
-interfaccia è comunque basata sulla più generale interfaccia dei socket.
+Benché i socket Unix domain, come meccanismo di comunicazione fra processi che
+girano sulla stessa macchina, non siano strettamente attinenti alla rete, li
+tratteremo comunque in questa sezione. Nonstante le loro peculiarità infatti,
+l'interfaccia di programmazione che serve ad utilizzarli resta sempre quella
+dei socket.
+
\section{Altri socket}
\label{sec:socket_other}
-
Tratteremo in questa sezione gli altri tipi particolari di socket supportati
da Linux, come i \textit{raw socket}, con i quali si possono \textsl{forgiare}
direttamente i pacchetti a tutti i livelli dello stack dei protocolli, o i