X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=elemtcp.tex;h=b021a78ce2f930973fb330c63c66ca67b4761746;hp=c57bbcf6f8aa0080bd5cb92878eb8c37b59242ba;hb=e3e15ed6d698e5cc35f3b7f4c5db96adc38255c3;hpb=b324b7a09e071b2f84a1849d109d4d14f27f44cd diff --git a/elemtcp.tex b/elemtcp.tex index c57bbcf..b021a78 100644 --- a/elemtcp.tex +++ b/elemtcp.tex @@ -9,7 +9,7 @@ %% License". %% \chapter{Socket TCP} -\label{cha:elem_TCP_sock} +\label{cha:TCP_socket} In questo capitolo iniziamo ad approfondire la conoscenza dei socket TCP, iniziando con una descrizione delle principali caratteristiche del @@ -1108,7 +1108,6 @@ scritti nella struttura puntata da \param{name}. Si tenga presente che se si utilizzato un buffer troppo piccolo per \param{name} l'indirizzo risulterà troncato. - La funzione si usa tutte le volte che si vuole avere l'indirizzo locale di un socket; ad esempio può essere usata da un client (che usualmente non chiama \func{bind}) per ottenere numero IP e porta locale associati al socket @@ -1146,7 +1145,8 @@ restituisce l'indirizzo remoto del socket, cio capo della connessione. Ci si può chiedere a cosa serva questa funzione dato che dal lato client l'indirizzo remoto è sempre noto quando si esegue la \func{connect} mentre dal lato server si possono usare, come vedremo in -\figref{fig:TCP_cunc_serv_code}, i valori di ritorno di \func{accept}. +\figref{fig:TCP_daytime_cunc_server_code}, i valori di ritorno di +\func{accept}. Il fatto è che in generale quest'ultimo caso non è sempre possibile. In particolare questo avviene quando il server, invece di gestire la connessione @@ -1299,11 +1299,12 @@ nell'\href{http://www.ietf.org/rfc/rfc0867.txt}{RFC~867}, che restituisce l'ora locale della macchina a cui si effettua la richiesta, e che è assegnato alla porta 13. -In \figref{fig:TCP_cli_code} è riportata la sezione principale del codice del -nostro client. Il sorgente completo del programma (\file{TCP_daytime.c}, che -comprende il trattamento delle opzioni ed una funzione per stampare un -messaggio di aiuto) è allegato alla guida nella sezione dei codici sorgente e -può essere compilato su una qualunque macchina GNU/Linux. +In \figref{fig:TCP_daytime_client_code} è riportata la sezione principale del +codice del nostro client. Il sorgente completo del programma +(\file{TCP\_daytime.c}, che comprende il trattamento delle opzioni ed una +funzione per stampare un messaggio di aiuto) è allegato alla guida nella +sezione dei codici sorgente e può essere compilato su una qualunque macchina +GNU/Linux. \begin{figure}[!htb] \footnotesize \centering @@ -1350,9 +1351,10 @@ ritorna (\texttt{\small 31}). Completata con successo la connessione il passo successivo (\texttt{\small 34--40}) è leggere la data dal socket; il protocollo prevede che il server -invii sempre una stringa alfanumerica di 26 caratteri, nella forma giorno -della settimana, mese, ora minuto e secondo, anno, seguita dai caratteri di -terminazione \verb|\r\n|, cioè qualcosa del tipo: +invii sempre una stringa alfanumerica, il formato della stringa non è +specificato dallo standard, per cui noi useremo il formato usato dalla +funzione \func{ctime}, seguito dai caratteri di terminazione \verb|\r\n|, cioè +qualcosa del tipo: \begin{verbatim} Wed Apr 4 00:53:00 2001\r\n \end{verbatim} @@ -1399,7 +1401,7 @@ elementare, che sia anche in grado di rispondere al precedente client. Come primo esempio realizzeremo un server iterativo, in grado di fornire una sola risposta alla volta. Il codice del programma è nuovamente mostrato in \figref{fig:TCP_daytime_iter_server_code}, il sorgente completo -(\file{TCP_iter_daytimed.c}) è allegato insieme agli altri file degli esempi. +(\file{TCP\_iter\_daytimed.c}) è allegato insieme agli altri file degli esempi. \begin{figure}[!htbp] \footnotesize \centering @@ -1477,36 +1479,37 @@ non \subsection{Un server \textit{daytime} concorrente} \label{sec:TCP_daytime_cunc_server} -Il server \texttt{daytime} dell'esempio in \secref{sec:TCP_daytime_client} è un -tipico esempio di server iterativo, in cui viene servita una richiesta alla -volta; in generale però, specie se il servizio è più complesso e comporta uno -scambio di dati più sostanzioso di quello in questione, non è opportuno -bloccare un server nel servizio di un client per volta; per questo si ricorre -alle capacità di multitasking del sistema. +Il server \texttt{daytime} dell'esempio in +\secref{sec:TCP_daytime_iter_server} è un tipico esempio di server iterativo, +in cui viene servita una richiesta alla volta; in generale però, specie se il +servizio è più complesso e comporta uno scambio di dati più sostanzioso di +quello in questione, non è opportuno bloccare un server nel servizio di un +client per volta; per questo si ricorre alle capacità di multitasking del +sistema. Come accennato anche in \secref{sec:proc_gen} una delle modalità più comuni di funzionamento da parte dei server è quella di usare la funzione \func{fork} per creare, ad ogni richiesta da parte di un client, un processo figlio che si incarichi della gestione della comunicazione. Si è allora riscritto il server -\texttt{daytime} dell'esempio precedente in forma concorrente, inserendo anche +\textit{daytime} dell'esempio precedente in forma concorrente, inserendo anche una opzione per la stampa degli indirizzi delle connessioni ricevute. -In \figref{fig:TCP_cunc_serv_code} è mostrato un estratto del codice, in cui -si sono tralasciati il trattamento delle opzioni e le parti rimaste invariate -rispetto al precedente esempio (cioè tutta la parte riguardante l'apertura -passiva del socket). Al solito il sorgente completo del server, nel file -\file{ElemDaytimeTCPCuncServ.c}, è allegato insieme ai sorgenti degli altri -esempi. +In \figref{fig:TCP_daytime_cunc_server_code} è mostrato un estratto del +codice, in cui si sono tralasciati il trattamento delle opzioni e le parti +rimaste invariate rispetto al precedente esempio (cioè tutta la parte +riguardante l'apertura passiva del socket). Al solito il sorgente completo del +server, nel file \file{TCP\_cunc\_daytimed.c}, è allegato insieme ai sorgenti +degli altri esempi. \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} - \includecodesample{listati/ElemDaytimeTCPCuncServ.c} + \includecodesample{listati/TCP_cunc_daytimed.c} \end{minipage} \normalsize \caption{Esempio di codice di un server concorrente elementare per il servizio daytime.} - \label{fig:TCP_cunc_serv_code} + \label{fig:TCP_daytime_cunc_server_code} \end{figure} Stavolta (\texttt{\small 21--25}) la funzione \func{accept} è chiamata @@ -1577,6 +1580,7 @@ torneremo su questo pi complessi. + \section{Un esempio più completo: il servizio \textit{echo}} \label{sec:TCP_echo_application} @@ -1584,57 +1588,101 @@ L'esempio precedente, basato sul servizio \textit{daytime}, elementare, in cui il flusso dei dati va solo nella direzione dal server al client. In questa sezione esamineremo un esempio di applicazione client/server un po' più complessa, che usi i socket TCP per una comunicazione in entrambe -le direzioni, implementando il servizio standard \textit{echo}, così come -definito dall'\href{http://www.ietf.org/rfc/rfc0862.txt}{RFC~862}. - -Si è scelto di usare questo servizio, seguendo l'esempio di \cite{UNP1}, -perché costituisce il prototipo ideale di una generica applicazione di rete in -cui un server risponde alle richieste di un client; nel caso di una -applicazione più complessa si potrà avere in più una elaborazione dell'input -del client da parte del server nel fornire le risposte in uscita. - -Ci limiteremo ad un esempio elementare, che usi solo le funzioni di base, ma -prenderemo in esame, oltre al comportamento in condizioni normali, anche tutti -i possibili scenari particolari (errori, sconnessione della rete, crash del -client o del server durante la connessione) che possono avere luogo durante -l'impiego di un'applicazione di rete. - -Partiremo da un'implementazione elementare che dovrà essere rimaneggiata di -volta in volta per poter tenere conto di tutte le evenienze che si possono -manifestare nella vita reale di un'applicazione di rete, fino ad arrivare ad -un'implementazione completa. +le direzioni. + +Ci limiteremo a fornire una implementazione elementare, che usi solo le +funzioni di base viste finore, ma prenderemo in esame, oltre al comportamento +in condizioni normali, anche tutti i possibili scenari particolari (errori, +sconnessione della rete, crash del client o del server durante la connessione) +che possono avere luogo durante l'impiego di un'applicazione di rete, partendo +da una versione primitiva che dovrà essere rimaneggiata di volta in volta per +poter tenere conto di tutte le evenienze che si possono manifestare nella vita +reale di un'applicazione di rete, fino ad arrivare ad un'implementazione +completa. + + +\subsection{Il servizio \textit{echo}} +\label{sec:TCP_echo} + + +Nella ricerca di un servizio che potesse fare da esempio per una comunicazione +bidirezionale, si è deciso, seguendo la scelta di Stevens in \cite{UNP1}, di +usare il servizio \textit{echo}, che si limita a restituire in uscita quanto +immesso in ingresso. Infatti, nonostante la sua estrema semplicità, questo +servizio costituisce il prototipo ideale per una generica applicazione di rete +in cui un server risponde alle richieste di un client. Nel caso di una +applicazione più complessa quello che si potrà avere in più è una elaborazione +dell'input del client, che in molti casi viene interpretato come un comando, +da parte di un server che risponde fornendo altri dati in uscita. + +Il servizio \textit{echo} è uno dei servizi standard solitamente provvisti +direttamente dal superserver \cmd{inetd}, ed è definito +dall'\href{http://www.ietf.org/rfc/rfc0862.txt}{RFC~862}. Come dice il nome il +servizio deve rimandare indietro sulla connessione i dati che gli vengono +inviati; l'RFC descrive le specifiche sia per TCP che UDP, e per il primo +stabilisce che una volta stabilita la connessione ogni dato in ingresso deve +essere rimandato in uscita, fintanto che il chiamante non ha chiude la +connessione; il servizio opera sulla porta 7. + +Nel nostro caso l'esempio sarà costituito da un client che legge una linea di +caratteri dallo standard input e la scrive sul server. A sua volta il server +leggerà la linea dalla connessione e la riscriverà immutata all'indietro. Sarà +compito del client leggere la risposta del server e stamparla sullo standard +output. + \subsection{Il client: prima versione} \label{sec:TCP_echo_client} -Il codice del client è riportato in \figref{fig:TCPsimpl_client_elem}, anche -esso ricalca la struttura del precedente client per il servizio -\texttt{daytime} (vedi \secref{sec:TCP_daytime_client}) ma, come per il -server, lo si è diviso in due parti, inserendo la parte relativa alle -operazioni specifiche previste per il protocollo \textit{echo} in una funzione -a parte. +Il codice della prima versione client per il servizio \textit{echo}, +diponibile nel file \file{TCP_echo1.c}, è riportato in +\figref{fig:TCP_echo_client_1}. Esso ricalca la struttura del precedente +client per il servizio \textit{daytime} (vedi +\secref{sec:TCP_daytime_client}), e la prima parte (\texttt{\small 10--27}) è +sostanzialmente identica, a parte l'uso di una porta diversa. \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15.6 cm} - \includecodesample{listati/EchoServerWrong.c} + \includecodesample{listati/TCP_echo_client.c} \end{minipage} \normalsize \caption{Codice della prima versione del client \textit{echo}.} - \label{fig:TCPsimpl_client_elem} + \label{fig:TCP_echo_client_1} \end{figure} -La funzione \code{main} si occupa della creazione del socket e della -connessione (linee \texttt{\small 10--27}) secondo la stessa modalità spiegata -in \secref{sec:TCP_daytime_client}, il client si connette sulla porta 7 -all'indirizzo specificato dalla linea di comando (a cui si è aggiunta una -elementare gestione delle opzioni non riportata in figura). - -Completata la connessione, al ritorno di \func{connect}, la funzione -\code{ClientEcho}, riportata in \figref{fig:TCPsimpl_client_echo_sub}, si -preoccupa di gestire la comunicazione, leggendo una riga alla volta dallo -\file{stdin}, scrivendola sul socket e ristampando su \file{stdout} quanto -ricevuto in risposta dal server. +Al solito si è tralasciata la sezione relativa alla gestione delle opzioni a +riga di comando; una volta dichiarate le variabili, si prosegue +(\texttt{\small 10--13}) con della creazione del socket, con l'usuale +controllo degli errori, la preparazione (\texttt{\small 14--17}) della +struttura dell'indirizzo, che usa la porta 7 riservata al servizio +\textit{echo}, l'indirizzo specificato a riga di comando appositamente +convertito (\texttt{\small 18--22}). Una volta inizializzato l'indirizzo si si +può eseguire (\texttt{\small 23--27}) la connessione al server secondo la +stessa modalità usata in \secref{sec:TCP_daytime_client}. + +Completata la connessione, al ritorno di \func{connect}, per gestire il +funzionamento del protocollo si usa la funzione \code{ClientEcho}, il cui +codice si è riportato a parte in \figref{fig:TCPsimpl_client_echo_sub}. Questa +si preoccupa di gestire tutta la comunicazione, leggendo una riga alla volta +dallo standard input \file{stdin}, scrivendola sul socket e ristampando su +\file{stdout} quanto ricevuto in risposta dal server. Al ritorno dalla +funzione (\texttt{\small 30--31}) anche il programma termina. + +La funzione \code{ClientEcho} utilizza due buffer (\texttt{\small 3}) per +gestire i dati inviati e letti sul socket. La comunicazione viene gestita +all'interno di un ciclo (\texttt{\small 5--10}), i dati da inviare sulla +connessione vengono presi dallo \file{stdin} usando la funzione \func{fgets}, +che legge una linea di testo (terminata da un \texttt{CR} e fino al massimo di +\const{MAXLINE} caratteri) e la salva sul buffer di invio. + +Si usa poi (\texttt{\small 6}) la funzione \func{FullWrite}, vista in +\secref{sec:sock_io_behav}, per scrivere i dati sul socket, gestendo +automaticamente l'invio multiplo qualora una singola \func{write} non sia +sufficiente. I dati vengono riletti indietro (\texttt{\small 7}) con una +\func{FullRead} sul buffer di ricezione e viene inserita (\texttt{\small 8}) +la terminazione della stringa e per poter usare (\texttt{\small 9}) la +funzione \func{fputs} per scriverli su \file{stdout}. \begin{figure}[!htb] \footnotesize \centering @@ -1647,44 +1695,17 @@ ricevuto in risposta dal server. \label{fig:TCPsimpl_client_echo_sub} \end{figure} -La funzione utilizza due buffer per gestire i dati inviati e letti sul socket -(\texttt{\small 3}). La comunicazione viene gestita all'interno di un ciclo -(linee \texttt{\small 5--10}), i dati da inviare sulla connessione vengono -presi dallo \file{stdin} usando la funzione \func{fgets} che legge una -linea di testo (terminata da un \texttt{CR} e fino al massimo di -\const{MAXLINE} caratteri) e la salva sul buffer di invio, la funzione -\func{FullWrite} (\texttt{\small 3}) scrive detti dati sul socket (gestendo -l'invio multiplo qualora una singola \func{write} non basti, come spiegato -in \secref{sec:sock_io_behav}). - -I dati che vengono riletti indietro con una \func{FullRead} sul buffer di -ricezione e viene inserita la terminazione della stringa (\texttt{\small - 7--8}) e per poter usare la funzione \func{fputs} per scriverli su -\file{stdout}. +Quando si conluderà l'invio di dati mandando un end-of-file sullo standard +inputo (ad esempio con la pressione di \texttt{C-d}) si avrà il ritorno di +\func{fgets} con un puntatore nullo (si riveda quanto spiegato in +\secref{sec:file_line_io}) e la conseguente uscita dal ciclo; al che la +subroutine ritorna ed il nostro programma client termina. -Un end of file inviato su \file{stdin} causa il ritorno di \func{fgets} -con un puntatore nullo e l'uscita dal ciclo, al che la subroutine ritorna ed -il client esce. - -\subsection{La struttura del server} +\subsection{Il server: prima versione} \label{sec:TCPsimp_server_main} -Il servizio \textit{echo} è uno dei servizi standard solitamente provvisti -direttamente dal superserver \cmd{inetd}, ed è definito -dall'\href{http://www.ietf.org/rfc/rfc0862.txt}{RFC~862}. Come dice il nome il -servizio deve rimandare indietro sulla connessione i dati che gli vengono -inviati; l'RFC descrive le specifiche sia per TCP che UDP, e per il primo -stabilisce che una volta stabilita la connessione ogni dato in ingresso deve -essere rimandato in uscita, fintanto che il chiamante non ha chiude la -connessione; il servizio opera sulla porta 7. - -Nel nostro caso l'esempio sarà costituito da un client che legge una linea di -caratteri dallo standard input e la scrive sul server, il server leggerà la -linea dalla connessione e la riscriverà all'indietro; sarà compito del client -leggere la risposta del server e stamparla sullo standard output. - -La prima versione del server, \file{ElemEchoTCPServer.c}, si compone di un +La prima versione del server, \file{TCP_echod.c}, si compone di un corpo principale, costituito dalla funzione \code{main}. Questa si incarica di creare il socket, metterlo in ascolto di connessioni in arrivo e creare un processo figlio a cui delegare la gestione di ciascuna connessione. Questa @@ -1705,7 +1726,7 @@ nel precedente esempio esaminato in \secref{sec:TCP_daytime_cunc_server}. La struttura di questa prima versione del server è sostanzialmente identica a quella dell'esempio citato, ed ad esso si applicano le considerazioni fatte in \secref{sec:TCP_daytime_cunc_server}. Le uniche differenze rispetto -all'esempio in \figref{fig:TCP_daytime_iter_server_code} sono che in questo +all'esempio in \figref{fig:TCP_daytime_cunc_server_code} sono che in questo caso per il socket in ascolto viene usata la porta 7 e che tutta la gestione della comunicazione è delegata alla funzione \code{ServEcho}. % Per ogni connessione viene creato un