\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
+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 un'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{presa}, 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 (vedi \secref{sec:ipc_pipes}) 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
risale al 1983, quando furono introdotti nel BSD 4.2; l'interfaccia è rimasta
sostanzialmente la stessa con piccole modifiche negli anni successivi. Benché
-siano state sviluppate interfacce alternative, originate dai sistemi SYSV,
+siano state sviluppate interfacce alternative, originate dai sistemi SVr4,
come la XTI (\textit{X/Open Transport Interface}) nessuna ha mai raggiunto la
-diffusione e la popolarità di quella dei socket (né tantomeno usabilità e
-flessibilità).
+diffusione e la popolarità di quella dei socket (né tantomeno la stessa
+usabilità e flessibilità).
La flessibilità e la genericità dell'interfaccia inoltre ha consentito di
utilizzare i socket con i più disparati meccanismi di comunicazione, e non
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.
Per questo motivo una semplice descrizione dell'interfaccia è assolutamente
inutile, in quanto il comportamento di quest'ultima e le problematiche da
-affrontare cambiano radicalmente a seconda dello ``stile'' di comunicazione
-usato. La scelta di questo stile va infatti ad incidere sulla semantica che
-verrà utilizzata a livello utente per gestire la comunicazione (su come
-inviare e ricevere i dati) e sul comportamento effettivo delle funzioni
-utilizzate.
+affrontare cambiano radicalmente a seconda dello \textsl{stile} di
+comunicazione usato. La scelta di questo stile va infatti ad incidere sulla
+semantica che verrà utilizzata a livello utente per gestire la comunicazione
+(su come inviare e ricevere i dati) e sul comportamento effettivo delle
+funzioni utilizzate.
La scelta di uno stile dipende sia dai meccanismi disponibili, sia dal tipo di
comunicazione che si vuole effettuare. Ad esempio alcuni stili di
-comunicazione considerano i dati come una sequenza continua di bytes, altri
+comunicazione considerano i dati come una sequenza continua di byte, altri
invece li raggruppano in blocchi (i pacchetti).
Un'altro esempio di stile concerne la possibilità che la comunicazione possa o
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{prototype}{sys/socket.h}{int socket(int domain, int type, int protocol)}
-\begin{itemize}
-\item \texttt{int socket(int domain, int type, int protocol)}
+ Apre un socket.
- La funzione restituisce un intero positivo se riesce, e -1 se fallisce, in
- quest'ultimo caso la variabile \texttt{errno} è settata con i seguenti
- codici di errore:
+ \bodydesc{La funzione restituisce un intero positivo se riesce, e -1 se
+ fallisce, in quest'ultimo caso la variabile \var{errno} è impostata 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
- creare il socket.
- \end{itemize}
-\end{itemize}
+ \item[\macro{EINVAL}] Protocollo sconosciuto o dominio non disponibile.
+ \item[\macro{ENOBUFS}] Non c'è sufficiente memoria per creare il socket (può
+ essere anche \macro{ENOMEM}).
+ \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
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
-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.
+indirizzi usati in quel dominio; le pagine di manuale di Linux si riferiscono
+a questi anche come \textit{name space}, (nome che però il manuale delle
+\acr{glibc} riserva ai domini) e che identifica il formato degli indirizzi
+usati in quel dominio.
L'idea alla base della distinzione era che una famiglia di protocolli potesse
supportare vari tipi di indirizzi, per cui il prefisso \texttt{PF\_} si
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
-protocolli disponibili sono riportate in \ntab.
+indirizzi sono definiti dall'header \textit{socket.h}. In Linux le famiglie di
+protocolli disponibili sono riportate in \tabref{tab:net_pf_names}.
\begin{table}[htb]
\footnotesize
\centering
- \begin{tabular}[c]{lll}
- Nome & Utilizzo & Man page \\
+ \begin{tabular}[c]{|l|l|l|}
+ \hline
+ \textbf{Nome} & \textbf{Utilizzo} &\textbf{Man page} \\
+ \hline
+ \hline
+ \macro{PF\_UNIX},
+ \macro{PF\_LOCAL} & Local communication & unix(7) \\
+ \macro{PF\_INET} & IPv4 Internet protocols & ip(7) \\
+ \macro{PF\_INET6} & IPv6 Internet protocols & ipv6(7) \\
+ \macro{PF\_IPX} & IPX - Novell protocols & \\
+ \macro{PF\_NETLINK}& Kernel user interface device & netlink(7) \\
+ \macro{PF\_X25} & ITU-T X.25 / ISO-8208 protocol & x25(7) \\
+ \macro{PF\_AX25} & Amateur radio AX.25 protocol & \\
+ \macro{PF\_ATMPVC} & Access to raw ATM PVCs & \\
+ \macro{PF\_APPLETALK}& Appletalk & ddp(7) \\
+ \macro{PF\_PACKET} & Low level packet interface & packet(7) \\
\hline
- PF\_UNIX,PF\_LOCAL & Local communication & unix(7) \\
- PF\_INET & IPv4 Internet protocols & ip(7) \\
- PF\_INET6 & IPv6 Internet protocols & \\
- PF\_IPX & IPX - Novell protocols & \\
- PF\_NETLINK & Kernel user interface device & netlink(7) \\
- PF\_X25 & ITU-T X.25 / ISO-8208 protocol & x25(7) \\
- PF\_AX25 & Amateur radio AX.25 protocol & \\
- PF\_ATMPVC & Access to raw ATM PVCs & \\
- PF\_APPLETALK & Appletalk & ddp(7) \\
- PF\_PACKET & Low level packet interface & packet(7) \\
\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è con userid
+effettivo uguale a zero) o con 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
-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}:
+scegliere lo stile di comunicazione indicando il tipo di socket; Linux e le
+\acr{glibc} mettono a disposizione i seguenti tipi di socket (che il manuale
+della \acr{glibc} chiama \textit{styles}) definiti come \ctyp{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
-tipo di socket sono valide, in quanto non è detto che nella famiglia esista un
-protocollo per tutti gli stili di comunicazione indicati qui sopra. Una
-tabella che mostra le combinazioni valide è la seguente:
+Si tenga presente che non tutte le combinazioni fra una famiglia di protocolli
+e un tipo di socket sono valide, in quanto non è detto che in una famiglia
+esista un protocollo per ciascuno dei diversi stili di comunicazione appena
+elencati.
\begin{table}[htb]
\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}
-Dove per ogni combinazione valida si è indicato il tipo di protocollo, o la
-parola \textsl{si} qualora non il protocollo non abbia un nome definito,
-mentre si sono lasciate vuote le caselle per le combinazioni non supportate.
+In \secref{tab:sock_sock_valid_combinations} sono mostrate le combinazioni
+valide possibili per le varie famiglie di protocolli. Per ogni combinazione
+valida si è indicato il tipo di protocollo, o la 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
-definizione dello standard ANSI, e per questo nel 1982 fu scelto di definire
-una struttura generica \texttt{sockaddr} per gli indirizzi dei socket mostrata
-in \nfig:
+(i \ctyp{void *}), ma l'interfaccia dei socket è antecedente alla definizione
+dello standard ANSI, e per questo nel 1982 fu scelto di definire una struttura
+generica per gli indirizzi dei socket, \type{sockaddr}, che si è riportata in
+\figref{fig:sock_sa_gen_struct}.
-\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
-definiti; la struttura è invece definita nell'include file
-\texttt{sys/socket.h}
+POSIX.1g, riassunti in \tabref{tab:sock_data_types} con i rispettivi file di
+include in cui sono definiti; la struttura è invece definita nell'include file
+\file{sys/socket.h}.
-\begin{table}[!htbp]
+\begin{table}[!htb]
\centering
\begin{tabular}{|l|l|l|}
\hline
- \multicolumn{1}{|c|}{Tipo}& \multicolumn{1}{|c|}{Descrizione}&
- \multicolumn{1}{|c|}{Header} \\
+ \multicolumn{1}{|c|}{\textbf{Tipo}}&
+ \multicolumn{1}{|c|}{\textbf{Descrizione}}&
+ \multicolumn{1}{|c|}{\textbf{Header}} \\
\hline
\hline
- \texttt{int8\_t} & intero a 8 bit con segno & \texttt{sys/types.h}\\
- \texttt{uint8\_t} & intero a 8 bit senza segno & \texttt{sys/types.h}\\
- \texttt{int16\_t} & intero a 16 bit con segno & \texttt{sys/types.h}\\
- \texttt{uint16\_t} & intero a 16 bit senza segno& \texttt{sys/types.h}\\
- \texttt{int32\_t} & intero a 32 bit con segno & \texttt{sys/types.h}\\
- \texttt{uint32\_t} & intero a 32 bit senza segno& \texttt{sys/types.h}\\
+ \type{int8\_t} & intero a 8 bit con segno & \file{sys/types.h}\\
+ \type{uint8\_t} & intero a 8 bit senza segno & \file{sys/types.h}\\
+ \type{int16\_t} & intero a 16 bit con segno & \file{sys/types.h}\\
+ \type{uint16\_t} & intero a 16 bit senza segno& \file{sys/types.h}\\
+ \type{int32\_t} & intero a 32 bit con segno & \file{sys/types.h}\\
+ \type{uint32\_t} & intero a 32 bit senza segno& \file{sys/types.h}\\
\hline
- \texttt{sa\_family\_t} & famiglia degli indirizzi& \texttt{sys/socket.h}\\
- \texttt{socklen\_t} & lunghezza (\texttt{uint32\_t}) dell'indirizzo di
- un socket& \texttt{sys/socket.h}\\
+ \type{sa\_family\_t} & famiglia degli indirizzi& \file{sys/socket.h}\\
+ \type{socklen\_t} & lunghezza (\type{uint32\_t}) dell'indirizzo di
+ un socket& \file{sys/socket.h}\\
\hline
- \texttt{in\_addr\_t} & indirizzo IPv4 (\texttt{uint32\_t}) &
- \texttt{netinet/in.h}\\
- \texttt{in\_port\_t} & porta TCP o UDP (\texttt{uint16\_t})&
- \texttt{netinet/in.h}\\
+ \type{in\_addr\_t} & indirizzo IPv4 (\type{uint32\_t}) &
+ \file{netinet/in.h}\\
+ \type{in\_port\_t} & porta TCP o UDP (\type{uint16\_t})&
+ \file{netinet/in.h}\\
\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 la struttura è leggermente diversa e prevede un primo membro
-aggiuntivo \texttt{uint8\_t sin\_len} (come riportato da R. Stevens nei suoi
+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 sussiste. Il campo
-\texttt{sa\_family\_t} era storicamente un \texttt{unsigned short}.
+richiesto dallo standard POSIX.1g, in Linux pertanto non esiste. Il campo
+\type{sa\_family\_t} era storicamente un \ctyp{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 \ctyp{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 pagine di manuale ha la forma mostrata in
+\figref{fig:sock_sa_ipv4_struct}, 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}
prevede numeri di porta, che sono utilizzati solo dai protocolli di livello
superiore come TCP e UDP. Questa struttura però viene usata anche per i socket
RAW che accedono direttamente al livello di IP, nel qual caso il numero della
-porta viene settato al numero di protocollo.
+porta viene impostato al numero di protocollo.
-Il membro \texttt{sin\_family} deve essere sempre settato; \texttt{sin\_port}
-specifica il numero di porta (vedi \ref{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 \texttt{CAP\_NET\_BIND\_SERVICE} possono usare la
-funzione \texttt{bind} su queste porte.
+Il membro \var{sin\_family} deve essere sempre impostato; \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 (con userid
+effettivo 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 un'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 impostato 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 riferimento esplicito
+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 \texttt{sys/un.h}.
+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 invece \texttt{sun\_path} inizia con uno zero
-vengono 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 byte come stringa (senza terminazione).
% \subsection{Il passaggio delle strutture}
% \texttt{getpeername} invece ricevono i valori del kernel
+
\section{Le funzioni di conversione degli indirizzi}
\label{sec:sock_addr_func}
\subsection{La \textit{endianess}}
\label{sec:sock_endianess}
-La rappresentazione di un numbero binario in un computer può essere fatta in
+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
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 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 è
+esempio nel caso dell'intero a 16 bit ci si ritroverà con i due byte in cui è
suddiviso scambiati di posto, e ne sarà quindi invertito l'ordine di lettura
-per cui, per riavere il valore originale dovrenno essere rovesciati.
+per cui, per riavere il valore originale dovranno essere 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; queste
+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{itemize}
-\item \texttt{unsigned long int htonl(unsigned long int hostlong)}
-
- Converte l'intero a 32 bit \texttt{hostlong} dal formato della macchina a
+\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).
-
-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 \texttt{inet\_aton}, \texttt{inet\_addr} e
- \texttt{inet\_ntoa}}
+\end{prototype}
+I nomi sono assegnati usando la lettera \texttt{n} come mnemonico per indicare
+l'ordinamento usato sulla rete (da \textit{network order}) e la lettera
+\texttt{h} come mnemonico per l'ordinamento usato sulla macchina locale (da
+\textit{host order}), mentre le lettere \texttt{s} e \texttt{l} stanno ad
+indicare i tipi di dato (\ctyp{long} o \ctyp{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.
+
+
+\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 \texttt{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\_ntoa(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}
+\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 \texttt{inet\_pton} e \texttt{inet\_ntop}}
+\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 \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 degli
-mnemonici per ricordare il tipo di conversione effettuata e stanno per
-\textit{presentation} e \textit{numeric}.
+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 \texttt{n} e \texttt{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 \param{af} che indica il tipo di
+indirizzo e può essere \macro{AF\_INET} o \macro{AF\_INET6}. Se la famiglia
+indicata non è valida entrambe le funzioni impostano la variabile \var{errno}
+al valore \macro{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}.
+
+ \bodydesc{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 impostata 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 convertiti 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}.
-\end{itemize}
+\section{Un esempio di applicazione}
+\label{sec:sock_appplication}
+
+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, esamineremo 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
-bytes minore di quello richiesto. Questo è un comportamento normale e non un
-errore, e succede perché si eccede in lettura o scrittura il limite di buffer
-del kernel.
+Infatti con i socket è comune che funzioni come \func{read} o \func{write}
+possano restituire in input o scrivere in output un numero di byte minore di
+quello richiesto. Come già accennato in \secref{sec:file_read} questo è un
+comportamento normale anche per l'I/O su file, 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
+la lettura (o scrittura) per la quantità di byte rimanenti (lo stesso può
+avvenire scrivendo più di 4096 byte in una pipe, dato che quello è il limite
di solito adottato per il buffer di trasmissione del kernel).
\begin{figure}[htb]
return (count - nleft);
}
\end{lstlisting}
- \caption{Funzione \texttt{SockRead}, legge $n$ bytes da un socket }
+ \caption{Funzione \func{SockRead}, legge \var{count} byte 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
+avere letto o scritto esattamente il numero di byte 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} byte su un socket }
\label{fig:sock_SockWrite_code}
\end{figure}
-Come si può notare le funzioni ripetono la lettura/scrittura in un loop fino
-all'esaurimento del numero di bytes richiesti, in caso di errore viene
-controllato se questo è \texttt{EINTR} (cioè un'interruzione della system call
+Come si può notare le funzioni ripetono la lettura/scrittura in un ciclo fino
+all'esaurimento del numero di byte richiesti, in caso di errore viene
+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 byte 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 byte 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 \figref{fig:net_cli_code} è 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 utilities */
+#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 routine 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 un'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 \figref{fig:net_serv_code}, 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 utilities */
+#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.
+
+%%% Local Variables:
+%%% mode: latex
+%%% TeX-master: "gapil"
+%%% End: