\chapter{Un esempio completo di client/server TCP}
\label{cha:simple_TCP_sock}

In questo capitolo riprenderemo le funzioni trattate nel precedente, usandole
per scrivere una prima applicazione client/server che usi i socket TCP per una
comunicazione in entrambe le direzioni. 

Inoltre 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.


\section{Il servizio \texttt{echo}}
\label{sec:TCPsimp_echo}

L'applicazione scelta come esempio sarà un'implementazione elementare, ma
completa, del servizio \texttt{echo}. Il servizio \texttt{echo} è uno dei
servizi standard solitamente provvisti direttamente dal superserver
\cmd{inetd}, ed è definito dall'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.

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; tutto quello che cambia
nel caso si una applicazione più complessa è la elaborazione dell'input del
client da parte del server nel fornire le risposte in uscita.

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.

\subsection{La struttura del server}
\label{sec:TCPsimp_server_main}

La prima versione del server, \file{ElemEchoTCPServer.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
parte, riportata in \figref{fig:TCPsimpl_serv_code}, è analoga a quella vista
nel precedente esempio esaminato in \secref{sec:TCPel_cunc_serv}.

\begin{figure}[!htb]
  \footnotesize
  \begin{lstlisting}{}
/* Subroutines declaration */
void ServEcho(int sockfd);
/* Program beginning */
int main(int argc, char *argv[])
{
    int list_fd, conn_fd;
    pid_t pid;
    struct sockaddr_in serv_add;
     ...
    /* 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);
    }
    /* handle echo to client */
    while (1) {
        /* accept connection */
        if ( (conn_fd = accept(list_fd, NULL, NULL)) < 0) {
            perror("accept error");
            exit(-1);
        }
        /* fork to handle connection */
        if ( (pid = fork()) < 0 ){
            perror("fork error");
            exit(-1);
        }
        if (pid == 0) {      /* child */
            close(list_fd);          /* close listening socket */   
            SockEcho(conn_fd);       /* handle echo */
            exit(0);
        } else {             /* parent */
            close(conn_fd);          /* close connected socket */
        }
    }
    /* normal exit, never reached */
    exit(0);
}
  \end{lstlisting}
  \caption{Codice della funzione \code{main} della prima versione del server
    per il servizio \texttt{echo}.}
  \label{fig:TCPsimpl_serv_code}
\end{figure}

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:TCPel_cunc_daytime}. Le uniche differenze rispetto all'esempio in
\figref{fig:TCPel_serv_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
% processo figlio, il quale si incarica di lanciare la funzione
% \texttt{SockEcho}.

Il codice della funzione \code{ServEcho} è invece mostrata in \nfig, la
comunicazione viene gestita all'interno del ciclo (linee \texttt{\small
  6--8}).  I dati inviati dal client vengono letti dal socket con una semplice
\func{read} (che ritorna solo in presenza di dati in arrivo), la riscrittura
viene invece gestita dalla funzione \func{SockWrite} (descritta in
\figref{fig:sock_SockWrite_code}) che si incarica di tenere conto
automaticamente della possibilità che non tutti i dati di cui è richiesta la
scrittura vengano trasmessi con una singola \func{write}.

\begin{figure}[!htb]
  \footnotesize
  \begin{lstlisting}{}
void ServEcho(int sockfd) {
    char buffer[MAXLINE];
    int nread, nwrite;
    
    /* main loop, reading 0 char means client close connection */
    while ( (nread = read(sockfd, buffer, MAXLINE)) != 0) {
        nwrite = SockWrite(sockfd, buffer, nread);
    }
    return;
}
  \end{lstlisting}
  \caption{Codice della prima versione della funzione \code{ServEcho} per la
    gestione del servizio \texttt{echo}.}
  \label{fig:TCPsimpl_server_elem_sub}
\end{figure}

Quando il client chiude la connessione il ricevimento del FIN fa ritornare la
\func{read} con un numero di byte letti pari a zero, il che causa l'uscita
dal ciclo e il ritorno della funzione, che a sua volta causa la terminazione
del processo figlio.


\subsection{Il client}
\label{sec:TCPsimp_client_main}

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:net_cli_sample}) ma, come per il server, lo
si è diviso in due parti, inserendo la parte relativa alle operazioni
specifiche previste per il protocollo \texttt{echo} in una funzione a parte.
\begin{figure}[!htb]
  \footnotesize
  \begin{lstlisting}{}
int main(int argc, char *argv[])
{
/* 
 * Variables definition  
 */
    int sock_fd, i;
    struct sockaddr_in serv_add;
    ...
    /* 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(7);                    /* echo port is 7 */
    /* 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 */
    ClientEcho(stdin, sock_fd);
    /* normal exit */
    return 0;
}
  \end{lstlisting}
  \caption{Codice della prima versione del client \texttt{echo}.}
  \label{fig:TCPsimpl_client_elem}
\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:net_cli_sample}, 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 ritrno fiììdi  \func{connect} è ritornata, 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.

