Risistemata flock, aggiunta figura sulla struttura del sistema
[gapil.git] / simpltcp.tex
1 \chapter{Un esempio completo di client/server TCP}
2 \label{cha:simple_TCP_sock}
3
4 In questo capitolo riprenderemo le funzioni trattate nel precedente, usandole
5 per scrivere una prima applicazione client/server che usi i socket TCP per una
6 comunicazione in entrambe le direzioni. 
7
8 Inoltre prenderemo in esame, oltre al comportamento in condizioni normali,
9 anche tutti i possibili scenari particolari (errori, sconnessione della rete,
10 crash del client o del server durante la connessione) che possono avere luogo
11 durante l'impiego di un'applicazione di rete.
12
13
14 \section{Il servizio \texttt{echo}}
15 \label{sec:TCPsimp_echo}
16
17 L'applicazione scelta come esempio sarà un'implementazione elementare, ma
18 completa, del servizio \texttt{echo}. Il servizio \texttt{echo} è uno dei
19 servizi standard solitamente provvisti direttamente dal superserver
20 \cmd{inetd}, ed è definito dall'RFC~862. Come dice il nome il servizio deve
21 rimandare indietro sulla connessione i dati che gli vengono inviati; l'RFC
22 descrive le specifiche sia per TCP che UDP, e per il primo stabilisce che una
23 volta stabilita la connessione ogni dato in ingresso deve essere rimandato in
24 uscita, fintanto che il chiamante non ha chiude la connessione; il servizio
25 opera sulla porta 7.
26
27 Nel nostro caso l'esempio sarà costituito da un client che legge una linea di
28 caratteri dallo standard input e la scrive sul server, il server leggerà la
29 linea dalla connessione e la riscriverà all'indietro; sarà compito del client
30 leggere la risposta del server e stamparla sullo standard output.
31
32 Si è scelto di usare questo servizio, seguendo l'esempio di \cite{UNP1},
33 perché costituisce il prototipo ideale di una generica applicazione di rete in
34 cui un server risponde alle richieste di un client; tutto quello che cambia
35 nel caso si una applicazione più complessa è la elaborazione dell'input del
36 client da parte del server nel fornire le risposte in uscita.
37
38 Partiremo da un'implementazione elementare che dovrà essere rimaneggiata di
39 volta in volta per poter tenere conto di tutte le evenienze che si possono
40 manifestare nella vita reale di un'applicazione di rete, fino ad arrivare ad
41 un'implementazione completa.
42
43 \subsection{La struttura del server}
44 \label{sec:TCPsimp_server_main}
45
46 La prima versione del server, \file{ElemEchoTCPServer.c}, si compone di un
47 corpo principale, costituito dalla funzione \code{main}.  Questa si incarica
48 di creare il socket, metterlo in ascolto di connessioni in arrivo e creare un
49 processo figlio a cui delegare la gestione di ciascuna connessione.  Questa
50 parte, riportata in \figref{fig:TCPsimpl_serv_code}, è analoga a quella vista
51 nel precedente esempio esaminato in \secref{sec:TCPel_cunc_serv}.
52
53 \begin{figure}[!htb]
54   \footnotesize
55   \begin{lstlisting}{}
56 /* Subroutines declaration */
57 void ServEcho(int sockfd);
58 /* Program beginning */
59 int main(int argc, char *argv[])
60 {
61     int list_fd, conn_fd;
62     pid_t pid;
63     struct sockaddr_in serv_add;
64      ...
65     /* create socket */
66     if ( (list_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
67         perror("Socket creation error");
68         exit(-1);
69     }
70     /* initialize address */
71     memset((void *)&serv_add, 0, sizeof(serv_add)); /* clear server address */
72     serv_add.sin_family = AF_INET;                  /* address type is INET */
73     serv_add.sin_port = htons(13);                  /* daytime port is 13 */
74     serv_add.sin_addr.s_addr = htonl(INADDR_ANY);   /* connect from anywhere */
75     /* bind socket */
76     if (bind(list_fd, (struct sockaddr *)&serv_add, sizeof(serv_add)) < 0) {
77         perror("bind error");
78         exit(-1);
79     }
80     /* listen on socket */
81     if (listen(list_fd, BACKLOG) < 0 ) {
82         perror("listen error");
83         exit(-1);
84     }
85     /* handle echo to client */
86     while (1) {
87         /* accept connection */
88         if ( (conn_fd = accept(list_fd, NULL, NULL)) < 0) {
89             perror("accept error");
90             exit(-1);
91         }
92         /* fork to handle connection */
93         if ( (pid = fork()) < 0 ){
94             perror("fork error");
95             exit(-1);
96         }
97         if (pid == 0) {      /* child */
98             close(list_fd);          /* close listening socket */   
99             SockEcho(conn_fd);       /* handle echo */
100             exit(0);
101         } else {             /* parent */
102             close(conn_fd);          /* close connected socket */
103         }
104     }
105     /* normal exit, never reached */
106     exit(0);
107 }
108   \end{lstlisting}
109   \caption{Codice della funzione \code{main} della prima versione del server
110     per il servizio \texttt{echo}.}
111   \label{fig:TCPsimpl_serv_code}
112 \end{figure}
113
114 La struttura di questa prima versione del server è sostanzialmente identica a
115 quella dell'esempio citato, ed ad esso si applicano le considerazioni fatte in
116 \secref{sec:TCPel_cunc_daytime}. Le uniche differenze rispetto all'esempio in
117 \figref{fig:TCPel_serv_code} sono che in questo caso per il socket in ascolto
118 viene usata la porta 7 e che tutta la gestione della comunicazione è delegata
119 alla funzione \code{ServEcho}.
120 %  Per ogni connessione viene creato un
121 % processo figlio, il quale si incarica di lanciare la funzione
122 % \texttt{SockEcho}.
123
124 Il codice della funzione \code{ServEcho} è invece mostrata in
125 \figref{fig:TCPsimpl_server_elem_sub}, la comunicazione viene gestita
126 all'interno del ciclo (linee \texttt{\small 6--8}).  I dati inviati dal client
127 vengono letti dal socket con una semplice \func{read} (che ritorna solo in
128 presenza di dati in arrivo), la riscrittura viene invece gestita dalla
129 funzione \func{SockWrite} (descritta in \figref{fig:sock_SockWrite_code}) che
130 si incarica di tenere conto automaticamente della possibilità che non tutti i
131 dati di cui è richiesta la scrittura vengano trasmessi con una singola
132 \func{write}.
133
134 \begin{figure}[!htb]
135   \footnotesize
136   \begin{lstlisting}{}
137 void ServEcho(int sockfd) {
138     char buffer[MAXLINE];
139     int nread, nwrite;
140     
141     /* main loop, reading 0 char means client close connection */
142     while ( (nread = read(sockfd, buffer, MAXLINE)) != 0) {
143         nwrite = SockWrite(sockfd, buffer, nread);
144     }
145     return;
146 }
147   \end{lstlisting}
148   \caption{Codice della prima versione della funzione \code{ServEcho} per la
149     gestione del servizio \texttt{echo}.}
150   \label{fig:TCPsimpl_server_elem_sub}
151 \end{figure}
152
153 Quando il client chiude la connessione il ricevimento del FIN fa ritornare la
154 \func{read} con un numero di byte letti pari a zero, il che causa l'uscita
155 dal ciclo e il ritorno della funzione, che a sua volta causa la terminazione
156 del processo figlio.
157
158
159 \subsection{Il client}
160 \label{sec:TCPsimp_client_main}
161
162 Il codice del client è riportato in \figref{fig:TCPsimpl_client_elem}, anche
163 esso ricalca la struttura del precedente client per il servizio
164 \texttt{daytime} (vedi \secref{sec:net_cli_sample}) ma, come per il server, lo
165 si è diviso in due parti, inserendo la parte relativa alle operazioni
166 specifiche previste per il protocollo \texttt{echo} in una funzione a parte.
167 \begin{figure}[!htb]
168   \footnotesize
169   \begin{lstlisting}{}
170 int main(int argc, char *argv[])
171 {
172 /* 
173  * Variables definition  
174  */
175     int sock_fd, i;
176     struct sockaddr_in serv_add;
177     ...
178     /* create socket */
179     if ( (sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
180         perror("Socket creation error");
181         return -1;
182     }
183     /* initialize address */
184     memset((void *) &serv_add, 0, sizeof(serv_add)); /* clear server address */
185     serv_add.sin_family = AF_INET;                   /* address type is INET */
186     serv_add.sin_port = htons(7);                    /* echo port is 7 */
187     /* build address using inet_pton */
188     if ( (inet_pton(AF_INET, argv[optind], &serv_add.sin_addr)) <= 0) {
189         perror("Address creation error");
190         return -1;
191     }
192     /* extablish connection */
193     if (connect(sock_fd, (struct sockaddr *)&serv_add, sizeof(serv_add)) < 0) {
194         perror("Connection error");
195         return -1;
196     }
197     /* read daytime from server */
198     ClientEcho(stdin, sock_fd);
199     /* normal exit */
200     return 0;
201 }
202   \end{lstlisting}
203   \caption{Codice della prima versione del client \texttt{echo}.}
204   \label{fig:TCPsimpl_client_elem}
205 \end{figure}
206
207 La funzione \code{main} si occupa della creazione del socket e della
208 connessione (linee \texttt{\small 10--27}) secondo la stessa modalità spiegata
209 in \secref{sec:net_cli_sample}, il client si connette sulla porta 7
210 all'indirizzo specificato dalla linea di comando (a cui si è aggiunta una
211 elementare gestione delle opzioni non riportata in figura).
212
213 Completata la connessione, al ritrno fiììdi  \func{connect} è ritornata, la
214 funzione \code{ClientEcho}, riportata in
215 \figref{fig:TCPsimpl_client_echo_sub}, si preoccupa di gestire la
216 comunicazione, leggendo una riga alla volta dallo \file{stdin}, scrivendola
217 sul socket e ristampando su \file{stdout} quanto ricevuto in risposta dal
218 server.
219
220 \begin{figure}[!htb]
221   \footnotesize
222   \begin{lstlisting}{}
223 void ClientEcho(FILE * filein, int socket) 
224 {
225     char sendbuff[MAXLINE], recvbuff[MAXLINE];
226     int nread; 
227     while (fgets(sendbuff, MAXLINE, filein) != NULL) {
228         SockWrite(socket, sendbuff, strlen(sendbuff)); 
229         nread = SockRead(socket, recvbuff, strlen(sendbuff));        
230         recvbuff[nread] = 0;
231         fputs(recvbuff, stdout);
232     }
233     return;
234 }
235   \end{lstlisting}
236   \caption{Codice della prima versione della funzione \texttt{ClientEcho} per 
237     la gestione del servizio \texttt{echo}.}
238   \label{fig:TCPsimpl_client_echo_sub}
239 \end{figure}
240
241 La funzione utilizza due buffer per gestire i dati inviati e letti sul socket
242 (\texttt{\small 3}).  La comunicazione viene gestita all'interno di un ciclo
243 (linee \texttt{\small 5--10}), i dati da inviare sulla connessione vengono
244 presi dallo \file{stdin} usando la funzione \func{fgets} che legge una
245 linea di testo (terminata da un \texttt{CR} e fino al massimo di
246 \macro{MAXLINE} caratteri) e la salva sul buffer di invio, la funzione
247 \func{SockWrite} (\texttt{\small 3}) scrive detti dati sul socket (gestendo
248 l'invio multiplo qualora una singola \func{write} non basti, come spiegato
249 in \secref{sec:sock_io_behav}).
250
251 I dati che vengono riletti indietro con una \func{SockRead} sul buffer di
252 ricezione e viene inserita la terminazione della stringa (\texttt{\small
253   7--8}) e per poter usare la funzione \func{fputs} per scriverli su
254 \file{stdout}. 
255
256 Un end of file inviato su \file{stdin} causa il ritorno di \func{fgets}
257 con un puntatore nullo e l'uscita dal ciclo, al che la subroutine ritorna ed
258 il client esce.
259
260
261 \section{Il funzionamento del servizio}
262 \label{sec:TCPsimpl_normal_work}
263
264 Benché il codice dell'esempio precedente sia molto ridotto, esso ci permetterà
265 di considerare in dettaglio tutte le problematiche che si possono incontrare
266 nello scrivere un'applicazione di rete. Infatti attraverso l'esame delle sue
267 modalità di funzionamento normali, all'avvio e alla terminazione, e di quello
268 che avviene nelle varie situazioni limite, da una parte potremo approfondire
269 la comprensione del protocollo TCP/IP e dall'altra ricavare le indicazioni
270 necessarie per essere in grado di scrivere applicazioni robuste, in grado di
271 gestire anche i casi limite.
272
273
274 \subsection{L'avvio e il funzionamento}
275 \label{sec:TCPsimpl_startup}
276
277 Il primo passo è compilare e lanciare il server (da root, per poter usare la
278 porta 7 che è riservata), alla partenza esso eseguirà l'apertura passiva con
279 la sequenza delle chiamate a \func{socket}, \func{bind}, \func{listen} e poi
280 si bloccherà nella \func{accept}. A questo punto si potrà controllarne lo
281 stato con \cmd{netstat}:
282 \begin{verbatim}
283 [piccardi@roke piccardi]$ netstat -at
284 Active Internet connections (servers and established)
285 Proto Recv-Q Send-Q Local Address           Foreign Address         State 
286 ...
287 tcp        0      0 *:echo                  *:*                     LISTEN
288 ...
289 \end{verbatim} %$
290 che ci mostra come il socket sia in ascolto sulla porta richiesta, accettando
291 connessioni da qualunque indirizzo e da qualunque porta e su qualunque
292 interfaccia locale.
293
294 A questo punto si può lanciare il client, esso chiamerà \func{socket} e
295 \func{connect}; una volta completato il three way handshake la connessione è
296 stabilita; la \func{connect} ritornerà nel client\footnote{si noti che è
297   sempre la \func{connect} del client a ritornare per prima, in quanto
298   questo avviene alla ricezione del secondo segmento (l'ACK del server) del
299   three way handshake, la \func{accept} del server ritorna solo dopo
300   un altro mezzo RTT quando il terzo segmento (l'ACK del client) viene
301   ricevuto.} e la \func{accept} nel server, ed usando di nuovo
302 \cmd{netstat} otterremmo che:
303 \begin{verbatim}
304 Active Internet connections (servers and established)
305 Proto Recv-Q Send-Q Local Address           Foreign Address         State
306 tcp        0      0 *:echo                  *:*                     LISTEN
307 tcp        0      0 roke:echo               gont:32981              ESTABLISHED
308 \end{verbatim}
309 mentre per quanto riguarda l'esecuzione dei programmi avremo che:
310 \begin{itemize}
311 \item il client chiama la funzione \code{ClientEcho} che si blocca sulla
312   \func{fgets} dato che non si è ancora scritto nulla sul terminale.
313 \item il server eseguirà una \func{fork} facendo chiamare al processo figlio
314   la funzione \code{ServEcho}, quest'ultima si bloccherà sulla \func{read}
315   dal socket sul quale ancora non sono presenti dati.
316 \item il processo padre del server chiamerà di nuovo \func{accept}
317   bloccandosi fino all'arrivo di un'altra connessione.
318 \end{itemize}
319 e se usiamo il comando \cmd{ps} per esaminare lo stato dei processi otterremo
320 un risultato del tipo:
321 \begin{verbatim}
322 [piccardi@roke piccardi]$ ps ax
323   PID TTY      STAT   TIME COMMAND
324  ...  ...      ...    ...  ...
325  2356 pts/0    S      0:00 ./echod
326  2358 pts/1    S      0:00 ./echo 127.0.0.1
327  2359 pts/0    S      0:00 ./echod
328 \end{verbatim} %$
329 (dove si sono cancellate le righe inutili) da cui si evidenzia la presenza di
330 tre processi, tutti in stato di \textit{sleep} (vedi
331 \tabref{tab:proc_proc_states}).
332
333 Se a questo punto si inizia a scrivere qualcosa sul client non sarà trasmesso
334 niente fin tanto che non si prema il tasto di a capo (si ricordi quanto detto
335 in \secref{sec:file_line_io} a proposito dell'I/O su terminale), solo allora
336 \func{fgets} ritornerà ed il client scriverà quanto immesso sul socket, per
337 poi passare a rileggere quanto gli viene inviato all'indietro dal server, che
338 a sua volta sarà inviato sullo standard output, che nel caso ne provoca
339 l'immediatamente stampa a video.
340
341
342 \subsection{La conclusione normale}
343 \label{sec:TCPsimpl_conclusion}
344
345 Tutto quello che scriveremo sul client sarà rimandato indietro dal server e
346 ristampato a video fintanto che non concluderemo l'immissione dei dati; una
347 sessione tipica sarà allora del tipo: 
348 \begin{verbatim}
349 [piccardi@roke sources]$ ./echo 127.0.0.1
350 Questa e` una prova
351 Questa e` una prova
352 Ho finito
353 Ho finito
354 \end{verbatim} %$
355 che termineremo inviando un EOF dal terminale (usando la combinazione di tasti
356 ctrl-D, che non compare a schermo); se eseguiamo un \cmd{netstat} a questo
357 punto avremo:
358 \begin{verbatim}
359 [piccardi@roke piccardi]$ netstat -at 
360 tcp        0      0 *:echo                  *:*                     LISTEN
361 tcp        0      0 localhost:33032         localhost:echo          TIME_WAIT
362 \end{verbatim} %$
363 con il client che entra in \texttt{TIME\_WAIT}.
364
365 Esaminiamo allora in dettaglio la sequenza di eventi che porta alla
366 terminazione normale della connessione, che ci servirà poi da riferimento
367 quando affronteremo il comportamento in caso di conclusioni anomale:
368
369 \begin{enumerate}
370 \item inviando un carattere di EOF da terminale la \func{fgets} ritorna
371   restituendo un puntatore nullo che causa l'uscita dal ciclo di
372   \code{while}, così la \code{ClientEcho} ritorna.
373 \item al ritorno di \code{ClientEcho} ritorna anche la funzione \code{main}, e
374   come parte del processo terminazione tutti i file descriptor vengono chiusi
375   (si ricordi quanto detto in \secref{sec:proc_term_conclusion}); questo causa
376   la chiusura del socket di comunicazione; il client allora invierà un FIN al
377   server a cui questo risponderà con un ACK.  A questo punto il client verrà a
378   trovarsi nello stato \texttt{FIN\_WAIT\_2} ed il server nello stato
379   \texttt{CLOSE\_WAIT} (si riveda quanto spiegato in
380   \secref{sec:TCPel_conn_term}).
381 \item quando il server riceve il FIN la \func{read} del processo figlio che
382   gestisce la connessione ritorna restituendo 0 causando così l'uscita dal
383   ciclo e il ritorno di \code{ServEcho}, a questo punto il processo figlio
384   termina chiamando \func{exit}.
385 \item all'uscita del figlio tutti i file descriptor vengono chiusi, la
386   chiusura del socket connesso fa sì che venga effettuata la sequenza finale
387   di chiusura della connessione, viene emesso un FIN dal server che riceverà
388   un ACK dal client, a questo punto la connessione è conclusa e il client
389   resta nello stato \texttt{TIME\_WAIT}.
390
391 \end{enumerate}
392
393
394 \subsection{La gestione dei processi figli}
395 \label{sec:TCPsimpl_child_hand}
396
397 Tutto questo riguarda la connessione, c'è però da tenere conto dell'effetto
398 del procedimento di chiusura del processo figlio nel server (si veda quanto
399 esaminato in \secref{sec:proc_termination}). In questo caso avremo l'invio del
400 segnale \macro{SIGCHLD} al padre, ma dato che non si è installato un
401 manipolatore e che l'azione predefinita per questo segnale è quella di essere
402 ignorato, non avendo predisposto la ricezione dello stato di terminazione,
403 otterremo che il processo figlio entrerà nello stato di zombie (si riveda
404 quanto illustrato in \secref{sec:sig_sigchld}), come risulterà ripetendo il
405 comando \cmd{ps}:
406 \begin{verbatim}
407  2356 pts/0    S      0:00 ./echod
408  2359 pts/0    Z      0:00 [echod <defunct>]
409 \end{verbatim}
410
411 Poiché non è possibile lasciare processi zombie che pur inattivi occupano
412 spazio nella tabella dei processi e a lungo andare saturerebbero le risorse
413 del kernel, occorrerà ricevere opportunamente lo stato di terminazione del
414 processo (si veda \secref{sec:proc_wait}), cosa che faremo utilizzando
415 \macro{SIGCHLD} secondo quanto illustrato in \secref{sec:sig_sigchld}.
416
417 La prima modifica al nostro server è pertanto quella di inserire la gestione
418 della terminazione dei processi figli attraverso l'uso di un manipolatore.
419 Per questo useremo la funzione \code{Signal}, illustrata in
420 \figref{fig:sig_Signal_code}, per installare il semplice manipolatore che
421 riceve i segnali dei processi figli terminati già visto in 
422 \figref{fig:sig_sigchld_handl}; aggiungendo il seguente codice:
423 \begin{lstlisting}{}
424     ...
425     /* install SIGCHLD handler */
426     Signal(SIGCHLD, sigchld_hand);  /* establish handler */
427     /* create socket */
428     ...
429 \end{lstlisting}
430
431 \noindent
432 all'esempio illustrato in \figref{fig:TCPsimpl_serv_code}, e linkando il tutto
433 alla funzione \code{sigchld\_hand}, si risolverà completamente il problema
434 degli zombie.
435
436
437
438 %%% Local Variables: 
439 %%% mode: latex
440 %%% TeX-master: "gapil"
441 %%% End: