\begin{figure}[!htb]
\centering
- \includegraphics[width=10cm]{img/port_alloc}
+ \includegraphics[width=15cm]{img/port_alloc}
\caption{Allocazione dei numeri di porta}
\label{fig:TCPel_port_alloc}
\end{figure}
\begin{prototype}{sys/socket.h}
{int bind(int sockfd, const struct sockaddr *serv\_addr, socklen\_t addrlen)}
- Il primo argomento è un file descriptor ottenuto da una precedente chiamata
- a \func{socket}, mentre il secondo e terzo argomento sono rispettivamente
- l'indirizzo (locale) del socket e la dimensione della struttura che lo
- contiene, secondo quanto già trattato in \secref{sec:sock_sockaddr}.
+ Assegna un indirizzo ad un socket.
\bodydesc{La funzione restituisce zero in caso di successo e -1 per un
errore; in caso di errore la variabile \var{errno} viene impostata secondo
\end{errlist}}
\end{prototype}
+Il primo argomento è un file descriptor ottenuto da una precedente chiamata a
+\func{socket}, mentre il secondo e terzo argomento sono rispettivamente
+l'indirizzo (locale) del socket e la dimensione della struttura che lo
+contiene, secondo quanto già trattato in \secref{sec:sock_sockaddr}.
+
Con il TCP la chiamata \func{bind} permette di specificare l'indirizzo, la
porta, entrambi o nessuno dei due. In genere i server utilizzano una porta
nota che assegnano all'avvio, se questo non viene fatto è il kernel a
viene registrata presso il \textit{portmapper}; quest'ultimo è un altro
demone che deve essere contattato dai client per ottenere la porta effimera
su cui si trova il server.} che in genere viene identificato dalla porta su
-cui risponde.
+cui risponde (l'elenco di queste porte, e dei relativi servizi, è in
+\file{/etc/services}).
Con \func{bind} si può assegnare un IP specifico ad un socket, purché questo
appartenga ad una interfaccia della macchina. Per un client TCP questo
è pari a zero, nell'esempio \figref{fig:net_serv_code} si è usata
un'assegnazione immediata del tipo:
-\footnotesize
\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
- serv_add.sin_addr.s_addr = htonl(INADDR_ANY); /* connect from anywhere */
+serv_add.sin_addr.s_addr = htonl(INADDR_ANY); /* connect from anywhere */
\end{lstlisting}
-\normalsize
Si noti che si è usato \func{htonl} per assegnare il valore
-\const{INADDR\_ANY}; benché essendo questo pari a zero il riordinamento sia
-inutile; ma dato che tutte le costanti \val{INADDR\_} sono definite
-secondo l'ordinamento della macchina è buona norma usare sempre la funzione
-\func{htonl}.
-
-L'esempio precedete funziona con IPv4 dato che l'indirizzo è rappresentabile
-anche con un intero a 32 bit; non si può usare lo stesso metodo con IPv6,
-in cui l'indirizzo è specificato come struttura, perché il linguaggio C non
-consente l'uso di una struttura costante come operando a destra in una
-assegnazione.
-
-Per questo nell'header \file{netinet/in.h} è definita una variabile
+\const{INADDR\_ANY}, benché essendo questo pari a zero il riordinamento sia
+inutile. Si tenga presente comunque che tutte le costanti \val{INADDR\_}
+(riportate in ) sono definite secondo l'ordinamento della macchina, ed anche
+se esse possono essere invarianti rispetto all'ordinamento, è comunque buona
+norma usare sempre la funzione \func{htonl}.
+
+\begin{table}[htb]
+ \centering
+ \footnotesize
+ \begin{tabular}[c]{|l|l|}
+ \hline
+ \textbf{Costante} & \textbf{Significato} \\
+ \hline
+ \hline
+ \const{INADDR\_ANY} & Indirizzo generico (\texttt{0.0.0.0})\\
+ \const{INADDR\_BROADCAST}& Indirizzo di \textit{broadcast}.\\
+ \const{INADDR\_LOOPBACK} & Indirizzo di \textit{loopback}
+ (\texttt{127.0.0.1}).\\
+ \const{INADDR\_NONE} & Indirizzo errato.\\
+ \hline
+ \end{tabular}
+ \caption{Costanti di definizione di alcuni indirizzi generici per IPv4.}
+ \label{tab:TCPel_ipv4_addr}
+\end{table}
+
+L'esempio precedente funziona correttamente con IPv4 poiché che l'indirizzo è
+rappresentabile anche con un intero a 32 bit; non si può usare lo stesso
+metodo con IPv6, in cui l'indirizzo deve necessariamente essere specificato
+con una struttura, perché il linguaggio C non consente l'uso di una struttura
+costante come operando a destra in una assegnazione.
+
+Per questo motivo nell'header \file{netinet/in.h} è definita una variabile
\type{in6addr\_any} (dichiarata come \ctyp{extern}, ed inizializzata dal
sistema al valore \const{IN6ADRR\_ANY\_INIT}) che permette di effettuare una
-assegnazione del tipo:
+assegnazione del tipo:
-\footnotesize
\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
- serv_add.sin6_addr = in6addr_any; /* connect from anywhere */
+serv_add.sin6_addr = in6addr_any; /* connect from anywhere */
\end{lstlisting}
-\normalsize
\subsection{La funzione \func{connect}}
\begin{prototype}{sys/socket.h}
{int connect(int sockfd, const struct sockaddr *servaddr, socklen\_t addrlen)}
- Il primo argomento è un file descriptor ottenuto da una precedente chiamata
- a \func{socket}, mentre il secondo e terzo argomento sono rispettivamente
- l'indirizzo e la dimensione della struttura che contiene l'indirizzo del
- socket, già descritta in \secref{sec:sock_sockaddr}.
+ Stabilisce una connessione fra due socket.
\bodydesc{La funzione restituisce zero in caso di successo e -1 per un
- errore, in caso di errore la variabile \var{errno} viene impostata secondo
- i seguenti codici di errore:
+ errore, in caso di errore \var{errno} assumerà i valori:
\begin{errlist}
\item[\errcode{ECONNREFUSED}] non c'è nessuno in ascolto sull'indirizzo
remoto.
\errval{ENOTSOCK}, \errval{EISCONN} e \errval{EADDRINUSE}.}
\end{prototype}
+Il primo argomento è un file descriptor ottenuto da una precedente chiamata a
+\func{socket}, mentre il secondo e terzo argomento sono rispettivamente
+l'indirizzo e la dimensione della struttura che contiene l'indirizzo del
+socket, già descritta in \secref{sec:sock_sockaddr}.
+
+
La struttura dell'indirizzo deve essere inizializzata con l'indirizzo IP e il
numero di porta del server a cui ci si vuole connettere, come mostrato
nell'esempio \secref{sec:net_cli_sample} usando le funzioni illustrate in
utile anche in seguito.
-\subsection{La \textit{endianess}}
+\subsection{La \textit{endianess}\index{endianess}}
\label{sec:sock_endianess}
La rappresentazione di un numero binario in un computer può essere fatta in
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 \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}.
+La \textit{endianess}\index{endianess} di un computer dipende essenzialmente
+dalla architettura 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 PCI, 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
\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 byte 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.
+Il problema connesso all'endianess\index{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 byte 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
\label{sec:sock_func_ipv4}
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.
+binario usato nelle strutture degli indirizzi alla rappresentazione simbolica
+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
\texttt{192.160.0.1}) al formato binario (direttamente in \textit{network
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 \var{dest} inizializzato a \val{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
- \const{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.
-\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}
+\begin{functions}
+ \headdecl{arpa/inet.h}
+
+ \funcdecl{in\_addr\_t inet\_addr(const char *strptr)} Converte la stringa
+ dell'indirizzo \textit{dotted decimal} in nel numero IP in network order.
+
+ \funcdecl{int inet\_aton(const char *src, struct in\_addr *dest)} Converte
+ la stringa dell'indirizzo \textit{dotted decimal} in un indirizzo IP.
+
+ \funcdecl{char *inet\_ntoa(struct in\_addr addrptr)}
+ Converte un indirizzo IP in una stringa \textit{dotted decimal}.
+
+ \bodydesc{Tutte queste le funzioni non generano codice di errore.}
+\end{functions}
+
+La prima funcione, \func{inet\_addr}, restituisce l'indirizzo a 32 bit in
+network order (del tipo \type{in\_addr\_t}) a partire dalla stringa passata
+nellargomento \param{strptr}. In caso di errore (quando la stringa non esprime
+un indirizzo valido) restituisce invece il valore \const{INADDR\_NONE} che
+tipicamente sono trentadue bit a uno. Questo però 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
+di \func{inet\_aton}.
+
+La funzione \func{inet\_aton} converte la stringa puntata da \param{src}
+nell'indirizzo binario che viene memorizzato nell'opportuna struttura
+\var{in\_addr} (si veda \secref{fig:sock_sa_ipv4_struct}) situata
+all'indirizzo dato dall'argomento \param{dest} (è espressa in questa forma in
+modo da poterla usare direttamente con il puntatore usato per passare la
+struttura degli indirizzi). La funzione restituesce 0 in caso di successo e 1
+in caso di fallimento. Se usata con \var{dest} inizializzato a \val{NULL}
+effettua la validazione dell'indirizzo.
+
+L'ultima funzione, \func{inet\_ntoa}, 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.
\subsection{Le funzioni \func{inet\_pton} e \func{inet\_ntop}}
Converte l'indirizzo espresso tramite una stringa nel valore numerico.
\bodydesc{La funzione restituisce un valore negativo se \var{af} specifica
- una famiglia di indirizzi non valida, settando \var{errno} a
+ una famiglia di indirizzi non valida, con \var{errno} che assume il valore
\errcode{EAFNOSUPPORT}, un valore nullo se \param{src} non rappresenta un
indirizzo valido, ed un valore positivo in caso di successo.}
\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
- \const{INET\_ADDRSTRLEN} in caso di indirizzi IPv4 e
- \const{INET6\_ADDRSTRLEN} per indirizzi IPv6; la lunghezza del buffer deve
- comunque venire specificata attraverso il parametro \var{len}.
+ Converte l'indirizzo dalla relativa struttura in una stringa simbolica.
- \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
- \errval{ENOSPC} in caso le dimensioni dell'indirizzo eccedano la lunghezza
- specificata da \var{len} o \errval{ENOAFSUPPORT} in caso \var{af} non sia
- una famiglia di indirizzi valida.}
+ \bodydesc{La funzione restituisce un puntatore non nullo alla stringa
+ convertita in caso di successo e \val{NULL} in caso di fallimento, nel
+ qual caso \var{errno} assume i valor:
+ \begin{errlist}
+ \item[\errcode{ENOSPC}] le dimensioni della stringa con la conversione
+ dell'indirizzo eccedono la lunghezza specificata da \var{len}.
+ \item[\errcode{ENOAFSUPPORT}] la famiglia di indirizzi \var{af} non è una
+ valida.
+ \end{errlist}}
\end{prototype}
+La funzione 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
+\const{INET\_ADDRSTRLEN} in caso di indirizzi IPv4 e \const{INET6\_ADDRSTRLEN}
+per indirizzi IPv6; la lunghezza del buffer deve comunque venire specificata
+attraverso il parametro \var{len}.
+
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
nullo e deve essere allocato precedentemente.
Il formato usato per gli indirizzi in formato di presentazione è la notazione
-\textit{dotted decimal} per IPv4 e quella descritta in
+\textit{dotted decimal} per IPv4 e quello descritto in
\secref{sec:IP_ipv6_notation} per IPv6.
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.
+passare agli esempi del client e del server, ritorniamo con maggiori dettagli
+su una caratteristica delle funzioni di I/O che nel caso dei socket è
+particolarmente rilevante, e che ci tornerà utile anche in seguito.
\subsection{Il comportamento delle funzioni di I/O}
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.
+comportamento normale per l'I/O su file; con i normali file di dati il
+problema si avverte solo quando si incontra la fine del file, ma in generale
+non è così.
In questo caso tutto quello che il programma chiamante deve fare è di ripetere
-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).
+la lettura (o scrittura) per la quantità di byte rimanenti (e le funzioni si
+possono bloccare se i dati non sono disponibili): è lo stesso comportamento
+che si può avere scrivendo più di \const{PIPE\_BUF} byte in una pipe (si
+riveda quanto detto in \secref{sec:ipc_pipes}).
\begin{figure}[htb]
\centering
\label{fig:sock_SockRead_code}
\end{figure}
-Per questo motivo seguendo l'esempio di W. R. Stevens si sono definite due
-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 byte specificato; il sorgente è
-riportato in \figref{fig:sock_SockRead_code} e
+Per questo motivo, seguendo l'esempio di W. R. Stevens in \cite{UNP1}, si sono
+definite due 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 byte
+specificato; il sorgente è riportato in \figref{fig:sock_SockRead_code} e
\figref{fig:sock_SockWrite_code} ed è disponibile fra i sorgenti allegati alla
guida nei files \file{SockRead.c} e \file{SockWrite.c}.
l'errore viene ritornato interrompendo il ciclo.
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 byte richiesti.
+arrivati alla fine del file (per i socket questo significa in genere che
+l'altro capo è stato chiuso, e non è quindi più possibile leggere niente) e
+pertanto si ritorna senza aver concluso la lettura di tutti i byte richiesti.