\begin{figure}[!htb]
  \footnotesize
  \begin{lstlisting}{}
void ClientEcho(FILE * filein, int socket) 
{
    char sendbuff[MAXLINE], recvbuff[MAXLINE];
    int nread; 
    while (fgets(sendbuff, MAXLINE, filein) != NULL) {
        SockWrite(socket, sendbuff, strlen(sendbuff)); 
        nread = SockRead(socket, recvbuff, strlen(sendbuff));        
        recvbuff[nread] = 0;
        fputs(recvbuff, stdout);
    }
    return;
}
  \end{lstlisting}
  \caption{Codice della prima versione della funzione \texttt{ClientEcho} per 
    la gestione del servizio \texttt{echo}.}
  \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
\macro{MAXLINE} caratteri) e la salva sul buffer di invio, la funzione
\func{SockWrite} (\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{SockRead} 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}. 

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.


\section{Il funzionamento del servizio}
\label{sec:TCPsimpl_normal_work}

Benché il codice dell'esempio precedente sia molto ridotto, esso ci permetterà
di considerare in dettaglio tutte le problematiche che si possono incontrare
nello scrivere un'applicazione di rete. Infatti attraverso l'esame delle sue
modalità di funzionamento normali, all'avvio e alla terminazione, e di quello
che avviene nelle varie situazioni limite, da una parte potremo approfondire
la comprensione del protocollo TCP/IP e dall'altra ricavare le indicazioni
necessarie per essere in grado di scrivere applicazioni robuste, in grado di
gestire anche i casi limite.


