X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=socket.tex;h=06831e7ba290f3b0a4cdf460b9de4e6f2ee888c5;hp=8c8440f3c9befbd1c9b411b39b2846f5b0e7abb0;hb=d47f15496fa85c8ec22edcde608f2665ec5b95ae;hpb=426fe2c288fe0b24fe6c3bddb712f274de7cf959 diff --git a/socket.tex b/socket.tex index 8c8440f..06831e7 100644 --- a/socket.tex +++ b/socket.tex @@ -446,7 +446,7 @@ si usa IPv4) L'indirizzo di un socket internet (secondo IPv4) comprende l'indirizzo internet di un'interfaccia più un \textsl{numero di porta} (affronteremo in -dettaglio il significato di questi numeri in \secref{sec:TCPel_port_num}). Il +dettaglio il significato di questi numeri in \secref{sec:TCP_port_num}). Il protocollo IP non 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 @@ -458,15 +458,15 @@ specifica il \textsl{numero di porta}. I numeri di porta sotto il 1024 sono chiamati \textsl{riservati} in quanto utilizzati da servizi standard e soltanto processi con i privilegi di amministratore (con user-ID effettivo uguale a zero) o con la capability \texttt{CAP\_NET\_BIND\_SERVICE} possono -usare la funzione \func{bind} (che vedremo in \secref{sec:TCPel_func_bind}) su +usare la funzione \func{bind} (che vedremo in \secref{sec:TCP_func_bind}) su queste porte. Il membro \var{sin\_addr} contiene un indirizzo internet, e viene acceduto sia come struttura (un resto di una implementazione precedente in cui questa era una \direct{union} usata per accedere alle diverse classi di indirizzi) che -direttamente come intero. In \file{netinet/in.h} vengono definiti anche alcune -costanti per alcuni indirizzi speciali, che vedremo in -\tabref{tab:TCPel_ipv4_addr}. +direttamente come intero. In \file{netinet/in.h} vengono definite anche alcune +costanti che identificano alcuni indirizzi speciali, riportati in +\tabref{tab:TCP_ipv4_addr}. Infine occorre sottolineare che sia gli indirizzi che i numeri di porta devono essere specificati in quello che viene chiamato \textit{network order}, cioè @@ -957,229 +957,6 @@ Il formato usato per gli indirizzi in formato di presentazione \index{socket|)} -\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, ritorniamo con maggiori dettagli -su una caratteristica delle funzioni di I/O, già accennata in -\secref{sec:file_read} e \secref{sec:file_write}, che nel caso dei socket è -particolarmente rilevante, e che ci tornerà utile anche in seguito. - - -\subsection{Il comportamento delle funzioni di I/O} -\label{sec:sock_io_behav} - -Una cosa che si tende a dimenticare quando si ha a che fare con i socket è che -le funzioni di input/output non sempre hanno lo stesso comportamento che -avrebbero con i normali file di dati (in particolare questo accade per i -socket di tipo stream). - -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 per l'I/O su file, ma con i normali file di dati il -problema si avverte solo quando si incontra la fine del file, in generale non -è così, e con i socket questo è particolarmente evidente. - -Quando ci si trova ad affrontare questo comportamento tutto quello che si deve -fare è semplicemente ripetere la lettura (o la scrittura) per la quantità di -byte restanti, tenendo conto che 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] - \footnotesize \centering - \begin{minipage}[c]{15cm} - \includecodesample{listati/FullRead.c} - \end{minipage} - \normalsize - \caption{Funzione \func{FullRead}, legge esattamente \var{count} byte da un - file descriptor, iterando opportunamente le letture.} - \label{fig:sock_FullRead_code} -\end{figure} - -Per questo motivo, seguendo l'esempio di R. W. Stevens in \cite{UNP1}, si sono -definite due funzioni, \func{FullRead} e \func{FullWrite}, che eseguono -lettura e scrittura tenendo conto di questa caratteristica, ed in grado di -ritornare dopo avere letto o scritto esattamente il numero di byte -specificato; il sorgente è riportato rispettivamente in -\figref{fig:sock_FullRead_code} e \figref{fig:sock_FullWrite_code} ed è -disponibile fra i sorgenti allegati alla guida nei file \file{FullRead.c} e -\file{FullWrite.c}. - -\begin{figure}[htb] - \centering - \footnotesize \centering - \begin{minipage}[c]{15cm} - \includecodesample{listati/FullWrite.c} - \end{minipage} - \normalsize - \caption{Funzione \func{FullWrite}, scrive \var{count} byte su un socket.} - \label{fig:sock_FullWrite_code} -\end{figure} - -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 è \errcode{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 ciclo. - -Nel caso della lettura, se il numero di byte letti è zero, significa che si è -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. - - - -\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 \centering - \begin{minipage}[c]{15cm} - \includecodesample{listati/ElemDaytimeTCPClient.c} - \end{minipage} - \normalsize - \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 -(\const{AF\_INET}), di tipo TCP \const{SOCK\_STREAM}. La funzione -\func{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 \struct{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 \centering - \begin{minipage}[c]{15cm} - \includecodesample{listati/ElemDaytimeTCPServer.c} - \end{minipage} - \normalsize - \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 \struct{sockaddr\_in}, anche in -questo caso si usa la porta standard del servizio daytime, ma come indirizzo -IP si il valore predefinito \const{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 occorrerebbero le opportune modifiche per tener conto -di quanto illustrato in \secref{sec:sess_daemon}. - - %%% Local Variables: %%% mode: latex