X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=othersock.tex;h=868ca66c247362706017f394bd3522e67860dce4;hp=d7bc4c0e520f533aa96d408f139a64e35651097f;hb=7502f301c66ac19b976087d4e0fb71807c051791;hpb=400a3e7bbb00a6aa4a146e5d55f3b98c0152146f diff --git a/othersock.tex b/othersock.tex index d7bc4c0..868ca66 100644 --- a/othersock.tex +++ b/othersock.tex @@ -29,30 +29,34 @@ principali e le modalit \label{sec:UDP_characteristics} Come illustrato in \secref{sec:net_udp} UDP è un protocollo molto semplice che -non supporta le connessioni e non è affidabile: i dati vengono inviati in -forma di pacchetti, e non ne è assicurata né la effettiva ricezione né -l'odinamento. +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ù +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 di tipo -\textit{datagram}, in cui i dati arrivano in singoli blocchi che devono essere -letti integralmente. - -Questo diverso comportamento significa anche che i socket UDP, pur restando -nella famiglia \const{PF\_INET}\footnote{o \const{PF\_INET6} qualora si usasse - invece il protocollo IPv6.} devono essere aperti quando si usa la funzione -\func{socket} (si riveda quanto illustrato a suo tempo in -\tabref{tab:sock_sock_valid_combinations}) utilizzando come valore per il tipo -di socket \const{SOCK\_DGRAM}. - -Questa differenza comporta ovviamente il fatto che anche le modalità con cui -si usano i socket UDP sono completamente diverse rispetto ai socket TCP, ed in +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. + +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}) +utilizzando per il tipo di socket il valore \const{SOCK\_DGRAM}. + +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 di 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 \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}. \begin{figure}[htb] \centering @@ -62,20 +66,200 @@ schema illustrato in \figref{fig:UDP_packet-exchange}. \label{fig:UDP_packet-exchange} \end{figure} -Anche se UDP è completamente diverso rispetto a TCP resta identica +Come illustrato in \figref{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 +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}. + +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à +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 +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 +verificare quali socket sono in ascolto: +\begin{verbatim} +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:* +udp 0 0 192.168.1.2:53 0.0.0.0:* +udp 0 0 127.0.0.1:53 0.0.0.0:* +udp 0 0 0.0.0.0:67 0.0.0.0:* +\end{verbatim} +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 +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 +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. \subsection{Le funzioni \func{sendto} e \func{recvfrom}} \label{sec:UDP_sendto_recvfrom} -Come accennato le due funzioni principali usate per la trasmissione di dati -attraverso i socket UDP, ma in generale attraverso qualunque socket che -preveda una comunicazione a pacchetti, sono \func{sendto} e \func{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 +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}.} è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/socket.h} + + \funcdecl{ssize\_t sendto(int sockfd, const void *buf, size\_t len, int + flags, const struct sockaddr *to, socklen\_t tolen)} + + Trasmette un messaggio ad un altro socket. + + \bodydesc{La funzione restituisce il numero di caratteri inviati in caso di + 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{ECONNRESET}] l'altro capo della comunicazione ha resettato la + conessione. + \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 + 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 + norma Linux non usa questo messaggio ma scarta silenziosamente i + pacchetti). + \item[\errcode{ENOTCONN}] il socket non è connesso e non si è specificata + una destinazione. + \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 + \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 + 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}. + +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}.} è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/socket.h} + + \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. + + \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 + 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{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. + \end{errlist} + ed anche \errval{EFAULT}, \errval{EBADF}, \errval{EINVAL}, \errval{EINTR}, + \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. + + + + + +\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. + \section{I socket \textit{Unix domain}} -\label{sec:UDP_socket} +\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