X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=othersock.tex;h=abf0f782533c07e85fb66bf14f74083688cf6052;hp=868ca66c247362706017f394bd3522e67860dce4;hb=193d612d40c5f81f5559ea6e11e70f6b6e51fb39;hpb=7502f301c66ac19b976087d4e0fb71807c051791 diff --git a/othersock.tex b/othersock.tex index 868ca66..abf0f78 100644 --- a/othersock.tex +++ b/othersock.tex @@ -1,45 +1,47 @@ %% othersock.tex %% -%% Copyright (C) 2004 Simone Piccardi. Permission is granted to +%% Copyright (C) 2004-2011 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", +%% Free Software Foundation; with the Invariant Sections being "Un preambolo", %% 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{Gli altri socket più comuni} + +\chapter{Gli altri tipi di socket} \label{cha:other_socket} -Dopo aver trattato in \capref{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, +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 sez.~\ref{sec:ipc_socketpair}. \section{I socket UDP} \label{sec:UDP_socket} -Dopo i socket TCP i socket più utilizzati nella programmazione di rete sono i -socket UDP; protocolli diffusi come NFS o il DNS usano principalmente questo +Dopo i socket TCP i socket più utilizzati nella programmazione di rete sono i +socket UDP: protocolli diffusi come NFS o il DNS usano principalmente questo tipo di socket. Tratteremo in questa sezione le loro caratteristiche -principali e le modalità per il loro utilizzo. +principali e le modalità per il loro utilizzo. \subsection{Le caratteristiche di un socket UDP} \label{sec:UDP_characteristics} -Come illustrato in \secref{sec:net_udp} UDP è un protocollo molto semplice che -non supporta le connessioni e non è affidabile: esso si appoggia direttamente -sopra IP (per i dettagli sul protocollo si veda \secref{sec:udp_protocol}). I -dati vengono inviati in forma di pacchetti, e non ne è assicurata né la -effettiva ricezione né l'arrivo nell'ordine in cui vengono inviati. Il -vantaggio del protocollo è la velocità, non è necessario trasmettere le -informazioni di controllo ed il risultato è una trasmissione di dati più +Come illustrato in sez.\ref{sec:net_udp} UDP è un protocollo molto semplice che +non supporta le connessioni e non è affidabile: esso si appoggia direttamente +sopra IP (per i dettagli sul protocollo si veda sez.~\ref{sec:udp_protocol}). +I dati vengono inviati in forma di pacchetti, e non ne è assicurata né la +effettiva ricezione né l'arrivo nell'ordine in cui vengono inviati. Il +vantaggio del protocollo è la velocità, non è necessario trasmettere le +informazioni di controllo ed il risultato è una trasmissione di dati più veloce ed immediata. Questo significa che a differenza dei socket TCP i socket UDP non supportano una comunicazione di tipo \textit{stream} in cui si ha a disposizione un -flusso continuo di dati che può essere letto un po' alla volta, ma piuttosto +flusso continuo di dati che può essere letto un po' alla volta, ma piuttosto una comunicazione di tipo \textit{datagram}, in cui i dati arrivano in singoli blocchi che devono essere letti integralmente. @@ -47,16 +49,17 @@ Questo diverso comportamento significa anche che i socket UDP, pur appartenendo alla famiglia \const{PF\_INET}\footnote{o \const{PF\_INET6} qualora si usasse invece il protocollo IPv6, che pure supporta UDP.} devono essere aperti quando si usa la funzione \func{socket} (si riveda quanto -illustrato a suo tempo in \tabref{tab:sock_sock_valid_combinations}) +illustrato a suo tempo in tab.~\ref{tab:sock_sock_valid_combinations}) utilizzando per il tipo di socket il valore \const{SOCK\_DGRAM}. -Questa differenza comporta ovviamente che anche le modalità con cui si usano i +Questa differenza comporta ovviamente che anche le modalità con cui si usano i socket UDP sono completamente diverse rispetto ai socket TCP, ed in particolare non esistendo il concetto di connessione non esiste il meccanismo -del \textit{three way handshake} nè quello degli stati del protocollo. In -realtà tutto quello che avviene nella comunicazione attraverso dei socket UDP -è la trasmissione di un pacchetto da un client ad un server o viceversa, -secondo lo schema illustrato in \figref{fig:UDP_packet-exchange}. +del \itindex{three~way~handshake} \textit{three way handshake} né quello degli +stati del protocollo. In realtà tutto quello che avviene nella comunicazione +attraverso dei socket UDP è la trasmissione di un pacchetto da un client ad un +server o viceversa, secondo lo schema illustrato in +fig.~\ref{fig:UDP_packet-exchange}. \begin{figure}[htb] \centering @@ -66,29 +69,29 @@ secondo lo schema illustrato in \figref{fig:UDP_packet-exchange}. \label{fig:UDP_packet-exchange} \end{figure} -Come illustrato in \figref{fig:UDP_packet-exchange} la struttura generica di +Come illustrato in fig.~\ref{fig:UDP_packet-exchange} la struttura generica di un server UDP prevede, una volta creato il socket, la chiamata a \func{bind} -per mettersi in ascolto dei dati. Questa è l'unica parte comune con un server +per mettersi in ascolto dei dati. Questa è l'unica parte comune con un server TCP: non essendovi il concetto di connessione le funzioni \func{listen} ed \func{accept} non sono mai utilizzate nel caso di server UDP. La ricezione dei dati dal client avviene attraverso la funzione \func{recvfrom}, mentre una -eventuale risposta sarà inviata con la funzione \func{sendto}. +eventuale risposta sarà inviata con la funzione \func{sendto}. -Da parte del client invece, una volta creato il socket non sarà necessario +Da parte del client invece, una volta creato il socket non sarà necessario connettersi con \func{connect} (anche se, come vedremo in -\secref{sec:UDP_connect}, è possibile usare questa funzione, con un -significato comunque diverso) ma si potrà effettuare direttamente una -richiesta inviando un pacchetto con la funzione \func{sendto} e si potrà +sez.~\ref{sec:UDP_connect}, è possibile usare questa funzione, con un +significato comunque diverso) ma si potrà effettuare direttamente una +richiesta inviando un pacchetto con la funzione \func{sendto} e si potrà leggere una eventuale risposta con la funzione \func{recvfrom}. -Anche se UDP è completamente diverso rispetto a TCP resta identica la -possibilità di gestire più canali di comunicazione fra due macchine -utilizzando le porte. In questo caso il server dovrà usare comunque la +Anche se UDP è completamente diverso rispetto a TCP resta identica la +possibilità di gestire più canali di comunicazione fra due macchine +utilizzando le porte. In questo caso il server dovrà usare comunque la funzione \func{bind} per scegliere la porta su cui ricevere i dati, e come nel -caso dei socket TCP si potrà usare il comando \cmd{netstat}\footnote{per - ottenere il risultato mostrato occorre usare le opzioni \texttt{-anu}.} per +caso dei socket TCP si potrà usare il comando \cmd{netstat} per verificare quali socket sono in ascolto: \begin{verbatim} +[piccardi@gont gapil]# netstat -anu Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State udp 0 0 0.0.0.0:32768 0.0.0.0:* @@ -100,10 +103,10 @@ in questo caso abbiamo attivi il DNS (sulla porta 53, e sulla 32768 per la connessione di controllo del server \cmd{named}) ed un server DHCP (sulla porta 67). -Si noti però come in questo caso la colonna che indica lo stato sia vuota. I +Si noti però come in questo caso la colonna che indica lo stato sia vuota. I socket UDP infatti non hanno uno stato. Inoltre anche in presenza di traffico -non si avranno indicazioni delle connessioni attive, proprio perché non esiste -niente di simile per i socket UDP, il kernel si limita infatti a ricevere i +non si avranno indicazioni delle connessioni attive, proprio perché questo +concetto non esiste per i socket UDP, il kernel si limita infatti a ricevere i pacchetti ed inviarli al processo in ascolto sulla porta cui essi sono destinati, oppure a scartarli inviando un messaggio \textit{ICMP port unreachable} qualora non vi sia nessun processo in ascolto. @@ -112,27 +115,28 @@ destinati, oppure a scartarli inviando un messaggio \textit{ICMP port \subsection{Le funzioni \func{sendto} e \func{recvfrom}} \label{sec:UDP_sendto_recvfrom} -Come accennato in \secref{sec:UDP_characteristics} le due funzioni principali -usate per la trasmissione di dati attraverso i socket UDP, sono \func{sendto} -e \func{recvfrom}. La necessità di usare queste funzioni è dovuta al fatto che -non esistendo con UDP il concetto di connessione, non si ha neanche a -disposizione un \textsl{socket connesso} su cui sia possibile usare -direttamente \func{read} e \func{write} avendo già stabilito (grazie alla -chiamata ad \func{accept} che lo associa ad una connessione) quali sono +Come accennato in sez.~\ref{sec:UDP_characteristics} le due funzioni +principali usate per la trasmissione di dati attraverso i socket UDP sono +\func{sendto} e \func{recvfrom}. La necessità di usare queste funzioni è +dovuta al fatto che non esistendo con UDP il concetto di connessione, non si +ha neanche a disposizione un \textsl{socket connesso} su cui sia possibile +usare direttamente \func{read} e \func{write} avendo già stabilito (grazie +alla chiamata ad \func{accept} che lo associa ad una connessione) quali sono sorgente e destinazione dei dati. Per questo motivo nel caso di UDP diventa essenziale utilizzare queste due -funzioni, che sono comunque usabili in generale per la trasmissione di dati -attraverso qualunque tipo di socket, dato che esse hanno la caratteristica di -provvedere tre argomenti aggiuntivi che consentono di specificare destinazione -o origine dei dati trasmessi. La prima delle due funzioni è \funcd{sendto} ed -il suo prototipo\footnote{il prototipo illustrato è quello utilizzato dalle - \acr{glibc}, che seguono le \textit{Single Unix Specification}, l'argomento - \param{flags} era di tipo \type{int} nei vari BSD4.*, mentre nelle - \acr{libc4} e \acr{libc5} veniva usato un \type{unsigned int}; l'argomento - \param{len} era \type{int} nei vari BSD4.* e nelle \acr{libc4}, ma - \type{size\_t} nelle \acr{libc5}; infine l'argomento \param{tolen} era - \type{int} nei vari BSD4.* nelle \acr{libc4} e nelle \acr{libc5}.} è: +funzioni, che sono comunque utilizzabili in generale per la trasmissione di +dati attraverso qualunque tipo di socket. Esse hanno la caratteristica di +prevedere tre argomenti aggiuntivi attraverso i quali è possibile specificare +la destinazione dei dati trasmessi o ottenere l'origine dei dati ricevuti. La +prima di queste funzioni è \funcd{sendto} ed il suo prototipo\footnote{il + prototipo illustrato è quello utilizzato dalle \acr{glibc}, che seguono le + \textit{Single Unix Specification}, l'argomento \param{flags} era di tipo + \texttt{int} nei vari BSD4.*, mentre nelle \acr{libc4} e \acr{libc5} veniva + usato un \texttt{unsigned int}; l'argomento \param{len} era \texttt{int} nei + vari BSD4.* e nelle \acr{libc4}, ma \type{size\_t} nelle \acr{libc5}; infine + l'argomento \param{tolen} era \texttt{int} nei vari BSD4.* nelle \acr{libc4} + e nelle \acr{libc5}.} è: \begin{functions} \headdecl{sys/types.h} \headdecl{sys/socket.h} @@ -146,72 +150,81 @@ il suo prototipo\footnote{il prototipo illustrato successo e -1 per un errore; nel qual caso \var{errno} viene impostata al rispettivo codice di errore: \begin{errlist} - \item[\errcode{EAGAIN}] il socket è in modalità non bloccante, ma - l'operazione richede che la funzione si blocchi. + \item[\errcode{EAGAIN}] il socket è in modalità non bloccante, ma + l'operazione richiede che la funzione si blocchi. \item[\errcode{ECONNRESET}] l'altro capo della comunicazione ha resettato la - conessione. - \item[\errcode{EDESTADDRREQ}] il socket non è di tipo connesso, e non si è + connessione. + \item[\errcode{EDESTADDRREQ}] il socket non è di tipo connesso, e non si è specificato un indirizzo di destinazione. - \item[\errcode{EISCONN}] il socket è già connesso, ma si è specificato un + \item[\errcode{EISCONN}] il socket è già connesso, ma si è specificato un destinatario. \item[\errcode{EMSGSIZE}] il tipo di socket richiede l'invio dei dati in un blocco unico, ma la dimensione del messaggio lo rende impossibile. - \item[\errcode{ENOBUFS}] la coda di uscita dell'interfaccia è già piena (di + \item[\errcode{ENOBUFS}] la coda di uscita dell'interfaccia è già piena (di norma Linux non usa questo messaggio ma scarta silenziosamente i pacchetti). - \item[\errcode{ENOTCONN}] il socket non è connesso e non si è specificata + \item[\errcode{ENOTCONN}] il socket non è connesso e non si è specificata una destinazione. - \item[\errcode{EOPNOTSUPP}] il valore di \param{flag} non è appropriato per + \item[\errcode{EOPNOTSUPP}] il valore di \param{flag} non è appropriato per il tipo di socket usato. - \item[\errcode{EPIPE}] il capo locale della connessione è stato chiuso, si - riceverà anche un segnale di \const{SIGPIPE}, a meno di non aver impostato + \item[\errcode{EPIPE}] il capo locale della connessione è stato chiuso, si + riceverà anche un segnale di \const{SIGPIPE}, a meno di non aver impostato \const{MSG\_NOSIGNAL} in \param{flags}. \end{errlist} ed anche \errval{EFAULT}, \errval{EBADF}, \errval{EINVAL}, \errval{EINTR}, - \errval{ENOMEM}, \errval{ENOTSOCK} più gli eventuali altri errori relativi + \errval{ENOMEM}, \errval{ENOTSOCK} più gli eventuali altri errori relativi ai protocolli utilizzati.} \end{functions} I primi tre argomenti sono identici a quelli della funzione \func{write} e -specificano il socket \param{sockfd} a cui si fa riferimento ed il buffer -\param{buf} (e relativa lunghezza \param{len}) che contiene i dati da inviare. -Come per \func{write} la funzione ritorna il numero di byte inviati; nel caso -di UDP però questo deve sempre corrispondere alla dimensione totale -specificata da \param{len} in quanto i dati vengono sempre inviati in forma di -pacchetto e non possono essere spezzati in invii successivi. Qualora non ci -sia spazio nel buffer di uscita la funzione si blocca (a meno di non avere -aperto il socket in modalità non bloccante), se invece non è possibile inviare -il messaggio all'interno di un unico pacchetto essa fallisce con l'errore di -\errcode{EMSGSIZE}. +specificano il socket \param{sockfd} a cui si fa riferimento, il buffer +\param{buf} che contiene i dati da inviare e la relativa lunghezza +\param{len}. Come per \func{write} la funzione ritorna il numero di byte +inviati; nel caso di UDP però questo deve sempre corrispondere alla dimensione +totale specificata da \param{len} in quanto i dati vengono sempre inviati in +forma di pacchetto e non possono essere spezzati in invii successivi. Qualora +non ci sia spazio nel buffer di uscita la funzione si blocca (a meno di non +avere aperto il socket in modalità non bloccante), se invece non è possibile +inviare il messaggio all'interno di un unico pacchetto (ad esempio perché +eccede le dimensioni massime del protocollo sottostante utilizzato) essa +fallisce con l'errore di \errcode{EMSGSIZE}. I due argomenti \param{to} e \param{tolen} servono a specificare la -destinazione del messaggio da inviare, e prendono l'indirizzo di quest'ultima, -nella stessa forma in cui lo si specificherebbe per \func{connect}: \param{to} -deve cioè contere l'indirizzo IP e la porta di destinazione cui si vogliono -inviare i dati e \param{tolen} la relativa dimensione. - -Se il socket è di un tipo che prevede le connessioni, questo deve essere già -connesso prima di eseguire la funzione (altrimenti si avrà un errore di -\errcode{ENOTCONN}) ed inoltre questi due ultimi argomenti devono essere -inizializzati rispettivamente a \const{NULL} e 0 (di solito vengono ignorati, -ma si potrebbe ricevere altrimenti anche un errore di \errcode{EISCONN}). - -Infine l'argomento \param{flags} è un intero usato come maschera binaria che -permette di impostare una serie di modalità di funzionamento della -comunicazione attraverso il socket (come \const{MSG\_NOSIGNAL} che impedisce -l'invio del segnale \const{SIGPIPE} quando si è già chiuso il capo locale -della connessione). Torneremo con maggiori dettagli sul significato di questo -argomento in \secref{sec:xxx_sendmsg}, per il momento ci si può limitare ad -usare sempre un valore nullo. - -La seconda funzione utilizzata nella comunicazione fra socket UDP è -\funcd{recvfrom} che serve invece a ricevere i dati inviati da un altro -socket, il suo prototipo\footnote{il prototipo è quello delle \acr{glibc} che - seguono le \textit{Single Unix Specification}, i vari BSD4.*, le \acr{libc4} - e le \acr{libc5} usano un \type{int} come valore di ritorno; per gli - argomenti \param{flags} e \param{len} vale quanto detto a proposito di - \func{sendto}; infine l'argomento \param{fromlen} è \type{int} per i vari - BSD4.*, le \acr{libc4} e le \acr{libc5}.} è: +destinazione del messaggio da inviare, e indicano rispettivamente la struttura +contenente l'indirizzo di quest'ultima e la sua dimensione; questi argomenti +vanno specificati stessa forma in cui li si sarebbero usati con +\func{connect}. Nel nostro caso \param{to} dovrà puntare alla struttura +contenente l'indirizzo IP e la porta di destinazione verso cui si vogliono +inviare i dati (questo è indifferente rispetto all'uso di TCP o UDP, usando +socket diversi si sarebbero dovute utilizzare le rispettive strutture degli +indirizzi). + +Se il socket è di un tipo che prevede le connessioni (ad esempio un socket +TCP), questo deve essere già connesso prima di poter eseguire la funzione, in +caso contrario si riceverà un errore di \errcode{ENOTCONN}. In questo +specifico caso in cui gli argomenti \param{to} e \param{tolen} non servono +essi dovranno essere inizializzati rispettivamente a \const{NULL} e 0; +normalmente quando si opera su un socket connesso essi vengono ignorati, ma +qualora si sia specificato un indirizzo è possibile ricevere un errore di +\errcode{EISCONN}. + +Finora abbiamo tralasciato l'argomento \param{flags}; questo è un intero usato +come maschera binaria che permette di impostare una serie di modalità di +funzionamento della comunicazione attraverso il socket (come +\const{MSG\_NOSIGNAL} che impedisce l'invio del segnale \const{SIGPIPE} quando +si è già chiuso il capo locale della connessione). Torneremo con maggiori +dettagli sul significato di questo argomento in sez.~\ref{sec:net_sendmsg}, +dove tratteremo le funzioni avanzate dei socket, per il momento ci si può +limitare ad usare sempre un valore nullo. + +La seconda funzione utilizzata nella comunicazione fra socket UDP è +\funcd{recvfrom}, che serve a ricevere i dati inviati da un altro socket; il +suo prototipo\footnote{il prototipo è quello delle \acr{glibc} che seguono le + \textit{Single Unix Specification}, i vari BSD4.*, le \acr{libc4} e le + \acr{libc5} usano un \texttt{int} come valore di ritorno; per gli argomenti + \param{flags} e \param{len} vale quanto detto a proposito di \func{sendto}; + infine l'argomento \param{fromlen} è \texttt{int} per i vari BSD4.*, le + \acr{libc4} e le \acr{libc5}.} è: \begin{functions} \headdecl{sys/types.h} \headdecl{sys/socket.h} @@ -219,53 +232,588 @@ socket, il suo prototipo\footnote{il prototipo \funcdecl{ssize\_t recvfrom(int sockfd, const void *buf, size\_t len, int flags, const struct sockaddr *from, socklen\_t *fromlen)} - Riceve un messaggio ad un altro socket. + Riceve un messaggio ad un socket. \bodydesc{La funzione restituisce il numero di byte ricevuti in caso di - successo e -1 in caso di errore; nel qual caso \var{errno} assumerà il + successo e -1 in caso di errore; nel qual caso \var{errno} assumerà il valore: \begin{errlist} - \item[\errcode{EAGAIN}] il socket è in modalità non bloccante, ma - l'operazione richede che la funzione si blocchi, oppure si è impostato un - timeout in ricezione e questo è scaduto. + \item[\errcode{EAGAIN}] il socket è in modalità non bloccante, ma + l'operazione richiede che la funzione si blocchi, oppure si è impostato un + timeout in ricezione e questo è scaduto. \item[\errcode{ECONNREFUSED}] l'altro capo della comunicazione ha rifiutato - la connessione (in genere perché il relativo servizio non è disponibile). - \item[\errcode{ENOTCONN}] il socket è di tipo connesso, ma non si è eseguita + la connessione (in genere perché il relativo servizio non è disponibile). + \item[\errcode{ENOTCONN}] il socket è di tipo connesso, ma non si è eseguita la connessione. \end{errlist} ed anche \errval{EFAULT}, \errval{EBADF}, \errval{EINVAL}, \errval{EINTR}, - \errval{ENOMEM}, \errval{ENOTSOCK} più gli eventuali altri errori relativi + \errval{ENOMEM}, \errval{ENOTSOCK} più gli eventuali altri errori relativi ai protocolli utilizzati.} \end{functions} Come per \func{sendto} i primi tre argomenti sono identici agli analoghi di \func{read}: dal socket vengono letti \param{len} byte che vengono salvati nel buffer \param{buf}. A seconda del tipo di socket (se di tipo \textit{datagram} -o \textit{stream}) inoltre i byte in eccesso che non sono stati letti possono -rispettivamente andare persi o restare disponibili per una lettura -successiva. +o di tipo \textit{stream}) i byte in eccesso che non sono stati letti possono +rispettivamente andare persi o restare disponibili per una lettura successiva. +Se non sono disponibili dati la funzione si blocca, a meno di non aver aperto +il socket in modalità non bloccante, nel qual caso si avrà il solito errore di +\errcode{EAGAIN}. Qualora \param{len} ecceda la dimensione del pacchetto la +funzione legge comunque i dati disponibili, ed il suo valore di ritorno è +comunque il numero di byte letti. + +I due argomenti \param{from} e \param{fromlen} sono utilizzati per ottenere +l'indirizzo del mittente del pacchetto che è stato ricevuto, e devono essere +opportunamente inizializzati; il primo deve contenere il puntatore alla +struttura (di tipo \ctyp{sockaddr}) che conterrà l'indirizzo e il secondo il +puntatore alla variabile con la dimensione di detta struttura. Si tenga +presente che mentre il contenuto della struttura \ctyp{sockaddr} cui punta +\param{from} può essere qualunque, la variabile puntata da \param{fromlen} +deve essere opportunamente inizializzata a \code{sizeof(sockaddr)}, +assicurandosi che la dimensione sia sufficiente a contenere tutti i dati +dell'indirizzo.\footnote{si ricordi che \ctyp{sockaddr} è un tipo generico che + serve ad indicare la struttura corrispondente allo specifico tipo di + indirizzo richiesto, il valore di \param{fromlen} pone un limite alla + quantità di dati che verranno scritti sulla struttura puntata da + \param{from} e se è insufficiente l'indirizzo risulterà corrotto.} Al +ritorno della funzione si otterranno i dati dell'indirizzo e la sua effettiva +lunghezza, (si noti che \param{fromlen} è un valore intero ottenuto come +\itindex{value~result~argument} \textit{value result argument}). Se non si è +interessati a questa informazione, entrambi gli argomenti devono essere +inizializzati al valore \const{NULL}. + +Una differenza fondamentale del comportamento di queste funzioni rispetto alle +usuali \func{read} e \func{write} che abbiamo usato con i socket TCP è che in +questo caso è perfettamente legale inviare con \func{sendto} un pacchetto +vuoto (che nel caso conterrà solo le intestazioni di IP e di UDP), +specificando un valore nullo per \param{len}. Allo stesso modo è possibile +ricevere con \func{recvfrom} un valore di ritorno di 0 byte, senza che questo +possa configurarsi come una chiusura della connessione\footnote{dato che la + connessione non esiste, non ha senso parlare di chiusura della connessione, + questo significa anche che con i socket UDP non è necessario usare + \func{close} o \func{shutdown} per terminare la comunicazione.} o come una +cessazione delle comunicazioni. + + + +\subsection{Un client UDP elementare} +\label{sec:UDP_daytime_client} + +Vediamo allora come implementare un primo client elementare con dei socket +UDP. Ricalcando quanto fatto nel caso dei socket TCP prenderemo come primo +esempio l'uso del servizio \textit{daytime}, utilizzando questa volta UDP. Il +servizio è definito nell'\href{http://www.ietf.org/rfc/rfc0862.txt}{RFC~867}, +che nel caso di uso di UDP prescrive che il client debba inviare un pacchetto +UDP al server (di contenuto non specificato), il quale risponderà a inviando a +sua volta un pacchetto UDP contenente la data. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \includecodesample{listati/UDP_daytime.c} + \end{minipage} + \normalsize + \caption{Sezione principale del client per il servizio \textit{daytime} su + UDP.} + \label{fig:UDP_daytime_client} +\end{figure} +In fig.~\ref{fig:UDP_daytime_client} è riportato la sezione principale del +codice del nostro client, il sorgente completo si trova nel file +\file{UDP\_daytime.c} distribuito con gli esempi allegati alla guida; al +solito si è tralasciato di riportare in figura la sezione relativa alla +gestione delle opzioni a riga di comando (nel caso praticamente assenti). + +Il programma inizia (\texttt{\small 9--12}) con la creazione del socket, al +solito uscendo dopo aver stampato un messaggio in caso errore. Si noti come in +questo caso, rispetto all'analogo client basato su socket TCP di +fig.~\ref{fig:TCP_daytime_client_code} si sia usato per il tipo di socket il +valore \const{SOCK\_DGRAM}, pur mantenendosi nella stessa famiglia data da +\const{AF\_INET}. + +Il passo successivo (\texttt{\small 13--21}) è l'inizializzazione della +struttura degli indirizzi; prima (\texttt{\small 14}) si cancella +completamente la stessa con \func{memset}, (\texttt{\small 15}) poi si imposta +la famiglia dell'indirizzo ed infine (\texttt{\small 16} la porta. Infine +(\texttt{\small 18--21}) si ricava l'indirizzo del server da contattare +dall'argomento passato a riga di comando, convertendolo con \func{inet\_pton}. +Si noti come questa sezione sia identica a quella del client TCP di +fig.~\ref{fig:TCP_daytime_client_code}, in quanto la determinazione dell'uso +di UDP al posto di TCP è stata effettuata quando si è creato il socket. + +Una volta completate le inizializzazioni inizia il corpo principale del +programma, il primo passo è inviare, come richiesto dal protocollo, un +pacchetto al server. Questo lo si fa (\texttt{\small 16}) inviando un +pacchetto vuoto (si ricordi quanto detto in +sez.~\ref{sec:UDP_sendto_recvfrom}) con \func{sendto}, avendo cura di passare +un valore nullo per il puntatore al buffer e la lunghezza del messaggio. In +realtà il protocollo non richiede che il pacchetto sia vuoto, ma dato che il +server comunque ne ignorerà il contenuto, è inutile inviare dei dati. + +Verificato (\texttt{\small 24--27}) che non ci siano stati errori nell'invio +si provvede (\texttt{\small 28}) ad invocare \func{recvfrom} per ricevere la +risposta del server. Si controlla poi (\texttt{\small 29--32}) che non vi +siano stati errori in ricezione (uscendo con un messaggio in caso contrario); +se è tutto a posto la variabile \var{nread} conterrà la dimensione del +messaggio di risposta inviato dal server che è stato memorizzato su +\var{buffer}, se (\texttt{\small 34}) pertanto il valore è positivo si +provvederà (\texttt{\small 35}) a terminare la stringa contenuta nel buffer di +lettura\footnote{si ricordi che, come illustrato in + sez.~\ref{sec:TCP_daytime_client}, il server invia in risposta una stringa + contenente la data, terminata dai due caratteri CR e LF, che pertanto prima + di essere stampata deve essere opportunamente terminata con un NUL.} e a +stamparla (\texttt{\small 36}) sullo standard output, controllando anche in +questo caso (\texttt{\small 36--38}) l'esito dell'operazione, ed uscendo con +un messaggio in caso di errore. + +Se pertanto si è avuto cura di attivare il server del servizio +\textit{daytime}\footnote{di norma questo è un servizio standard fornito dal + \textsl{superdemone} \cmd{inetd}, per cui basta abilitarlo nel file di + configurazione di quest'ultimo, avendo cura di predisporre il servizio su + UDP.} potremo verificare il funzionamento del nostro client interrogando +quest'ultimo con: +\begin{verbatim} +[piccardi@gont sources]$ ./daytime 127.0.0.1 +Sat Mar 20 23:17:13 2004 +\end{verbatim}%$ +ed osservando il traffico con uno sniffer potremo effettivamente vedere lo +scambio dei due pacchetti, quello vuoto di richiesta, e la risposta del +server: +\begin{verbatim} +[root@gont gapil]# tcpdump -i lo +tcpdump: listening on lo +23:41:21.645579 localhost.32780 > localhost.daytime: udp 0 (DF) +23:41:21.645710 localhost.daytime > localhost.32780: udp 26 (DF) +\end{verbatim} +Una differenza fondamentale del nostro client è che in questo caso, non +disponendo di una connessione, è per lui impossibile riconoscere errori di +invio relativi alla rete. La funzione \func{sendto} infatti riporta solo +errori locali, i dati vengono comunque scritti e la funzione ritorna senza +errori anche se il server non è raggiungibile o non esiste un server in +ascolto sull'indirizzo di destinazione. Questo comporta ad esempio che se si +usa il nostro programma interrogando un server inesistente questo resterà +perennemente bloccato nella chiamata a \func{recvfrom}, fin quando non lo +interromperemo. Vedremo in sez.~\ref{sec:UDP_connect} come si può porre rimedio +a questa problematica. + + +\subsection{Un server UDP elementare} +\label{sec:UDP_daytime_server} + +Nella sezione precedente abbiamo visto come scrivere un client elementare per +servizio \textit{daytime}, vediamo in questa come deve essere scritto un +server. Si ricordi che il compito di quest'ultimo è quello di ricevere un +pacchetto di richiesta ed inviare in risposta un pacchetto contenente una +stringa con la data corrente. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \includecodesample{listati/UDP_daytimed.c} + \end{minipage} + \normalsize + \caption{Sezione principale del server per il servizio \textit{daytime} su + UDP.} + \label{fig:UDP_daytime_server} +\end{figure} + +In fig.~\ref{fig:UDP_daytime_server} è riportato la sezione principale del +codice del nostro client, il sorgente completo si trova nel file +\file{UDP\_daytimed.c} distribuito con gli esempi allegati alla guida; anche +in questo caso si è omessa la sezione relativa alla gestione delle opzioni a +riga di comando (la sola presente è \texttt{-v} che permette di stampare a +video l'indirizzo associato ad ogni richiesta). + +Anche in questo caso la prima parte del server (\texttt{\small 9--23}) è +sostanzialmente identica a quella dell'analogo server per TCP illustrato in +fig.~\ref{fig:TCP_daytime_cunc_server_code}; si inizia (\texttt{\small 10}) +con il creare il socket, uscendo con un messaggio in caso di errore +(\texttt{\small 10--13}), e di nuovo la sola differenza con il caso precedente +è il diverso tipo di socket utilizzato. Dopo di che (\texttt{\small 14--18}) +si inizializza la struttura degli indirizzi che poi (\texttt{\small 20}) verrà +usata da \func{bind}; si cancella (\texttt{\small 15}) preventivamente il +contenuto, si imposta (\texttt{\small 16}) la famiglia dell'indirizzo, la +porta (\texttt{\small 17}) e l'indirizzo (\texttt{\small 18}) su cui si +riceveranno i pacchetti. Si noti come in quest'ultimo sia l'indirizzo +generico \const{INADDR\_ANY}; questo significa (si ricordi quanto illustrato +in sez.~\ref{sec:TCP_func_bind}) che il server accetterà pacchetti su uno +qualunque degli indirizzi presenti sulle interfacce di rete della macchina. + +Completata l'inizializzazione tutto quello che resta da fare è eseguire +(\texttt{\small 20--23}) la chiamata a \func{bind}, controllando la presenza +di eventuali errori, ed uscendo con un avviso qualora questo fosse il caso. +Nel caso di socket UDP questo è tutto quello che serve per consentire al +server di ricevere i pacchetti a lui indirizzati, e non è più necessario +chiamare successivamente \func{listen}. In questo caso infatti non esiste il +concetto di connessione, e quindi non deve essere predisposta una coda delle +connessioni entranti. Nel caso di UDP i pacchetti arrivano al kernel con un +certo indirizzo ed una certa porta di destinazione, il kernel controlla se +corrispondono ad un socket che è stato \textsl{legato} ad essi con +\func{bind}, qualora questo sia il caso scriverà il contenuto all'interno del +socket, così che il programma possa leggerlo, altrimenti risponderà alla +macchina che ha inviato il pacchetto con un messaggio ICMP di tipo +\textit{port unreachable}. + +Una volta completata la fase di inizializzazione inizia il corpo principale +(\texttt{\small 24--44}) del server, mantenuto all'interno di un ciclo +infinito in cui si trattano le richieste. Il ciclo inizia (\texttt{\small 26}) +con una chiamata a \func{recvfrom}, che si bloccherà in attesa di pacchetti +inviati dai client. Lo scopo della funzione è quello di ritornare tutte le +volte che un pacchetto viene inviato al server, in modo da poter ricavare da +esso l'indirizzo del client a cui inviare la risposta in \var{addr}. Per +questo motivo in questo caso (al contrario di quanto fatto in +fig.~\ref{fig:UDP_daytime_client}) si è avuto cura di passare gli argomenti +\var{addr} e \var{len} alla funzione. Dopo aver controllato (\texttt{\small + 27--30}) la presenza di eventuali errori (uscendo con un messaggio di errore +qualora ve ne siano) si verifica (\texttt{\small 31}) se è stata attivata +l'opzione \texttt{-v} (che imposta la variabile \var{verbose}) stampando nel +caso (\texttt{\small 32--35}) l'indirizzo da cui si è appena ricevuto una +richiesta (questa sezione è identica a quella del server TCP illustrato in +fig.~\ref{fig:TCP_daytime_cunc_server_code}). + +Una volta ricevuta la richiesta resta solo da ottenere il tempo corrente +(\texttt{\small 36}) e costruire (\texttt{\small 37}) la stringa di risposta, +che poi verrà inviata (\texttt{\small 38}) al client usando \func{sendto}, +avendo al solito cura di controllare (\texttt{\small 40--42}) lo stato di +uscita della funzione e trattando opportunamente la condizione di errore. + +Si noti come per le peculiarità del protocollo si sia utilizzato un server +iterativo, che processa le richieste una alla volta via via che gli arrivano. +Questa è una caratteristica comune dei server UDP, conseguenza diretta del +fatto che non esiste il concetto di connessione, per cui non c'è la necessità +di trattare separatamente le singole connessioni. Questo significa anche che è +il kernel a gestire la possibilità di richieste multiple in contemporanea; +quello che succede è semplicemente che il kernel accumula in un buffer in +ingresso i pacchetti UDP che arrivano e li restituisce al processo uno alla +volta per ciascuna chiamata di \func{recvfrom}; nel nostro caso sarà poi +compito del server distribuire le risposte sulla base dell'indirizzo da cui +provengono le richieste. + + +\subsection{Le problematiche dei socket UDP} +\label{sec:UDP_problems} + +L'esempio del servizio \textit{daytime} illustrato nelle precedenti sezioni +è in realtà piuttosto particolare, e non evidenzia quali possono essere i +problemi collegati alla mancanza di affidabilità e all'assenza del concetto di +connessione che sono tipiche dei socket UDP. In tal caso infatti il protocollo +è estremamente semplice, dato che la comunicazione consiste sempre in una +richiesta seguita da una risposta, per uno scambio di dati effettuabile con un +singolo pacchetto, per cui tutti gli eventuali problemi sarebbero assai più +complessi da rilevare. + +Anche qui però possiamo notare che se il pacchetto di richiesta del client, o +la risposta del server si perdono, il client resterà permanentemente bloccato +nella chiamata a \func{recvfrom}. Per evidenziare meglio quali problemi si +possono avere proviamo allora con un servizio leggermente più complesso come +\textit{echo}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \includecodesample{listati/UDP_echo_first.c} + \end{minipage} + \normalsize + \caption{Sezione principale della prima versione client per il servizio + \textit{echo} su UDP.} + \label{fig:UDP_echo_client} +\end{figure} + +In fig.~\ref{fig:UDP_echo_client} è riportato un estratto del corpo principale +del nostro client elementare per il servizio \textit{echo} (al solito il +codice completo è con i sorgenti allegati). Le uniche differenze con l'analogo +client visto in fig.~\ref{fig:TCP_echo_client_1} sono che al solito si crea +(\texttt{\small 14}) un socket di tipo \const{SOCK\_DGRAM}, e che non è +presente nessuna chiamata a \func{connect}. Per il resto il funzionamento del +programma è identico, e tutto il lavoro viene effettuato attraverso la +chiamata (\texttt{\small 28}) alla funzione \func{ClientEcho} che stavolta +però prende un argomento in più, che è l'indirizzo del socket. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \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_client_echo} +\end{figure} + +Ovviamente in questo caso il funzionamento della funzione, il cui codice è +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. + +Data l'assenza di una connessione come nel caso di TCP il meccanismo è molto +più semplice da gestire. Al solito si esegue un ciclo infinito (\texttt{\small + 6--30}) che parte dalla lettura (\texttt{\small 7}) sul buffer di invio +\var{sendbuff} di una stringa dallo standard input, se la stringa è vuota +(\texttt{\small 7--9}), indicando che l'input è terminato, si ritorna +immediatamente causando anche la susseguente terminazione del programma. + +Altrimenti si procede (\texttt{\small 10--11}) all'invio della stringa al +destinatario invocando \func{sendto}, utilizzando, oltre alla stringa appena +letta, gli argomenti passati nella chiamata a \func{ClientEcho}, ed in +particolare l'indirizzo del server che si è posto in \var{serv\_addr}; qualora +(\texttt{\small 12}) si riscontrasse un errore si provvederà al solito +(\texttt{\small 13--14}) ad uscire con un messaggio di errore. + +Il passo immediatamente seguente (\texttt{\small 17}) l'invio è quello di +leggere l'eventuale risposta del server con \func{recvfrom}; si noti come in +questo caso si sia scelto di ignorare l'indirizzo dell'eventuale pacchetto di +risposta, controllando (\texttt{\small 18--21}) soltanto la presenza di un +errore (nel qual caso al solito si ritorna dopo la stampa di un adeguato +messaggio). Si noti anche come, rispetto all'analoga funzione +\func{ClientEcho} utilizzata nel client TCP illustrato in +sez.~\ref{sec:TCP_echo_client} non si sia controllato il caso di un messaggio +nullo, dato che, nel caso di socket UDP, questo non significa la terminazione +della comunicazione. + +L'ultimo passo (\texttt{\small 17}) è quello di terminare opportunamente la +stringa di risposta nel relativo buffer per poi provvedere alla sua stampa +sullo standard output, eseguendo il solito controllo (ed eventuale uscita con +adeguato messaggio informativo) in caso di errore. + +In genere fintanto che si esegue il nostro client in locale non sorgerà nessun +problema, se però si proverà ad eseguirlo attraverso un collegamento remoto +(nel caso dell'esempio seguente su una VPN, attraverso una ADSL abbastanza +congestionata) e in modalità non interattiva, la probabilità di perdere +qualche pacchetto aumenta, ed infatti, eseguendo il comando come: +\begin{verbatim} +[piccardi@gont sources]$ cat UDP_echo.c | ./echo 192.168.1.120 +/* UDP_echo.c + * + * Copyright (C) 2004 Simone Piccardi +... +... +/* + * Include needed headers + +\end{verbatim}%$ +si otterrà che, dopo aver correttamente stampato alcune righe, il programma si +blocca completamente senza stampare più niente. Se al contempo si fosse tenuto +sotto controllo il traffico UDP diretto o proveniente dal servizio +\textit{echo} con \cmd{tcpdump} si sarebbe ottenuto: +\begin{verbatim} +[root@gont gapil]# tcpdump \( dst port 7 or src port 7 \) +... +... +18:48:16.390255 gont.earthsea.ea.32788 > 192.168.1.120.echo: udp 4 (DF) +18:48:17.177613 192.168.1.120.echo > gont.earthsea.ea.32788: udp 4 (DF) +18:48:17.177790 gont.earthsea.ea.32788 > 192.168.1.120.echo: udp 26 (DF) +18:48:17.964917 192.168.1.120.echo > gont.earthsea.ea.32788: udp 26 (DF) +18:48:17.965408 gont.earthsea.ea.32788 > 192.168.1.120.echo: udp 4 (DF) +\end{verbatim} +che come si vede il traffico fra client e server si interrompe dopo l'invio di +un pacchetto UDP per il quale non si è ricevuto risposta. + +Il problema è che in tutti i casi in cui un pacchetto di risposta si perde, o +una richiesta non arriva a destinazione, il nostro programma si bloccherà +nell'esecuzione di \func{recvfrom}. Lo stesso avviene anche se il server non è +in ascolto, in questo caso però, almeno dal punto di vista dello scambio di +pacchetti, il risultato è diverso, se si lancia al solito il programma e si +prova a scrivere qualcosa si avrà ugualmente un blocco su \func{recvfrom} ma +se si osserva il traffico con \cmd{tcpdump} si vedrà qualcosa del tipo: +\begin{verbatim} +[root@gont gapil]# tcpdump \( dst 192.168.0.2 and src 192.168.1.120 \) \ + or \( src 192.168.0.2 and dst 192.168.1.120 \) +tcpdump: listening on eth0 +00:43:27.606944 gont.earthsea.ea.32789 > 192.168.1.120.echo: udp 6 (DF) +00:43:27.990560 192.168.1.120 > gont.earthsea.ea: icmp: 192.168.1.120 udp port +echo unreachable [tos 0xc0] +\end{verbatim} +cioè in questo caso si avrà in risposta un pacchetto ICMP di destinazione +irraggiungibile che ci segnala che la porta in questione non risponde. + +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 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,\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 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. \subsection{L'uso della funzione \func{connect} con i socket UDP} \label{sec:UDP_connect} -Come illustrato in \secref{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. +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. 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 argomento 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. + +\index{socket!locali|(} + \section{I socket \textit{Unix domain}} \label{sec:unix_socket} -Benché i socket Unix domain non siano strattamente 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. Nonostante le loro peculiarità infatti, +l'interfaccia di programmazione che serve ad utilizzarli resta sempre quella +dei socket. + + + +\subsection{Il passaggio di file descriptor} +\label{sec:sock_fd_passing} + + + +\index{socket!locali|)} + + +\section{Altri socket} +\label{sec:socket_other} + +Tratteremo in questa sezione gli altri tipi particolari di socket supportati +da Linux, come quelli relativi a particolare protocolli di trasmissione, i +socket \textit{netlink} che definiscono una interfaccia di comunicazione con +il kernel, ed i \textit{packet socket} che consentono di inviare pacchetti +direttamente a livello delle interfacce di rete. + +\subsection{I socket \textit{raw}} +\label{sec:socket_raw} + +Tratteremo in questa sezione i cosiddetti \textit{raw socket}, con i quali si +possono \textsl{forgiare} direttamente i pacchetti a tutti i livelli dello +stack dei protocolli. + +\subsection{I socket \textit{netlink}} +\label{sec:socket_netlink} + + +\subsection{I \textit{packet socket}} +\label{sec:packet_socket} + + +% articoli interessanti: +% http://www.linuxjournal.com/article/5617 +% http://www.linuxjournal.com/article/4659 +% + +% LocalWords: socket cap TCP UDP domain sez NFS DNS stream datagram PF INET to +% LocalWords: IPv tab SOCK DGRAM three way handshake client fig bind listen AF +% LocalWords: accept recvfrom sendto connect netstat named DHCP kernel ICMP CR +% LocalWords: port unreachable read write glibc Specification flags int BSD LF +% LocalWords: libc unsigned len size tolen sys ssize sockfd const void buf MSG +% LocalWords: struct sockaddr socklen errno EAGAIN ECONNRESET EDESTADDRREQ RFC +% LocalWords: EISCONN EMSGSIZE ENOBUFS ENOTCONN EOPNOTSUPP EPIPE SIGPIPE EBADF +% LocalWords: NOSIGNAL EFAULT EINVAL EINTR ENOMEM ENOTSOCK NULL fromlen from +% LocalWords: ECONNREFUSED value result argument close shutdown daytime nell' +% LocalWords: memset inet pton nread NUL superdemone inetd sniffer daytimed +% LocalWords: INADDR ANY addr echo ClientEcho sendbuff serv VPN tcpdump l'I +% LocalWords: Stevens destination descriptor raw stack netlink packet