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