Messo esempio di server elementare.
[gapil.git] / othersock.tex
index c5195dfe6e66cffecf001b92926abf6c098ddd08..00ee9c6619378a94f1b9a11561e9983840efcc51 100644 (file)
@@ -8,19 +8,20 @@
 %% 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, 
+capitolo gli altri tipi di socket, a partire dai socket UDP, e i socket deiit
+\textit{Unix domain} già incontrati in \secref{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
+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.
 
@@ -85,10 +86,10 @@ Anche se UDP 
 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:*
@@ -102,8 +103,8 @@ porta 67).
 
 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.
@@ -113,8 +114,8 @@ destinati, oppure a scartarli inviando un messaggio \textit{ICMP port
 \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}
-\func{recvfrom}. La necessità di usare queste funzioni è dovuta al fatto che
+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
@@ -122,17 +123,18 @@ 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 attraveso 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
+  \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}.} è:
 \begin{functions}
   \headdecl{sys/types.h}
   \headdecl{sys/socket.h}
@@ -173,45 +175,54 @@ il suo prototipo\footnote{il prototipo illustrato 
 \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.
+destinazione del messaggio da inviare, e indicano rispettivamente la struttura
+contentente 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} devrà 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 devranno essere inizializzati rispettivamente a \const{NULL} e 0;
+normalmente quando si opera su un socket conesso 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 \secref{sec:xxx_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 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}.} è:
+\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 \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}.} è:
 \begin{functions}
   \headdecl{sys/types.h}
   \headdecl{sys/socket.h}
@@ -219,7 +230,7 @@ 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
@@ -241,12 +252,195 @@ socket, il suo prototipo\footnote{il prototipo 
 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. 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}.
+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 con i puntatori alle variabili dove la struttura
+contenente quest'ultimo e la relativa lunghezza saranno scritti (si noti che
+\param{fromlen} è un valore intero ottenuto come \textit{value return
+  argoment}).  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 cominicazione.} 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 \figref{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
+\figref{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 dal
+parametro passato a riga di comando, convertendolo con \func{inet\_pton}. Si
+noti come questa sezione sia identica a quella del client TCP di
+\figref{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 \secref{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
+  \secref{sec:TCP_daytime_client}, il server invia in risposta una stringa
+  contenente la data, terminata dai due carratteri 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 \secref{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 \figref{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
+\figref{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 \secref{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}.
 
 
 
@@ -271,6 +465,7 @@ interfaccia 
 
 
 
+
 %%% Local Variables: 
 %%% mode: latex
 %%% TeX-master: "gapil"