\chapter{Introduzione ai socket}
\label{cha:socket_intro}
-Il \textit{socket} (traducibile liberamente come \textsl{manicotto}) è uno dei
-principali meccanismi di comunicazione fra programmi utilizzato in ambito unix
-(e non solo). Il socket costituisce in sostanza un canale di comunicazione fra
-due processi su cui si possono leggere e scrivere dati analogo a quello di una
-pipe ma a differenza di questa e degli altri meccanismi esaminati nel capitolo
-\ref{cha:IPC} i socket non sono limitati alla comunicazione fra processi che
-girano sulla stessa macchina ma possono effettuare la comunicazione anche
-attraverso la rete.
+In questo capitolo inizieremo a spiegare le caratteristiche principali della
+principale interfaccia per la programmazione di rete, quella dei
+\textit{socket}, che pur essendo nata in unix è usata ormai da tutti i sistemi
+operativi.
+
+Dopo una breve panoramica sulle caratteristiche di questa interfaccia vedremo
+come creare un socket e come collegarlo allo specifico protocollo di rete che
+utilizzerà per la comunicazione. Per evitare una introduzione puramente teorica
+concluderemo il capitolo con un primo esempio di applicazione.
+
+\section{Una panoramica}
+\label{sec:sock_overview}
+
+Iniziamo con una descrizione essenziale di cosa sono i \textit{socket} e di
+quali sono i concetti fondamentali da tenere presente quando si ha a che fare
+con essi.
+
+\subsection{I \textit{socket}}
+\label{sec:sock_socket_def}
+
+Il \textit{socket}\footnote{una traduzione letterale potrebbe essere
+ \textsl{manicotto}, ma essendo universalmente noti come socket utilizzeremo
+ sempre la parola inglese} è uno dei principali meccanismi di comunicazione
+fra programmi utilizzato in ambito unix. Il socket costituisce in sostanza un
+canale di comunicazione fra due processi su cui si possono leggere e scrivere
+dati analogo a quello di una pipe ma a differenza di questa e degli altri
+meccanismi esaminati nel capitolo \capref{cha:IPC} i socket non sono limitati
+alla comunicazione fra processi che girano sulla stessa macchina ma possono
+effettuare la comunicazione anche attraverso la rete.
Quella dei socket costituisce infatti la principale API (\textit{Application
Program Interface}) usata nella programmazione di rete. La loro origine
tratteremo in maniera più estesa.
-\section{Concetti base}
+\subsection{Concetti base}
\label{sec:sock_gen}
Per capire il funzionamento dei socket occorre avere presente il funzionamento
-dei protocolli di rete (vedi \ref{cha:network}), ma l'interfaccia è del tutto
-generale e benché le problematiche (e quindi le modalità di risolvere i
+dei protocolli di rete (vedi \capref{cha:network}), ma l'interfaccia è del
+tutto generale e benché le problematiche (e quindi le modalità di risolvere i
problemi) siano diverse a seconda del tipo di protocollo di comunicazione
usato, le funzioni da usare restano le stesse.
gestire la perdita o il rimescolamento dei dati.
-\section{La funzione \texttt{socket}}
+\section{La creazione di un \textit{socket}}
+\label{sec:sock_creation}
+
+Come accennato l'interfaccia dei socket è estremamente flessibile e permette
+di interagire con protocolli di comunicazione anche molto diversi fra di loro;
+in questa sezione vedremo come è possibile creare un socket e come specificare
+il tipo di comunicazione che esso deve utilizzare.
+
+\subsection{La funzione \func{socket}}
\label{sec:sock_socket}
La creazione di un socket avviene attraverso l'uso della funzione
-\texttt{socket} questa restituisce un \textit{socket descriptor} (un valore
-intero non negativo) che come gli analoghi file descriptor di files e alle
-pipes serve come riferimento al socket; in sostanza è l'indice nella tabella
+\func{socket} questa restituisce un \textit{socket descriptor} (un valore
+intero non negativo) che come gli analoghi file descriptor di file e alle
+pipe serve come riferimento al socket; in sostanza è l'indice nella tabella
dei file che contiene i puntatori alle opportune strutture usate dal kernel ed
allocate per ogni processo, (la stessa usata per i files e le pipes [NdA
verificare!]).
-Il prototipo della funzione è definito nell'header \texttt{sys/socket.h}, la
-funzione prende tre parametri, il dominio del socket (che definisce la
-famiglia di protocolli, vedi \ref{sec:sock_domain}), il tipo di socket (che
-definisce lo stile di comunicazione vedi \ref{sec:sock_type}) e il protocollo;
-in genere quest'ultimo è indicato implicitamente dal tipo di socket, per cui
-viene messo a zero (con l'eccezione dei \textit{raw socket}).
+La funzione prende tre parametri, il dominio del socket (che definisce la
+famiglia di protocolli, vedi \secref{sec:sock_domain}), il tipo di socket (che
+definisce lo stile di comunicazione vedi \secref{sec:sock_type}) e il
+protocollo; in genere quest'ultimo è indicato implicitamente dal tipo di
+socket, per cui viene messo a zero (con l'eccezione dei \textit{raw socket}).
-\begin{itemize}
-\item \texttt{int socket(int domain, int type, int protocol)}
+\begin{prototype}{sys/socket.h}{int socket(int domain, int type, int protocol)}
La funzione restituisce un intero positivo se riesce, e -1 se fallisce, in
- quest'ultimo caso la variabile \texttt{errno} è settata con i seguenti
+ quest'ultimo caso la variabile \var{errno} è settata con i seguenti
codici di errore:
- \begin{itemize}
- \item \texttt{EPROTONOSUPPORT} Il tipo di socket o il protocollo scelto non
+ \begin{errlist}
+ \item \macro{EPROTONOSUPPORT} Il tipo di socket o il protocollo scelto non
sono supportati nel dominio.
- \item \texttt{ENFILE} Il kernel non ha memoria sufficiente a creare una
+ \item \macro{ENFILE} Il kernel non ha memoria sufficiente a creare una
nuova struttura per il socket.
- \item \texttt{EMFILE} Si è ecceduta la tabella dei file.
- \item \texttt{EACCES} Non si hanno privilegi per creare un socket nel
+ \item \macro{EMFILE} Si è ecceduta la tabella dei file.
+ \item \macro{EACCES} Non si hanno privilegi per creare un socket nel
dominio o con il protocollo specificato.
- \item \texttt{EINVAL} Protocollo sconosciuto o dominio non disponibile.
- \item \texttt{ENOBUFS} o \texttt{ENOMEM} Non c'è sufficiente memoria per
+ \item \macro{EINVAL} Protocollo sconosciuto o dominio non disponibile.
+ \item \macro{ENOBUFS} o \macro{ENOMEM} Non c'è sufficiente memoria per
creare il socket.
- \end{itemize}
-\end{itemize}
+ \end{errlist}
+\end{prototype}
Si noti che la creazione del socket non comporta nulla riguardo
all'indicazione degli indirizzi remoti o locali attraverso i quali si vuole
Dati i tanti e diversi protocolli di comunicazione disponibili, esistono vari
tipi di socket, che vengono classificati raggruppandoli in quelli che si
-chiamano \textsl{domini} (\textit{domains}). La scelta di un dominio equivale
-in sostanza alla scelta di una famiglia di protocolli. Ciascun dominio ha un
-suo nome simbolico che convenzionalmente inizia con \texttt{PF\_} (da
-\textit{Protocol Family}, altro nome con cui si indicano i domini).
+chiamano \textsl{domini}. La scelta di un dominio equivale in sostanza alla
+scelta di una famiglia di protocolli. Ciascun dominio ha un suo nome simbolico
+che convenzionalmente inizia con \texttt{PF\_} da \textit{protocol family},
+altro nome con cui si indicano i domini.
A ciascun tipo di dominio corrisponde un analogo nome simbolico che inizia per
-\texttt{AF\_} da \textit{Address Family}, e che identifica il formato degli
-indirizzi usati in quel dominio; le man pages di linux si riferiscono a questi
+\texttt{AF\_} da \textit{address family}, e che identifica il formato degli
+indirizzi usati in quel dominio; le man pages di Linux si riferiscono a questi
anche come \textit{name space}, (nome che però il manuale della glibc riserva
ai domini) e che identifica il formato degli indirizzi usati in quel dominio.
nomi sono equivalenti e corrispondono agli stessi valori.
I domini (e i relativi nomi simbolici), così come i nomi delle famiglie di
-indirizzi sono definiti dall'header \textit{socket.h}. In linux le famiglie di
+indirizzi sono definiti dall'header \textit{socket.h}. In Linux le famiglie di
protocolli disponibili sono riportate in \ntab.
\begin{table}[htb]
\footnotesize
\centering
\begin{tabular}[c]{lll}
- Nome & Utilizzo & Man page \\
+ \hline
+ \textsl{Nome} & \textsl{Utilizzo} &\textsl{Man page} \\
+ \hline
+ \hline
PF\_UNIX,PF\_LOCAL & Local communication & unix(7) \\
PF\_INET & IPv4 Internet protocols & ip(7) \\
PF\_INET6 & IPv6 Internet protocols & \\
PF\_ATMPVC & Access to raw ATM PVCs & \\
PF\_APPLETALK & Appletalk & ddp(7) \\
PF\_PACKET & Low level packet interface & packet(7) \\
+ \hline
\end{tabular}
- \caption{Famiglie di protocolli definiti in linux}
+ \caption{Famiglie di protocolli definiti in Linux}
\label{tab:net_pf_names}
\end{table}
Non tutte le famiglie di protocolli sono accessibili dall'utente generico, ad
-esempio in generale tutti i socket di tipo \texttt{SOCK\_RAW} possono essere
-creati solo da processi che hanno i provilegi di root (cioè effective uid
-uguale a zero) o la capability \texttt{CAP\_NET\_RAW}.
+esempio in generale tutti i socket di tipo \macro{SOCK\_RAW} possono essere
+creati solo da processi che hanno i privilegi di root (cioè effective uid
+uguale a zero) o la capability \macro{CAP\_NET\_RAW}.
\subsection{Il tipo, o stile}
La scelta di un dominio non comporta però la scelta dello stile di
comunicazione, questo infatti viene a dipendere dal protocollo che si andrà ad
utilizzare fra quelli disponibili nella famiglia scelta. Le API permettono di
-scegliere lo stile di comunicazione indicando il tipo di socket; linux e le
+scegliere lo stile di comunicazione indicando il tipo di socket; Linux e le
glibc mettono a disposizione i seguenti tipi di socket (che il manuale della
-glibc chiama \textit{styles}) definiti come \texttt{int} in \texttt{socket.h}:
+glibc chiama \textit{styles}) definiti come \type{int} in \file{socket.h}:
\begin{list}{}{}
-\item \texttt{SOCK\_STREAM} Provvede un canale di trasmissione dati
+\item \macro{SOCK\_STREAM} Provvede un canale di trasmissione dati
bidirezionale, sequenziale e affidabile. Opera su una connessione con un
altro socket. I dati vengono ricevuti e trasmessi come un flusso continuo di
byte (da cui il nome \textit{stream}).
-\item \texttt{SOCK\_DGRAM} Viene usato per mandare pacchetti di lunghezza
+\item \macro{SOCK\_DGRAM} Viene usato per mandare pacchetti di lunghezza
massima fissata (\textit{datagram}) indirizzati singolarmente, senza
connessione e in maniera non affidabile. È l'opposto del precedente.
-\item \texttt{SOCK\_SEQPACKET} Provvede un canale di trasmissione di dati
+\item \macro{SOCK\_SEQPACKET} Provvede un canale di trasmissione di dati
bidirezionale, sequenziale e affidabile. Opera su una connessione con un
altro socket. I dati possono solo essere trasmessi e letti per pacchetti (di
dimensione massima fissata).
-\item \texttt{SOCK\_RAW} Provvede l'accesso a basso livello ai protocolli di
+\item \macro{SOCK\_RAW} Provvede l'accesso a basso livello ai protocolli di
rete e alle varie interfacce. I normali programmi di comunicazione non
devono usarlo.
-\item \texttt{SOCK\_RDM} Provvede un canale di trasmissione di pacchetti
+\item \macro{SOCK\_RDM} Provvede un canale di trasmissione di pacchetti
affidabile ma in cui non è garantito l'ordine di arrivo dei pacchetti.
-\item \texttt{SOCK\_PACKET} Obsoleto, non deve essere usato.
+\item \macro{SOCK\_PACKET} Obsoleto, non deve essere usato.
\end{list}
Si tenga presente che non tutte le combinazioni di famiglia di protocolli e
\footnotesize
\centering
\begin{tabular}{l|c|c|c|c|c|}
- \multicolumn{1}{c}{} &\multicolumn{1}{c}{\texttt{SOCK\_STREAM}}&
- \multicolumn{1}{c}{\texttt{SOCK\_DGRAM}} &
- \multicolumn{1}{c}{\texttt{SOCK\_RAW}} &
- \multicolumn{1}{c}{\texttt{SOCK\_PACKET}}&
- \multicolumn{1}{c}{\texttt{SOCK\_SEQPACKET}} \\
+ \multicolumn{1}{c}{} &\multicolumn{1}{c}{\macro{SOCK\_STREAM}}&
+ \multicolumn{1}{c}{\macro{SOCK\_DGRAM}} &
+ \multicolumn{1}{c}{\macro{SOCK\_RAW}} &
+ \multicolumn{1}{c}{\macro{SOCK\_PACKET}}&
+ \multicolumn{1}{c}{\macro{SOCK\_SEQPACKET}} \\
\cline{2-6}
- \texttt{PF\_UNIX} & si & si & & & \\
+ \macro{PF\_UNIX} & si & si & & & \\
\cline{2-6}
- \texttt{PF\_INET} & TCP & UDP & IPv4 & & \\
+ \macro{PF\_INET} & TCP & UDP & IPv4 & & \\
\cline{2-6}
- \texttt{PF\_INET6} & TCP & UDP & IPv6 & & \\
+ \macro{PF\_INET6} & TCP & UDP & IPv6 & & \\
\cline{2-6}
- \texttt{PF\_IPX} & & & & & \\
+ \macro{PF\_IPX} & & & & & \\
\cline{2-6}
- \texttt{PF\_NETLINK} & & si & si & & \\
+ \macro{PF\_NETLINK} & & si & si & & \\
\cline{2-6}
- \texttt{PF\_X25} & & & & & si \\
+ \macro{PF\_X25} & & & & & si \\
\cline{2-6}
- \texttt{PF\_AX25} & & & & & \\
+ \macro{PF\_AX25} & & & & & \\
\cline{2-6}
- \texttt{PF\_ATMPVC} & & & & & \\
+ \macro{PF\_ATMPVC} & & & & & \\
\cline{2-6}
- \texttt{PF\_APPLETALK} & & si & si & & \\
+ \macro{PF\_APPLETALK} & & si & si & & \\
\cline{2-6}
- \texttt{PF\_PACKET} & & si & si & & \\
+ \macro{PF\_PACKET} & & si & si & & \\
\cline{2-6}
\end{tabular}
- \caption{Combinazioni valide di dominio e tipo di protocollo per la funzione \texttt{socket}.}
+ \caption{Combinazioni valide di dominio e tipo di protocollo per la
+ funzione \func{socket}.}
\label{tab:sock_sock_valid_combinations}
\end{table}
parola \textsl{si} qualora non il protocollo non abbia un nome definito,
mentre si sono lasciate vuote le caselle per le combinazioni non supportate.
+
+
\section{Le strutture degli indirizzi dei socket}
\label{sec:sock_sockaddr}
Ogni famiglia di protocolli ha ovviamente una sua forma di indirizzamento e in
corrispondenza a questa una sua peculiare struttura degli indirizzi; i nomi di
-tutte queste strutture iniziano per \texttt{sockaddr\_}, quelli propri di
+tutte queste strutture iniziano per \var{sockaddr\_}, quelli propri di
ciascuna famiglia vengono identificati dal suffisso finale, aggiunto al nome
precedente.
+
\subsection{La struttura generica}
\label{sec:sock_sa_gen}
maneggiare puntatori a strutture relative a tutti gli indirizzi possibili
nelle varie famiglie di protocolli; questo pone il problema di come passare
questi puntatori, il C ANSI risolve questo problema coi i puntatori generici
-(i \texttt{void *}), ma l'interfaccia dei socket è antecendente alla
+(i \type{void *}), ma l'interfaccia dei socket è antecendente alla
definizione dello standard ANSI, e per questo nel 1982 fu scelto di definire
-una struttura generica \texttt{sockaddr} per gli indirizzi dei socket mostrata
+una struttura generica \type{sockaddr} per gli indirizzi dei socket mostrata
in \nfig:
-\begin{figure}[!htbp]
+\begin{figure}[!htb]
\footnotesize
- \begin{lstlisting}{}
+ \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
struct sockaddr {
sa_family_t sa_family; /* address family: AF_xxx */
char sa_data[14]; /* address (protocol-specific) */
};
\end{lstlisting}
- \caption{La struttura generica degli indirizzi dei socket \texttt{sockaddr}}
+ \caption{La struttura generica degli indirizzi dei socket \type{sockaddr}}
\label{fig:sock_sa_gen_struct}
\end{figure}
occorrerà eseguire un casting del relativo puntatore.
I tipi di dati che compongono la struttura sono stabiliti dallo standard
-Posix.1g, riassunti in \ntab\ con i rispettivi file di include in cui sono
+POSIX.1g, riassunti in \ntab\ con i rispettivi file di include in cui sono
definiti; la struttura è invece definita nell'include file
-\texttt{sys/socket.h}
+\file{sys/socket.h}
-\begin{table}[!htbp]
+\begin{table}[!htb]
\centering
\begin{tabular}{|l|l|l|}
\hline
\hline
\end{tabular}
\caption{Tipi di dati usati nelle strutture degli indirizzi, secondo quanto
- stabilito dallo standard Posix.1g}
+ stabilito dallo standard POSIX.1g}
\label{tab:sock_data_types}
\end{table}
-In alcuni sistemi (per BSD a partire da 4.3BSD-reno) la struttura è
-leggermente diversa e prevede un primo membro aggiuntivo \texttt{uint8\_t
- sin\_len} (come riportato da R. Stevens nei suoi libri). Questo campo non
-verrebbe usato direttamente dal programmatore e non è richiesto dallo standard
-Posix.1g, in Linux pertanto non sussiste. Il campo \texttt{sa\_family\_t} era
-storicamente un \texttt{unsigned short}.
+In alcuni sistemi la struttura è leggermente diversa e prevede un primo membro
+aggiuntivo \var{uint8\_t sin\_len} (come riportato da R. Stevens nei suoi
+libri). Questo campo non verrebbe usato direttamente dal programmatore e non è
+richiesto dallo standard POSIX.1g, in Linux pertanto non esiste. Il campo
+\type{sa\_family\_t} era storicamente un \type{unsigned short}.
Dal punto di vista del programmatore l'unico uso di questa struttura è quello
di fare da riferimento per il casting, per il kernel le cose sono un po'
diverse, in quanto esso usa il puntatore per recuperare il campo
-\texttt{sa\_family} con cui determinare il tipo di indirizzo; per questo
-motivo, anche se l'uso di un puntatore \texttt{void *} sarebbe più immediato
+\var{sa\_family} con cui determinare il tipo di indirizzo; per questo
+motivo, anche se l'uso di un puntatore \type{void *} sarebbe più immediato
per l'utente (che non dovrebbe più eseguire il casting), è stato mantenuto
l'uso di questa struttura.
\subsection{La struttura degli indirizzi IPv4}
\label{sec:sock_sa_ipv4}
-I socket di tipo \texttt{PF\_INET} vengono usati per la comunicazione
+I socket di tipo \macro{PF\_INET} vengono usati per la comunicazione
attraverso internet; la struttura per gli indirizzi per un socket internet
-(IPv4) è definita come \texttt{sockaddr\_in} nell'header file
-\texttt{netinet/in.h} e secondo le man page ha la forma mostrata in \nfig,
-conforme allo standard Posix.1g.
-
+(IPv4) è definita come \type{sockaddr\_in} nell'header file
+\file{netinet/in.h} e secondo le man page ha la forma mostrata in \nfig,
+conforme allo standard POSIX.1g.
-\begin{figure}[!htbp]
+\begin{figure}[!htb]
\footnotesize
- \begin{lstlisting}{}
+ \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
u_int16_t sin_port; /* port in network byte order */
};
\end{lstlisting}
\caption{La struttura degli indirizzi dei socket internet (IPv4)
- \texttt{sockaddr\_in}.}
+ \type{sockaddr\_in}.}
\label{fig:sock_sa_ipv4_struct}
\end{figure}
RAW che accedono direttamente al livello di IP, nel qual caso il numero della
porta viene settato al numero di protocollo.
-Il membro \texttt{sin\_family} deve essere sempre settato; \texttt{sin\_port}
-specifica il numero di porta; i numeri di porta sotto il 1024 sono chiamati
-\textsl{riservati} in quanto utilizzati da servizi standard. Soltanto processi
-con i privilegi di root (effective uid uguale a zero) o con la capability
-\texttt{CAP\_NET\_BIND\_SERVICE} possono usare la funzione \texttt{bind} su
-queste porte.
+Il membro \var{sin\_family} deve essere sempre settato; \var{sin\_port}
+specifica il numero di porta (vedi \secref{sec:TCPel_port_num}; i numeri di
+porta sotto il 1024 sono chiamati \textsl{riservati} in quanto utilizzati da
+servizi standard. Soltanto processi con i privilegi di root (effective uid
+uguale a zero) o con la capability \macro{CAP\_NET\_BIND\_SERVICE} possono
+usare la funzione \func{bind} su queste porte.
-Il membro \texttt{sin\_addr} contiene l'indirizzo internet dell'altro capo
+Il membro \var{sin\_addr} contiene l'indirizzo internet dell'altro capo
della comunicazione, e viene acceduto sia come struttura (un resto di una
-implementazione precedente in cui questa era una union usata per accedere alle
-diverse classi di indirizzi) che come intero.
+implementazione precedente in cui questa era una \texttt{union} usata per
+accedere alle diverse classi di indirizzi) che come intero.
Infine è da sottolineare che sia gli indirizzi che i numeri di porta devono
essere specificati in quello che viene chiamato \textit{network order}, cioè
con i bit ordinati in formato \textit{big endian}, questo comporta la
necessità di usare apposite funzioni di conversione per mantenere la
-portabilità del codice (vedi \ref{sec:sock_addr_func} per i dettagli del
+portabilità del codice (vedi \secref{sec:sock_addr_func} per i dettagli del
problema e le relative soluzioni).
+
\subsection{La struttura degli indirizzi IPv6}
\label{sec:sock_sa_ipv6}
-Essendo IPv6 una estenzione di IPv4 i socket di tipo \texttt{PF\_INET6} sono
+Essendo IPv6 una estensione di IPv4 i socket di tipo \macro{PF\_INET6} sono
sostanzialmente identici ai precedenti; la parte in cui si trovano
praticamente tutte le differenze è quella della struttura degli indirizzi. La
-struttura degli indirizzi è definita ancora in \texttt{netinet/in.h}.
+struttura degli indirizzi è definita ancora in \file{netinet/in.h}.
-\begin{figure}[!htbp]
+\begin{figure}[!htb]
\footnotesize
- \begin{lstlisting}{}
+ \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
struct sockaddr_in6 {
u_int16_t sin6_family; /* AF_INET6 */
u_int16_t sin6_port; /* port number */
};
\end{lstlisting}
\caption{La struttura degli indirizzi dei socket IPv6
- \texttt{sockaddr\_in6}.}
+ \type{sockaddr\_in6}.}
\label{fig:sock_sa_ipv6_struct}
\end{figure}
-Il campo \texttt{sin6\_family} deve essere sempre settato ad
-\texttt{AF\_INET6}, il campo \texttt{sin6\_port} è analogo a quello di IPv4 e
-segue le stesse regole; il campo \texttt{sin6\_flowinfo} è a dua volta diviso
+Il campo \var{sin6\_family} deve essere sempre settato ad
+\macro{AF\_INET6}, il campo \var{sin6\_port} è analogo a quello di IPv4 e
+segue le stesse regole; il campo \var{sin6\_flowinfo} è a sua volta diviso
in tre parti di cui i 24 bit inferiori indicano l'etichetta di flusso, i
successivi 4 bit la priorità e gli ultimi 4 sono riservati; questi valori
fanno riferimento ad alcuni campi specifici dell'header dei pacchetti IPv6
-(vedi \ref{sec:appA_ipv6}) ed il loro uso è sperimentale.
+(vedi \secref{sec:IP_ipv6head}) ed il loro uso è sperimentale.
-Il campo \texttt{sin6\_addr} contiene l'indirizzo a 128 bit usato da IPv6,
-infine il campo \texttt{sin6\_scope\_id} è un campo introdotto con il kernel
+Il campo \var{sin6\_addr} contiene l'indirizzo a 128 bit usato da IPv6,
+infine il campo \var{sin6\_scope\_id} è un campo introdotto con il kernel
2.4 per gestire alcune operazioni riguardanti il multicasting.
-Si noti che questa struttura è più grande di una \texttt{sockaddr} generica,
+Si noti che questa struttura è più grande di una \var{sockaddr} generica,
quindi occorre stare attenti a non avere fatto assunzioni riguardo alla
possibilità di contenere i dati nelle dimensioni di quest'ultima.
-
+
\subsection{La struttura degli indirizzi locali}
\label{sec:sock_sa_local}
-I socket di tipo \texttt{PF\_UNIX} vengono usati per una comunicazione
+I socket di tipo \macro{PF\_UNIX} vengono usati per una comunicazione
efficiente fra processi che stanno sulla stessa macchina; essi rispetto ai
precedenti possono essere anche creati in maniera anonima attraverso la
-funzione \texttt{socketpair}. Quando però si vuole fare riferiemento ad uno di
-questi socket si deve usare la seguente struttura di indirizzi definita nel
-file di header \texttt{sys/un.h}.
+funzione \func{socketpair}. Quando però si vuole fare riferimento esplicito
+ad uno di questi socket si deve usare la seguente struttura di indirizzi
+definita nel file di header \file{sys/un.h}.
-\begin{figure}[!htbp]
+\begin{figure}[!htb]
\footnotesize
- \begin{lstlisting}{}
+ \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
#define UNIX_PATH_MAX 108
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
};
\end{lstlisting}
\caption{La struttura degli indirizzi dei socket locali
- \texttt{sockaddr\_un}.}
+ \var{sockaddr\_un}.}
\label{fig:sock_sa_local_struct}
\end{figure}
-In questo caso il campo \texttt{sun\_family} deve essere \texttt{AF\_UNIX},
-mentre il campo \texttt{sun\_path} deve specificare un indirizzo; questo ha
+In questo caso il campo \var{sun\_family} deve essere \macro{AF\_UNIX},
+mentre il campo \var{sun\_path} deve specificare un indirizzo; questo ha
due forme un file (di tipo socket) nel filesystem o una stringa univoca
(tenuta in uno spazio di nomi astratto). Nel primo caso l'indirizzo viene
specificato come una stringa (terminata da uno zero) corrispondente al
-pathname del file; nel secondo qinvece \texttt{sun\_path} inizia con uno zero
-vegono usati i restanti bytes come stringa (senza terminazione).
+pathname del file; nel secondo invece \var{sun\_path} inizia con uno zero
+vengono usati i restanti bytes come stringa (senza terminazione).
-\subsection{Il passaggio delle strutture}
-\label{sec:sock_addr_pass}
+% \subsection{Il passaggio delle strutture}
+% \label{sec:sock_addr_pass}
-Come detto nelle funzioni della API dei socket le strutture degli indirizzi
-vengono sempre passate per riferimento usando un puntatore; anche la lunghezza
-della struttura è passata come argomento, ma in questo caso la modalità del
-passaggio dipende dalla direzione del medesimo, dal processo al kernel o
-viceversa.
+% Come detto nelle funzioni della API dei socket le strutture degli indirizzi
+% vengono sempre passate per riferimento usando un puntatore; anche la lunghezza
+% della struttura è passata come argomento, ma in questo caso la modalità del
+% passaggio dipende dalla direzione del medesimo, dal processo al kernel o
+% viceversa.
% In particolare le tre funzioni \texttt{bind}, \texttt{connect} e
% \texttt{sendto} passano la struttura al kernel, in questo caso è passata
% \texttt{getpeername} invece ricevono i valori del kernel
+
\section{Le funzioni di conversione degli indirizzi}
\label{sec:sock_addr_func}
-Come accennato gli indirizzi internet e i numeri di porta usati nella rete
-devono essere forniti in formato big endian. In genere la rappresentazione di
-un numbero binario in un computer può essere fatta in due modi, chiamati
-rispettivamente \textit{big endian} e \textit{little endian} a seconda di come
-i singoli bit vengono aggregati per formare le variabili intere (in diretta
-corrispondenza a come sono poi in realtà cablati sui bus interni del
-computer).
+In questa sezione tratteremo delle varie funzioni usate per manipolare gli
+indirizzi, limitandoci però agli indirizzi internet.
+
+Come accennato gli indirizzi e i numeri di porta usati nella rete devono
+essere forniti in formato opportuno (il \textit{network order}). Per capire
+cosa significa tutto ciò occorre introdurre un concetto generale che tornerà
+utile anche in seguito.
+
+
+\subsection{La \textit{endianess}}
+\label{sec:sock_endianess}
+
+La rappresentazione di un numero binario in un computer può essere fatta in
+due modi, chiamati rispettivamente \textit{big endian} e \textit{little
+ endian} a seconda di come i singoli bit vengono aggregati per formare le
+variabili intere (in diretta corrispondenza a come sono poi in realtà cablati
+sui bus interni del computer).
Per capire meglio il problema si consideri un intero a 16 bit scritto in una
locazione di memoria posta ad un certo indirizzo. I singoli bit possono essere
disposti un memoria in due modi, a partire dal più significativo o a partire
dal meno significativo. Così nel primo caso si troverà il byte che contiene i
bit più significativi all'indirizzo menzionato e il byte con i bit meno
-significativi nell'indirizzo successivo; questo ordinamento è detto little
-endian dato che il dato finale è la parte ``piccola'' del numero. Il caso
-opposto, in cui si parte dal bit meno significativo è detto per lo stesso
-motivo big endian.
+significativi nell'indirizzo successivo; questo ordinamento è detto
+\textit{little endian} dato che il dato finale è la parte ``piccola'' del
+numero. Il caso opposto, in cui si parte dal bit meno significativo è detto
+per lo stesso motivo \textit{big endian}.
La \textit{endianess} di un computer dipende essenzialmente dalla architettura
-hardware usata; intel e digital usano il little endian, motorola, ibm, sun
-(sostanzialmente tutti gli altri) usano il big endian. Il formato della rete è
-anch'esso big endian. Esistono poi anche dei processori che possono scegliere
-il tipo di formato all'avvio e alcuni, come il PowerPC o l'intel i860, possono
-pure passare da un tipo all'altro con una specifica istruzione; in ogni caso
-in linux l'ordinamanento è definito dall'archiettura e anche se questi
-cambiamenti sono possibili anche dopo che il sistema è avviato, non vengono
-mai eseguiti.
+hardware usata; Intel e Digital usano il \textit{little endian}, Motorola,
+IBM, Sun (sostanzialmente tutti gli altri) usano il \textit{big endian}. Il
+formato della rete è anch'esso \textit{big endian}, altri esempi sono quello
+del bus PC, che è \textit{little endian}, o quello del bus VME che è
+\textit{big endian}.
+
+Esistono poi anche dei processori che possono scegliere il tipo di formato
+all'avvio e alcuni che, come il PowerPC o l'Intel i860, possono pure passare
+da un tipo di ordinamento all'altro con una specifica istruzione. In ogni caso
+in Linux l'ordinamento è definito dall'architettura e dopo l'avvio del sistema
+resta sempre lo stesso, anche quando il processore permetterebbe di eseguire
+questi cambiamenti.
+
+\subsection{Le funzioni per il riordinamento}
+\label{sec:sock_func_ord}
Il problema connesso all'endianess è che quando si passano dei dati da un tipo
-di architettura all'altra; in questo caso infatti nel passaggio i dati vengono
-interpretati in maniera diversa, e nel caso dell'esempio dell'intero a 16 bit
-ci si ritroverà con i due bytes componenti scambiati di posto, mentre in
-generale ne sarà invertito l'ordine di lettura e andranno perciò rovesciati.
-
-Per questo motivo si usano le seguenti funzioni di conversione (i cui
-prototipi sono definiti in \texttt{netinet/in.h}) che servono a tener conto
-automaticamente della possibile differenza fra l'ordinamento usato sul
-computer e quello che viene usato nelle trasmissione sulla rete:
-\begin{itemize}
-\item \texttt{unsigned long int htonl(unsigned long int hostlong)}
-
- Converte l'intero a 32 bit \texttt{hostlong} dal formato della macchina a
+di architettura all'altra i dati vengono interpretati in maniera diversa, e ad
+esempio nel caso dell'intero a 16 bit ci si ritroverà con i due bytes in cui è
+suddiviso scambiati di posto, e ne sarà quindi invertito l'ordine di lettura
+per cui, per riavere il valore originale dovranno essere rovesciati.
+
+Per questo motivo si usano le seguenti funzioni di conversione che servono a
+tener conto automaticamente della possibile differenza fra l'ordinamento usato
+sul computer e quello che viene usato nelle trasmissione sulla rete; queste
+funzioni sono:
+\begin{prototype}{netinet/in.h}
+{unsigned long int htonl(unsigned long int hostlong)}
+ Converte l'intero a 32 bit \var{hostlong} dal formato della macchina a
quello della rete.
-
-\item \texttt{unsigned sort int htons(unsigned short int hostshort)}
-
- Converte l'intero a 16 bit \texttt{hostshort} dal formato della macchina a
+\end{prototype}
+\begin{prototype}{netinet/in.h}
+{unsigned short int htons(unsigned short int hostshort)}
+ Converte l'intero a 16 bit \var{hostshort} dal formato della macchina a
quello della rete.
-
-\item \texttt{unsigned long int ntonl(unsigned long int netlong)}
-
- Converte l'intero a 32 bit \texttt{netlong} dal formato della rete a quello
+\end{prototype}
+\begin{prototype}{netinet/in.h}
+{unsigned long int ntonl(unsigned long int netlong)}
+ Converte l'intero a 32 bit \var{netlong} dal formato della rete a quello
della macchina.
-
-\item \texttt{unsigned sort int ntons(unsigned short int netshort)}
-
- Converte l'intero a 16 bit \texttt{netshort} dal formato della rete a quello
+\end{prototype}
+\begin{prototype}{netinet/in.h}
+{unsigned sort int ntons(unsigned short int netshort)}
+ Converte l'intero a 16 bit \var{netshort} dal formato della rete a quello
della macchina.
-\end{itemize}
-I nomi sono assegnati usando la lettera $n$ come mnemonico per indicare
-l'ordinamento usato sulla rete (da \textit{network order}) e la lettera $h$
-come mnemonico per l'ordinamento usato sulla macchina locale (da \textit{host
- order}), mentre le lettere $s$ e $l$ stanno ad indicare i tipi di dato
-(\texttt{long} o \texttt{short}, riportati anche dai prototipi).
+\end{prototype}
+I nomi sono assegnati usando la lettera \func{n} come mnemonico per indicare
+l'ordinamento usato sulla rete (da \textit{network order}) e la lettera
+\func{h} come mnemonico per l'ordinamento usato sulla macchina locale (da
+\textit{host order}), mentre le lettere \func{s} e \func{l} stanno ad indicare
+i tipi di dato (\type{long} o \type{short}, riportati anche dai prototipi).
+
+Usando queste funzioni si ha la conversione automatica: nel caso in cui la
+macchina che si sta usando abbia una architettura \textit{big endian} queste
+funzioni sono definite come macro che non fanno nulla. Per questo motivo vanno
+sempre utilizzate, anche quando potrebbero non essere necessarie, in modo da
+assicurare la portabilità del codice su tutte le architetture.
-Usando queste funzioni si ha la conversione automatica (nel caso pure la
-macchina sia in big endian queste funzioni sono definite come macro che non
-fanno nulla); esse vanno sempre utilizzate per assicurare la portabilità del
-codice su tutte le architetture.
+\subsection{Le funzioni \func{inet\_aton}, \func{inet\_addr} e
+ \func{inet\_ntoa}}
+\label{sec:sock_func_ipv4}
-Un secondo insieme di funzioni di manipolazione (i cui prototipi sono definiti
-in \texttt{arpa/inet.h}) serve per passare dal formato binario usato nelle
-strutture degli indirizzi alla rappresentazione dei numeri IP che si usa
-normalente.
+Un secondo insieme di funzioni di manipolazione serve per passare dal formato
+binario usato nelle strutture degli indirizzi alla rappresentazione dei numeri
+IP che si usa normalmente.
Le prime tre funzioni di manipolazione riguardano la conversione degli
indirizzi IPv4 da una stringa in cui il numero di IP è espresso secondo la
cosiddetta notazione \textit{dotted-decimal}, (cioè nella forma
\texttt{192.160.0.1}) al formato binario (direttamente in \textit{network
- order}) e viceversa; in questo caso si usa la lettera $a$ come mnemonico per
-indicare la stringa. Dette funzioni sono:
-\begin{itemize}
-\item \texttt{int inet\_aton(const char *src, struct in\_addr *dest)}
-
- Converte la stringa puntata da \texttt{src} nell'indirizzo binario da
- memorizzare all'indirizzo puntato da \texttt{dest}, restituendo 0 in caso
- di successo e 1 in caso di fallimento (è espressa in questa forma in modo da
+ order}) e viceversa; in questo caso si usa la lettera \func{a} come
+mnemonico per indicare la stringa. Dette funzioni sono:
+\begin{prototype}{arpa/inet.h}
+ {int inet\_aton(const char *src, struct in\_addr *dest)}
+ Converte la stringa puntata da \var{src} nell'indirizzo binario da
+ memorizzare all'indirizzo puntato da \var{dest}, restituendo 0 in caso di
+ successo e 1 in caso di fallimento (è espressa in questa forma in modo da
poterla usare direttamente con il puntatore usato per passare la struttura
- degli indirizzi). Se usata con \texttt{dest} inizializzato a
- \texttt{NULL} effettua la validazione dell'indirizzo.
-
-\item \texttt{in\_addr\_t inet\_addr(const char *strptr)}
-
+ degli indirizzi). Se usata con \var{dest} inizializzato a \macro{NULL}
+ effettua la validazione dell'indirizzo.
+\end{prototype}
+\begin{prototype}{arpa/inet.h}{in\_addr\_t inet\_addr(const char *strptr)}
Restituisce l'indirizzo a 32 bit in network order a partire dalla stringa
passata come parametro, in caso di errore restituisce il valore
- \texttt{INADDR\_NONE} che tipicamente sono trentadue bit a uno; questo
+ \macro{INADDR\_NONE} che tipicamente sono trentadue bit a uno; questo
comporta che la stringa \texttt{255.255.255.255}, che pure è un indirizzo
valido, non può essere usata con questa funzione; per questo motivo essa è
generalmente deprecata in favore della precedente.
-
-\item \texttt{char *inet\_ntop(struct in\_addr addrptr)}
-
- Converte il valore a 32 bit dell'indirizzo (espresso in network order)
- restituendo il puntatore alla stringa che contiene l'espressione in formato
- dotted decimal. Si deve tenere presente che la stringa risiede in memoria
- statica, per cui questa funzione non è rientrante.
-\end{itemize}
-
-Le tre funzioni precedenti sono però limitate solo ad IPv4, per questo motivo
-è preferibile usare le due nuove funzioni \texttt{inet\_pton} e
-\texttt{inet\_ntop} che possono convertire anche gli indirizzi IPv6 (secondo
-lo schema in \nfig). Anche in questo caso le lettere $n$ e $p$ sono gli
-mnemonici per ricordare il tipo di conversione effettuata e stanno per
-\textit{presentation} e \textit{numeric}.
+\end{prototype}
+\begin{prototype}{arpa/inet.h}{char *inet\_ntoa(struct in\_addr addrptr)}
+ Converte il valore a 32 bit dell'indirizzo (espresso in \textit{network
+ order}) restituendo il puntatore alla stringa che contiene l'espressione
+ in formato dotted decimal. Si deve tenere presente che la stringa risiede in
+ memoria statica, per cui questa funzione non è rientrante.
+\end{prototype}
+
+
+\subsection{Le funzioni \func{inet\_pton} e \func{inet\_ntop}}
+\label{sec:sock_conv_func_gen}
+
+Le tre funzioni precedenti sono limitate solo ad indirizzi IPv4, per questo
+motivo è preferibile usare le due nuove funzioni \func{inet\_pton} e
+\func{inet\_ntop} che possono convertire anche gli indirizzi IPv6. Anche in
+questo caso le lettere \func{n} e \func{p} sono degli mnemonici per ricordare
+il tipo di conversione effettuata e stanno per \textit{presentation} e
+\textit{numeric}.
+
+% \begin{figure}[htb]
+% \centering
+
+% \caption{Schema della rappresentazioni utilizzate dalle funzioni di
+% conversione \texttt{inet\_pton} e \texttt{inet\_ntop} }
+% \label{fig:sock_inet_conv_func}
+
+% \end{figure}
+
+Entrambe le funzioni accettano l'argomento \texttt{af} che indica il tipo di
+indirizzo e può essere \texttt{AF\_INET} o \texttt{AF\_INET6}. Se la famiglia
+indicata non è valida entrambe le funzioni settano la variabile \texttt{errno}
+al valore \texttt{EAFNOSUPPORT}. I prototipi delle suddette funzioni sono i
+seguenti:
+\begin{prototype}{sys/socket.h}
+ {int inet\_pton(int af, const char *src, void *addr\_ptr)} Converte la
+ stringa puntata da \var{src} nell'indirizzo IP da memorizzare
+ all'indirizzo puntato da \var{addr\_ptr}, la funzione restituisce un
+ valore positivo in caso di successo, e zero se la stringa non rappresenta un
+ indirizzo valido, e negativo se \var{af} specifica una famiglia di indirizzi
+ non valida.
+\end{prototype}
+
+\begin{prototype}{sys/socket.h}
+ {char *inet\_ntop(int af, const void *addr\_ptr, char *dest, size\_t len)}
+ Converte la struttura dell'indirizzo puntata da \var{addr\_ptr} in una
+ stringa che viene copiata nel buffer puntato dall'indirizzo \var{dest};
+ questo deve essere preallocato dall'utente e la lunghezza deve essere almeno
+ \macro{INET\_ADDRSTRLEN} in caso di indirizzi IPv4 e
+ \macro{INET6\_ADDRSTRLEN} per indirizzi IPv6; la lunghezza del buffer deve
+ comunque venire specificata attraverso il parametro \var{len}.
+
+ La funzione restituisce un puntatore non nullo a \var{dest} in caso di
+ successo e un puntatore nullo in caso di fallimento, in quest'ultimo caso
+ viene settata la variabile \var{errno} con il valore \macro{ENOSPC} in
+ caso le dimensioni dell'indirizzo eccedano la lunghezza specificata da
+ \var{len} o \macro{ENOAFSUPPORT} in caso \var{af} non sia una famiglia di
+ indirizzi valida.
+\end{prototype}
-\begin{figure}[htb]
- \centering
+Gli indirizzi vengono cnovertiti da/alle rispettive strutture di indirizzo
+(\var{struct in\_addr} per IPv4, e \var{struct in6\_addr} per IPv6), che
+devono essere precedentemente allocate e passate attraverso il puntatore
+\var{addr\_ptr}; il parametro \var{dest} di \func{inet\_ntop} non può essere
+nullo e deve essere allocato precedentemente.
- \caption{Schema della rappresentazioni utilizzate dalle funzioni di
- conversione \texttt{inet\_pton} e \texttt{inet\_ntop} }
- \label{fig:sock_inet_conv_func}
+Il formato usato per gli indirizzi in formato di presentazione è la notazione
+\textit{dotted decimal} per IPv4 e quella descritta in
+\secref{sec:IP_ipv6_notation} per IPv6.
-\end{figure}
-Entrambe le funzioni accettano l'argomento \texttt{family} che indica il tipo
-di indirizzo e può essere \texttt{AF\_INET} o \texttt{AF\_INET6}. Se la
-famiglia indicata non è valida entrambe le funzioni ritornano un valore
-negativo e settano la variabile \texttt{errno} al valore
-\texttt{EAFNOSUPPORT}. I prototipi delle suddette funzioni sono i seguenti:
-\begin{itemize}
-\item \texttt{int inet\_pton(int family, const char *src, void *dest)}
-
- Converte la stringa puntata da \texttt{src} nell'indirizzo binario da
- memorizzare all'indirizzo puntato da \texttt{dest}, restituendo 0 in caso di
- successo e 1 in caso di fallimento.
-
-\item \texttt{char *inet\_ntop(int family, const void *src, char *dest,
- size\_t len)}
-
- Converte la struttura dell'indirizzo puntata da \texttt{src} in una stringa
- che viene copiata nel buffer puntato dall'indirizzo \texttt{dest}; questo
- deve essere preallocato dall'utente e la lunghezza deve essere almeno
- \texttt{INET\_ADDRSTRLEN} in caso di indirizzi IPv4 e
- \texttt{INET6\_ADDRSTRLEN} per indirizzi IPv6; la lunghezza del buffer deve
- comunque venire specificata attraverso il parametro \texttt{len}.
-
- La funzione restituisce un puntatore non nullo a \texttt{dest} in caso di
- successo e un puntatore nullo in caso di fallimento, in quest'ultimo caso
- viene settata la variabile \texttt{errno} con il valore \texttt{ENOSPC} in
- caso le dimensioni dell'indirizzo eccedano la lunghezza specificata da
- \texttt{len}.
+\section{Un esempio di applicazione}
+\label{sec:sock_appplication}
-\end{itemize}
+Per evitare di rendere questa introduzione ai socket puramente teorica
+iniziamo con il mostrare un esempio di un client TCP elementare. Prima di
+passare agli esempi del client e del server, esamimeremo una caratteristica
+delle funzioni di I/O sui socket che ci tornerà utile anche in seguito.
-\section{Il comportamento delle funzioni di I/O}
+\subsection{Il comportamento delle funzioni di I/O}
\label{sec:sock_io_behav}
Una cosa di cui non sempre si è consapevoli quando si ha a che fare con i
comportamento che avrebbero con i normali files (in particolare questo accade
per i socket di tipo stream).
-Infatti con i socket può accadere che funzioni come \texttt{read} o
-\texttt{write} possano restituire in input o scrivere in output un numero di
+Infatti con i socket può accadere che funzioni come \func{read} o
+\func{write} possano restituire in input o scrivere in output un numero di
bytes minore di quello richiesto. Questo è un comportamento normale e non un
-errore, e succede perché si eccede il limite di buffer del kernel. In questo
-caso tutto quello che il programma chiamante deve fare è di ripetere la
-lettura (o scrittura) per la quantità di bytes rimanenti (lo stesso può
+errore, e succede perché si eccede in lettura o scrittura il limite di buffer
+del kernel.
+
+In questo caso tutto quello che il programma chiamante deve fare è di ripetere
+la lettura (o scrittura) per la quantità di bytes rimanenti (lo stesso può
avvenire scrivendo più di 4096 bytes in una pipe, dato che quello è il limite
di solito adottato per il buffer di trasmissione del kernel).
return (count - nleft);
}
\end{lstlisting}
- \caption{Funzione \texttt{SockRead}, legge $n$ bytes da un socket }
+ \caption{Funzione \func{SockRead}, legge \var{count} bytes da un socket }
\label{fig:sock_SockRead_code}
\end{figure}
Per questo motivo seguendo l'esempio di W. R. Stevens si sono definite due
-funzioni \texttt{SockRead} e \texttt{SockWrite} che eseguono la lettura da un
+funzioni \func{SockRead} e \func{SockWrite} che eseguono la lettura da un
socket tenendo conto di questa caratteristica, ed in grado di ritornare dopo
avere letto o scritto esattamente il numero di bytes specificato; il sorgente
è riportato in \curfig\ e \nfig\ ed è disponibile fra i sorgenti allegati alla
-guida nei files \texttt{SockRead.c} e \texttt{SockWrite.c}.
+guida nei files \file{SockRead.c} e \file{SockWrite.c}.
\begin{figure}[htb]
\centering
return (count);
}
\end{lstlisting}
- \caption{Funzione \texttt{SockWrite}, scrive $n$ bytes su un socket }
+ \caption{Funzione \func{SockWrite}, scrive \var{count} bytes su un socket }
\label{fig:sock_SockWrite_code}
\end{figure}
-Come si può notare le funzioni ripetono la lettura/scrittura in un loop fino
+Come si può notare le funzioni ripetono la lettura/scrittura in un ciclo fino
all'esaurimento del numero di bytes richiesti, in caso di errore viene
-controllato se questo è \texttt{EINTR} (cioè un'interruzione della system call
+controllato se questo è \macro{EINTR} (cioè un'interruzione della system call
dovuta ad un segnale), nel qual caso l'accesso viene ripetuto, altrimenti
-l'errore viene ritornato interrompendo il loop.
+l'errore viene ritornato interrompendo il ciclo.
-Nel caso della lettura se il numero di bytes letti è zero significa che è
+Nel caso della lettura, se il numero di bytes letti è zero, significa che si è
arrivati alla fine del file e pertanto si ritorna senza aver concluso la
-lettura di tutti i bytes richiesti.
+lettura di tutti i bytes richiesti.
+
+
+
+\subsection{Un primo esempio di client}
+\label{sec:net_cli_sample}
+
+Lo scopo di questo esempio è fornire un primo approccio alla programmazione di
+rete e vedere come si usano le funzioni descritte in precedenza, alcune delle
+funzioni usate nell'esempio saranno trattate in dettaglio nel capitolo
+successivo; qui ci limiteremo a introdurre la nomenclatura senza fornire
+definizioni precise e dettagli di funzionamento che saranno trattati
+estensivamente più avanti.
+
+In \nfig\ è riportata la sezione principale del codice del nostro client
+elementare per il servizio \textit{daytime}, un servizio standard che
+restituisce l'ora locale della macchina a cui si effettua la richiesta.
+
+\begin{figure}[!htb]
+ \footnotesize
+ \begin{lstlisting}{}
+#include <sys/types.h> /* predefined types */
+#include <unistd.h> /* include unix standard library */
+#include <arpa/inet.h> /* IP addresses conversion utiliites */
+#include <sys/socket.h> /* socket library */
+#include <stdio.h> /* include standard I/O library */
+
+int main(int argc, char *argv[])
+{
+ int sock_fd;
+ int i, nread;
+ struct sockaddr_in serv_add;
+ char buffer[MAXLINE];
+ ...
+ /* create socket */
+ if ( (sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("Socket creation error");
+ return -1;
+ }
+ /* initialize address */
+ memset((void *) &serv_add, 0, sizeof(serv_add)); /* clear server address */
+ serv_add.sin_family = AF_INET; /* address type is INET */
+ serv_add.sin_port = htons(13); /* daytime post is 13 */
+ /* build address using inet_pton */
+ if ( (inet_pton(AF_INET, argv[optind], &serv_add.sin_addr)) <= 0) {
+ perror("Address creation error");
+ return -1;
+ }
+ /* extablish connection */
+ if (connect(sock_fd, (struct sockaddr *)&serv_add, sizeof(serv_add)) < 0) {
+ perror("Connection error");
+ return -1;
+ }
+ /* read daytime from server */
+ while ( (nread = read(sock_fd, buffer, MAXLINE)) > 0) {
+ buffer[nread]=0;
+ if (fputs(buffer, stdout) == EOF) { /* write daytime */
+ perror("fputs error");
+ return -1;
+ }
+ }
+ /* error on read */
+ if (nread < 0) {
+ perror("Read error");
+ return -1;
+ }
+ /* normal exit */
+ return 0;
+}
+ \end{lstlisting}
+ \caption{Esempio di codice di un client elementare per il servizio daytime.}
+ \label{fig:net_cli_code}
+\end{figure}
+
+Il sorgente completo del programma (\file{ElemDaytimeTCPClient.c}, che
+comprende il trattamento delle opzioni e una funzione per stampare un
+messaggio di aiuto) è allegato alla guida nella sezione dei codici sorgente e
+può essere compilato su una qualunque macchina Linux.
+
+Il programma anzitutto include gli header necessari (\texttt{\small 1--5});
+dopo la dichiarazione delle variabili (\texttt{\small 9--12}) si è omessa
+tutta la parte relativa al trattamento degli argomenti passati dalla linea di
+comando (effettuata con le apposite routines illustrate in
+\capref{sec:proc_opt_handling}).
+
+Il primo passo (\texttt{\small 14--18}) è creare un \textit{socket} IPv4
+(\macro{AF\_INET}), di tipo TCP \macro{SOCK\_STREAM}. La funzione
+\macro{socket} ritorna il descrittore che viene usato per identificare il
+socket in tutte le chiamate successive. Nel caso la chiamata fallisca si
+stampa un errore con la relativa routine e si esce.
+
+Il passo seguente (\texttt{\small 19--27}) è quello di costruire una apposita
+struttura \type{sockaddr\_in} in cui sarà inserito l'indirizzo del server ed
+il numero della porta del servizio. Il primo passo è inizializzare tutto a
+zero, per poi inserire il tipo di protocollo e la porta (usando per
+quest'ultima la funzione \func{htons} per convertire il formato dell'intero
+usato dal computer a quello usato nella rete), infine si utilizza la funzione
+\func{inet\_pton} per convertire l'indirizzo numerico passato dalla linea di
+comando.
+
+Usando la funzione \func{connect} sul socket creato in precedenza
+(\texttt{\small 28--32}) si provvede poi a stabilire la connessione con il
+server specificato dall'indirizzo immesso nella struttura passata come secondo
+argomento, il terzo argomento è la dimensione di detta struttura. Dato che
+esistono diversi tipi di socket, si è dovuto effettuare un cast della
+struttura inizializzata in precedenza, che è specifica per i socket IPv4. Un
+valore di ritorno negativo implica il fallimento della connessione.
+
+Completata con successo la connessione il passo successivo (\texttt{\small
+ 34--40}) è leggere la data dal socket; il server invierà sempre una stringa
+di 26 caratteri della forma \verb|Wed Apr 4 00:53:00 2001\r\n|, che viene
+letta dalla funzione \func{read} e scritta su \file{stdout}.
+
+Dato il funzionamento di TCP la risposta potrà tornare in un unico pacchetto
+di 26 byte (come avverrà senz'altro nel caso in questione) ma potrebbe anche
+arrivare in 26 pacchetti di un byte. Per questo nel caso generale non si può
+mai assumere che tutti i dati arrivino con una singola lettura, pertanto
+quest'ultima deve essere effettuata in un ciclo in cui si continui a leggere
+fintanto che la funzione \func{read} non ritorni uno zero (che significa che
+l'altro capo ha chiuso la connessione) o un numero minore di zero (che
+significa un errore nella connessione).
+
+Si noti come in questo caso la fine dei dati sia specificata dal server che
+chiude la connessione; questa è una delle tecniche possibili (è quella usata
+pure dal protocollo HTTP), ma ce ne possono essere altre, ad esempio FTP marca
+la conclusione di un blocco di dati con la sequenza ASCII \verb|\r\n|
+(carriage return e line feed), mentre il DNS mette la lunghezza in testa ad
+ogni blocco che trasmette. Il punto essenziale è che TCP non provvede nessuna
+indicazione che permetta di marcare dei blocchi di dati, per cui se questo è
+necessario deve provvedere il programma stesso.
+
+\subsection{Un primo esempio di server}
+\label{sec:net_serv_sample}
+
+Dopo aver illustrato il client daremo anche un esempio di un server
+elementare, in grado di rispondere al precedente client. Il listato è
+nuovamente mostrato in \nfig, il sorgente completo
+(\file{ElemDaytimeTCPServer.c}) è allegato insieme agli altri file nella
+directory \file{sources}.
+
+\begin{figure}[!htbp]
+ \footnotesize
+ \begin{lstlisting}{}
+#include <sys/types.h> /* predefined types */
+#include <unistd.h> /* include unix standard library */
+#include <arpa/inet.h> /* IP addresses conversion utiliites */
+#include <sys/socket.h> /* socket library */
+#include <stdio.h> /* include standard I/O library */
+#include <time.h>
+#define MAXLINE 80
+#define BACKLOG 10
+int main(int argc, char *argv[])
+{
+/*
+ * Variables definition
+ */
+ int list_fd, conn_fd;
+ int i;
+ struct sockaddr_in serv_add;
+ char buffer[MAXLINE];
+ time_t timeval;
+ ...
+ /* create socket */
+ if ( (list_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("Socket creation error");
+ exit(-1);
+ }
+ /* initialize address */
+ memset((void *)&serv_add, 0, sizeof(serv_add)); /* clear server address */
+ serv_add.sin_family = AF_INET; /* address type is INET */
+ serv_add.sin_port = htons(13); /* daytime port is 13 */
+ serv_add.sin_addr.s_addr = htonl(INADDR_ANY); /* connect from anywhere */
+ /* bind socket */
+ if (bind(list_fd, (struct sockaddr *)&serv_add, sizeof(serv_add)) < 0) {
+ perror("bind error");
+ exit(-1);
+ }
+ /* listen on socket */
+ if (listen(list_fd, BACKLOG) < 0 ) {
+ perror("listen error");
+ exit(-1);
+ }
+ /* write daytime to client */
+ while (1) {
+ if ( (conn_fd = accept(list_fd, (struct sockaddr *) NULL, NULL)) <0 ) {
+ perror("accept error");
+ exit(-1);
+ }
+ timeval = time(NULL);
+ snprintf(buffer, sizeof(buffer), "%.24s\r\n", ctime(&timeval));
+ if ( (write(conn_fd, buffer, strlen(buffer))) < 0 ) {
+ perror("write error");
+ exit(-1);
+ }
+ close(conn_fd);
+ }
+ /* normal exit */
+ exit(0);
+}
+ \end{lstlisting}
+ \caption{Esempio di codice di un semplice server per il servizio daytime.}
+ \label{fig:net_serv_code}
+\end{figure}
+Come per il client si includono gli header necessari a cui è aggiunto quello
+per trattare i tempi, e si definiscono alcune costanti e le variabili
+necessarie in seguito (\texttt{\small 1--18}), come nel caso precedente si
+sono omesse le parti relative al trattamento delle opzioni da riga di comando.
+
+La creazione del socket (\texttt{\small 22--26}) è analoga al caso precedente,
+come pure l'inizializzazione della struttura \type{sockaddr\_in}, anche in
+questo caso si usa la porta standard del servizio daytime, ma come indirizzo
+IP si il valore predefinito \macro{INET\_ANY} che corrisponde ad un indirizzo
+generico (\texttt{\small 27--31}).
+
+Si effettua poi (\texttt{\small 32--36}) la chiamata alla funzione
+\func{bind} che permette di associare la precedente struttura al socket, in
+modo che quest'ultimo possa essere usato per accettare connessioni su una
+qualunque delle interfacce di rete locali.
+
+Il passo successivo (\texttt{\small 37--41}) è mettere ``in ascolto'' il
+socket, questo viene effettuato con la funzione \func{listen} che dice al
+kernel di accettare connessioni per il socket specificato, la funzione indica
+inoltre, con il secondo parametro, il numero massimo di connessioni che il
+kernel accetterà di mettere in coda per il suddetto socket.
+
+Questa ultima chiamata completa la preparazione del socket per l'ascolto (che
+viene chiamato anche \textit{listening descriptor}) a questo punto il processo
+è mandato in sleep (\texttt{\small 44--47}) con la successiva chiamata alla
+funzione \func{accept}, fin quando non arriva e viene accettata una
+connessione da un client.
+
+Quando questo avviene \func{accept} ritorna un secondo descrittore di socket,
+che viene chiamato \textit{connected descriptor} che è quello che viene usato
+dalla successiva chiamata alla \func{write} per scrivere la risposta al
+client, una volta che si è opportunamente (\texttt{\small 48--49}) costruita
+la stringa con la data da trasmettere. Completata la trasmissione il nuovo
+socket viene chiuso (\texttt{\small 54}). Il tutto è inserito in un ciclo
+infinito (\texttt{\small 42--55}) in modo da poter ripetere l'invio della data
+ad una successiva connessione.
+
+È importante notare che questo server è estremamente elementare, infatti a
+parte il fatto di essere dipendente da IPv4, esso è in grado di servire solo
+un client alla volta, è cioè un \textsl{server iterativo}, inoltre esso è
+scritto per essere lanciato da linea di comando, se lo si volesse utilizzare
+come demone di sistema (che è in esecuzione anche quando non c'è nessuna shell
+attiva e il terminale da cui lo si è lanciato è stato sconnesso),
+occorrerebbero delle opportune modifiche.