\subsection{L'avvio e il funzionamento}
\label{sec:TCPsimpl_startup}

Il primo passo è compilare e lanciare il server (da root, per poter usare la
porta 7 che è riservata), alla partenza esso eseguirà l'apertura passiva con
la sequenza delle chiamate a \func{socket}, \func{bind}, \func{listen} e poi
si bloccherà nella \func{accept}. A questo punto si potrà controllarne lo
stato con \cmd{netstat}:
\begin{verbatim}
[piccardi@roke piccardi]$ netstat -at
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State 
...
tcp        0      0 *:echo                  *:*                     LISTEN
...
\end{verbatim} %$
che ci mostra come il socket sia in ascolto sulla porta richiesta, accettando
connessioni da qualunque indirizzo e da qualunque porta e su qualunque
interfaccia locale.

A questo punto si può lanciare il client, esso chiamerà \func{socket} e
\func{connect}; una volta completato il three way handshake la connessione è
stabilita; la \func{connect} ritornerà nel client\footnote{si noti che è
  sempre la \func{connect} del client a ritornare per prima, in quanto
  questo avviene alla ricezione del secondo segmento (l'ACK del server) del
  three way handshake, la \func{accept} del server ritorna solo dopo
  un altro mezzo RTT quando il terzo segmento (l'ACK del client) viene
  ricevuto.} e la \func{accept} nel server, ed usando di nuovo
\cmd{netstat} otterremmo che:
\begin{verbatim}
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 *:echo                  *:*                     LISTEN
tcp        0      0 roke:echo               gont:32981              ESTABLISHED
\end{verbatim}
mentre per quanto riguarda l'esecuzione dei programmi avremo che:
\begin{itemize}
\item il client chiama la funzione \code{ClientEcho} che si blocca sulla
  \func{fgets} dato che non si è ancora scritto nulla sul terminale.
\item il server eseguirà una \func{fork} facendo chiamare al processo figlio
  la funzione \code{ServEcho}, quest'ultima si bloccherà sulla \func{read}
  dal socket sul quale ancora non sono presenti dati.
\item il processo padre del server chiamerà di nuovo \func{accept}
  bloccandosi fino all'arrivo di un'altra connessione.
\end{itemize}
e se usiamo il comando \cmd{ps} per esaminare lo stato dei processi otterremo
un risultato del tipo:
\begin{verbatim}
[piccardi@roke piccardi]$ ps ax
  PID TTY      STAT   TIME COMMAND
 ...  ...      ...    ...  ...
 2356 pts/0    S      0:00 ./echod
 2358 pts/1    S      0:00 ./echo 127.0.0.1
 2359 pts/0    S      0:00 ./echod
\end{verbatim} %$
(dove si sono cancellate le righe inutili) da cui si evidenzia la presenza di
tre processi, tutti in stato di \textit{sleep} (vedi
\tabref{tab:proc_proc_states}).

Se a questo punto si inizia a scrivere qualcosa sul client non sarà trasmesso
niente fin tanto che non si prema il tasto di a capo (si ricordi quanto detto
in \secref{sec:file_line_io} a proposito dell'I/O su terminale), solo allora
\func{fgets} ritornerà ed il client scriverà quanto immesso sul socket, per
poi passare a rileggere quanto gli viene inviato all'indietro dal server, che
a sua volta sarà inviato sullo standard output, che nel caso ne provoca
l'immediatamente stampa a video.


\subsection{La conclusione normale}
\label{sec:TCPsimpl_conclusion}

Tutto quello che scriveremo sul client sarà rimandato indietro dal server e
ristampato a video fintanto che non concluderemo l'immissione dei dati; una
sessione tipica sarà allora del tipo: 
\begin{verbatim}
[piccardi@roke sources]$ ./echo 127.0.0.1
Questa e` una prova
Questa e` una prova
Ho finito
Ho finito
\end{verbatim} %$
che termineremo inviando un EOF dal terminale (usando la combinazione di tasti
ctrl-D, che non compare a schermo); se eseguiamo un \cmd{netstat} a questo
punto avremo:
\begin{verbatim}
[piccardi@roke piccardi]$ netstat -at 
tcp        0      0 *:echo                  *:*                     LISTEN
tcp        0      0 localhost:33032         localhost:echo          TIME_WAIT
\end{verbatim} %$
con il client che entra in \texttt{TIME\_WAIT}.

Esaminiamo allora in dettaglio la sequenza di eventi che porta alla
terminazione normale della connessione, che ci servirà poi da riferimento
quando affronteremo il comportamento in caso di conclusioni anomale:

\begin{enumerate}
\item inviando un carattere di EOF da terminale la \func{fgets} ritorna
  restituendo un puntatore nullo che causa l'uscita dal ciclo di
  \code{while}, così la \code{ClientEcho} ritorna.
\item al ritorno di \code{ClientEcho} ritorna anche la funzione \code{main}, e
  come parte del processo terminazione tutti i file descriptor vengono chiusi
  (si ricordi quanto detto in \secref{sec:proc_term_conclusion}); questo causa
  la chiusura del socket di comunicazione; il client allora invierà un FIN al
  server a cui questo risponderà con un ACK.  A questo punto il client verrà a
  trovarsi nello stato \texttt{FIN\_WAIT\_2} ed il server nello stato
  \texttt{CLOSE\_WAIT} (si riveda quanto spiegato in
  \secref{sec:TCPel_conn_term}).
\item quando il server riceve il FIN la \func{read} del processo figlio che
  gestisce la connessione ritorna restituendo 0 causando così l'uscita dal
  ciclo e il ritorno di \code{ServEcho}, a questo punto il processo figlio
  termina chiamando \func{exit}.
\item all'uscita del figlio tutti i file descriptor vengono chiusi, la
  chiusura del socket connesso fa sì che venga effettuata la sequenza finale
  di chiusura della connessione, viene emesso un FIN dal server che riceverà
  un ACK dal client, a questo punto la connessione è conclusa e il client
  resta nello stato \texttt{TIME\_WAIT}.

\end{enumerate}


\subsection{La gestione dei processi figli}
\label{sec:TCPsimpl_child_hand}

Tutto questo riguarda la connessione, c'è però da tenere conto dell'effetto
del procedimento di chiusura del processo figlio nel server (si veda quanto
esaminato in \secref{sec:proc_termination}). In questo caso avremo l'invio del
segnale \macro{SIGCHLD} al padre, ma dato che non si è installato un
manipolatore e che l'azione predefinita per questo segnale è quella di essere
ignorato, non avendo predisposto la ricezione dello stato di terminazione,
otterremo che il processo figlio entrerà nello stato di zombie (si riveda
quanto illustrato in \secref{sec:sig_sigchld}), come risulterà ripetendo il
comando \cmd{ps}:
\begin{verbatim}
 2356 pts/0    S      0:00 ./echod
 2359 pts/0    Z      0:00 [echod <defunct>]
\end{verbatim}

Poiché non è possibile lasciare processi zombie che pur inattivi occupano
spazio nella tabella dei processi e a lungo andare saturerebbero le risorse
del kernel, occorrerà ricevere opportunamente lo stato di terminazione del
processo (si veda \secref{sec:proc_wait}), cosa che faremo utilizzando
\macro{SIGCHLD} secondo quanto illustrato in \secref{sec:sig_sigchld}.

La prima modifica al nostro server è pertanto quella di inserire la gestione
della terminazione dei processi figli attraverso l'uso di un manipolatore.
Per questo useremo la funzione \code{Signal}, illustrata in
\figref{fig:sig_Signal_code}, per installare il semplice manipolatore che
riceve i segnali dei processi figli terminati già visto in 
\figref{fig:sig_sigchld_handl}; aggiungendo il seguente codice:
\begin{lstlisting}{}
    ...
    /* install SIGCHLD handler */
    Signal(SIGCHLD, sigchld_hand);  /* establish handler */
    /* create socket */
    ...
\end{lstlisting}

\noindent
all'esempio illustrato in \figref{fig:TCPsimpl_serv_code}, e linkando il tutto
alla funzione \code{sigchld\_hand}, si risolverà completamente il problema
degli zombie.



%%% Local Variables: 
%%% mode: latex
%%% TeX-master: "gapil"
%%% End: 
