X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=tcpsock.tex;h=1a156f7ee2ad13087dd82a7d38286bf138617fd4;hp=b78321ec3ac09e819fd227e2c10c3eaeb4c8e7fd;hb=66e83c068629844f84fe4a0d44b382f756c9ef32;hpb=eefeeaee57a56cfe1ff8c02a26c44d6f87f1368c diff --git a/tcpsock.tex b/tcpsock.tex index b78321e..1a156f7 100644 --- a/tcpsock.tex +++ b/tcpsock.tex @@ -1,9 +1,9 @@ %% tcpsock.tex %% -%% Copyright (C) 2000-2004 Simone Piccardi. Permission is granted to +%% Copyright (C) 2000-2005 Simone Piccardi. Permission is granted to %% copy, distribute and/or modify this document under the terms of the GNU Free %% Documentation License, Version 1.1 or any later version published by the -%% Free Software Foundation; with the Invariant Sections being "Prefazione", +%% Free Software Foundation; with the Invariant Sections being "Un preambolo", %% with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the %% license is included in the section entitled "GNU Free Documentation %% License". @@ -34,9 +34,11 @@ questa sezione ci concentreremo sulle modalit inizio e conclude una connessione e faremo inoltre un breve accenno al significato di alcuni dei vari \textsl{stati} ad essa associati. + \subsection{La creazione della connessione: il \textit{three way handshake}} \label{sec:TCP_conn_cre} +\index{\textit{three~way~handshake}|(} Il processo che porta a creare una connessione TCP è chiamato \textit{three way handshake}; la successione tipica degli eventi (e dei \textsl{segmenti}\footnote{Si ricordi che il segmento è l'unità elementare di @@ -116,6 +118,8 @@ aspetta di ricevere con il pacchetto successivo; dato che il primo pacchetto SYN consuma un byte, nel \textit{three way handshake} il numero di acknowledge è sempre pari al numero di sequenza iniziale incrementato di uno; lo stesso varrà anche (vedi fig.~\ref{fig:TCP_close}) per l'acknowledgement di un FIN. +\index{\textit{three~way~handshake}|)} + \subsection{Le opzioni TCP.} \label{sec:TCP_TCP_opt} @@ -705,9 +709,9 @@ Si noti che si \const{INADDR\_ANY}, anche se, essendo questo nullo, il riordinamento è inutile. Si tenga presente comunque che tutte le costanti \val{INADDR\_} (riportate in tab.~\ref{tab:TCP_ipv4_addr}) sono definite secondo -l'\textit{endianess} della macchina, ed anche se esse possono essere -invarianti rispetto all'ordinamento dei bit, è comunque buona norma usare -sempre la funzione \func{htonl}. +l'\textit{endianess}\index{\textit{endianess}} della macchina, ed anche se +esse possono essere invarianti rispetto all'ordinamento dei bit, è comunque +buona norma usare sempre la funzione \func{htonl}. \begin{table}[htb] \centering @@ -754,8 +758,8 @@ connessione con un server TCP,\footnote{di nuovo la funzione limiterà ad impostare l'indirizzo dal quale e verso il quale saranno inviati e ricevuti i pacchetti, mentre per socket di tipo \texttt{SOCK\_STREAM} o \texttt{SOCK\_SEQPACKET}, essa attiverà la procedura di avvio (nel caso del - TCP il \textit{three-way-handsjake}) della connessione.} il prototipo della -funzione è il seguente: + TCP il \index{\textit{three~way~handshake}}\textit{three way handshake}) + della connessione.} il prototipo della funzione è il seguente: \begin{prototype}{sys/socket.h} {int connect(int sockfd, const struct sockaddr *servaddr, socklen\_t addrlen)} @@ -797,12 +801,12 @@ numero di porta del server a cui ci si vuole connettere, come mostrato nell'esempio sez.~\ref{sec:TCP_daytime_client}, usando le funzioni illustrate in sez.~\ref{sec:sock_addr_func}. -Nel caso di socket TCP la funzione \func{connect} avvia il \textit{three way - handshake}, e ritorna solo quando la connessione è stabilita o si è -verificato un errore. Le possibili cause di errore sono molteplici (ed i -relativi codici riportati sopra), quelle che però dipendono dalla situazione -della rete e non da errori o problemi nella chiamata della funzione sono le -seguenti: +Nel caso di socket TCP la funzione \func{connect} avvia il +\index{\textit{three~way~handshake}}\textit{three way handshake}, e ritorna +solo quando la connessione è stabilita o si è verificato un errore. Le +possibili cause di errore sono molteplici (ed i relativi codici riportati +sopra), quelle che però dipendono dalla situazione della rete e non da errori +o problemi nella chiamata della funzione sono le seguenti: \begin{enumerate} \item Il client non riceve risposta al SYN: l'errore restituito è \errcode{ETIMEDOUT}. Stevens riporta che BSD invia un primo SYN alla chiamata @@ -897,11 +901,12 @@ infatti vengono mantenute due code: \begin{enumerate} \item La coda delle connessioni incomplete (\textit{incomplete connection queue} che contiene un riferimento per ciascun socket per il quale è - arrivato un SYN ma il \textit{three way handshake} non si è ancora concluso. - Questi socket sono tutti nello stato \texttt{SYN\_RECV}. + arrivato un SYN ma il \index{\textit{three~way~handshake}}\textit{three way + handshake} non si è ancora concluso. Questi socket sono tutti nello stato + \texttt{SYN\_RECV}. \item La coda delle connessioni complete (\textit{complete connection queue} - che contiene un ingresso per ciascun socket per il quale il three way - handshake è stato completato ma ancora \func{accept} non è ritornata. + che contiene un ingresso per ciascun socket per il quale il \textit{three + way handshake} è stato completato ma ancora \func{accept} non è ritornata. Questi socket sono tutti nello stato \texttt{ESTABLISHED}. \end{enumerate} @@ -909,12 +914,13 @@ Lo schema di funzionamento quando arriva un SYN da un client il server crea una nuova voce nella coda delle connessioni incomplete, e poi risponde con il SYN$+$ACK. La voce resterà nella coda delle connessioni incomplete fino al ricevimento dell'ACK dal -client o fino ad un timeout. Nel caso di completamento del three way handshake -la voce viene spostata nella coda delle connessioni complete. Quando il -processo chiama la funzione \func{accept} (vedi sez.~\ref{sec:TCP_func_accept}) -la prima voce nella coda delle connessioni complete è passata al programma, o, -se la coda è vuota, il processo viene posto in attesa e risvegliato all'arrivo -della prima connessione completa. +client o fino ad un timeout. Nel caso di completamento del +\index{\textit{three~way~handshake}}\textit{three way handshake} la voce viene +spostata nella coda delle connessioni complete. Quando il processo chiama la +funzione \func{accept} (vedi sez.~\ref{sec:TCP_func_accept}) la prima voce +nella coda delle connessioni complete è passata al programma, o, se la coda è +vuota, il processo viene posto in attesa e risvegliato all'arrivo della prima +connessione completa. \begin{figure}[htb] \centering @@ -966,7 +972,7 @@ che il compito principale della coda sia quello di gestire il caso in cui il server è occupato fra chiamate successive alla \func{accept} (per cui la coda più occupata sarebbe quella delle connessioni completate), ma piuttosto quello di gestire la presenza di un gran numero di SYN in attesa di concludere il -three way handshake. +\textit{three way handshake}\index{\textit{three~way~handshake}}. Infine va messo in evidenza che, nel caso di socket TCP, quando un SYN arriva con tutte le code piene, il pacchetto deve essere ignorato. Questo perché la @@ -1202,9 +1208,9 @@ argomento per una \func{write} o una \func{read} (anche se l'altro capo della connessione non avesse chiuso la sua parte). Il kernel invierà comunque tutti i dati che ha in coda prima di iniziare la sequenza di chiusura. -Vedremo più avanti in sez.~\ref{sec:TCP_so_linger} come è possibile cambiare -questo comportamento, e cosa deve essere fatto perché il processo possa -assicurarsi che l'altro capo abbia ricevuto tutti i dati. +Vedremo più avanti in sez.~\ref{sec:sock_generic_options} come sia possibile +cambiare questo comportamento, e cosa può essere fatto perché il processo +possa assicurarsi che l'altro capo abbia ricevuto tutti i dati. Come per tutti i file descriptor anche per i socket viene mantenuto un numero di riferimenti, per cui se più di un processo ha lo stesso socket aperto @@ -1898,14 +1904,14 @@ 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: +\func{connect}; una volta completato il \textit{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 \textit{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 @@ -2152,7 +2158,7 @@ perch interrotta dall'arrivo di \const{SIGCHLD}, è quella ad \func{accept}, che è l'unica funzione che può mettere il processo padre in stato di sleep nel periodo in cui un figlio può terminare; si noti infatti come le altre -\index{system call lente} \textit{slow system call}\footnote{si ricordi la +\index{system~call~lente} \textit{slow system call}\footnote{si ricordi la distinzione fatta in sez.~\ref{sec:sig_gen_beha}.} o sono chiamate prima di entrare nel ciclo principale, quando ancora non esistono processi figli, o sono chiamate dai figli stessi e non risentono di \const{SIGCHLD}. @@ -2248,13 +2254,14 @@ funzione \func{accept}. Benché questo non sia un fatto comune, un evento simile può essere osservato con dei server molto occupati. In tal caso, con una struttura del server simile a quella del nostro esempio, in cui la gestione delle singole -connessioni è demandata a processi figli, può accadere che il three way -handshake venga completato e la relativa connessione abortita subito dopo, -prima che il padre, per via del carico della macchina, abbia fatto in tempo ad -eseguire la chiamata ad \func{accept}. Di nuovo si ha una situazione analoga -a quella illustrata in fig.~\ref{fig:TCP_early_abort}, in cui la connessione -viene stabilita, ma subito dopo si ha una condizione di errore che la chiude -prima che essa sia stata accettata dal programma. +connessioni è demandata a processi figli, può accadere che il \textit{three + way handshake}\index{\textit{three~way~handshake}} venga completato e la +relativa connessione abortita subito dopo, prima che il padre, per via del +carico della macchina, abbia fatto in tempo ad eseguire la chiamata ad +\func{accept}. Di nuovo si ha una situazione analoga a quella illustrata in +fig.~\ref{fig:TCP_early_abort}, in cui la connessione viene stabilita, ma +subito dopo si ha una condizione di errore che la chiude prima che essa sia +stata accettata dal programma. Questo significa che, oltre alla interruzione da parte di un segnale, che abbiamo trattato in sez.~\ref{sec:TCP_child_hand} nel caso particolare di @@ -2357,14 +2364,15 @@ anarres.echo > gont.34559: R 511689732:511689732(0) win 0 \end{verbatim} Le prime tre righe vengono prodotte al momento in cui lanciamo il nostro -client, e corrispondono ai tre pacchetti del three way handshake. L'output del -comando riporta anche i numeri di sequenza iniziali, mentre la lettera -\texttt{S} indica che per quel pacchetto si aveva il SYN flag attivo. Si noti +client, e corrispondono ai tre pacchetti del +\index{\textit{three~way~handshake}}\textit{three way handshake}. L'output +del comando riporta anche i numeri di sequenza iniziali, mentre la lettera +\texttt{S} indica che per quel pacchetto si aveva il SYN flag attivo. Si noti come a partire dal secondo pacchetto sia sempre attivo il campo \texttt{ack}, seguito dal numero di sequenza per il quale si da il ricevuto; quest'ultimo, a partire dal terzo pacchetto, viene espresso in forma relativa per maggiore compattezza. Il campo \texttt{win} in ogni riga indica la \textit{advertising - window} di cui parlavamo in sez.~\ref{sec:TCP_TCP_opt}. Allora si può + window} di cui parlavamo in sez.~\ref{sec:TCP_TCP_opt}. Allora si può verificare dall'output del comando come venga appunto realizzata la sequenza di pacchetti descritta in sez.~\ref{sec:TCP_conn_cre}: prima viene inviato dal client un primo pacchetto con il SYN che inizia la connessione, a cui il @@ -2372,16 +2380,17 @@ server risponde dando il ricevuto con un secondo pacchetto, che a sua volta porta un SYN, cui il client risponde con un il terzo pacchetto di ricevuto. Ritorniamo allora alla nostra sessione con il servizio echo: dopo le tre righe -del three way handshake non avremo nulla fin tanto che non scriveremo una -prima riga sul client; al momento in cui facciamo questo si genera una -sequenza di altri quattro pacchetti. Il primo, dal client al server, -contraddistinto da una lettera \texttt{P} che significa che il flag PSH è -impostato, contiene la nostra riga (che è appunto di 11 caratteri), e ad esso -il server risponde immediatamente con un pacchetto vuoto di ricevuto. Poi -tocca al server riscrivere indietro quanto gli è stato inviato, per cui sarà -lui a mandare indietro un terzo pacchetto con lo stesso contenuto appena -ricevuto, e a sua volta riceverà dal client un ACK nel quarto pacchetto. -Questo causerà la ricezione dell'eco nel client che lo stamperà a video. +del \textit{three way handshake}\index{\textit{three~way~handshake}} non +avremo nulla fin tanto che non scriveremo una prima riga sul client; al +momento in cui facciamo questo si genera una sequenza di altri quattro +pacchetti. Il primo, dal client al server, contraddistinto da una lettera +\texttt{P} che significa che il flag PSH è impostato, contiene la nostra riga +(che è appunto di 11 caratteri), e ad esso il server risponde immediatamente +con un pacchetto vuoto di ricevuto. Poi tocca al server riscrivere indietro +quanto gli è stato inviato, per cui sarà lui a mandare indietro un terzo +pacchetto con lo stesso contenuto appena ricevuto, e a sua volta riceverà dal +client un ACK nel quarto pacchetto. Questo causerà la ricezione dell'eco nel +client che lo stamperà a video. A questo punto noi procediamo ad interrompere l'esecuzione del server con un \texttt{C-c} (cioè con l'invio di \const{SIGTERM}): nel momento in cui @@ -2581,15 +2590,15 @@ successivo, per tentare di ristabilire la connessione. Il risultato finale qui dipende dall'implementazione dello stack TCP, e nel caso di Linux anche dall'impostazione di alcuni dei parametri di sistema che si trovano in \file{/proc/sys/net/ipv4}, che ne controllano il comportamento: -in questo caso in particolare da \file{tcp\_retries2}. Questo parametro -infatti specifica il numero di volte che deve essere ritentata la -ritrasmissione di un pacchetto nel mezzo di una connessione prima di riportare -un errore di timeout. Il valore preimpostato è pari a 15, il che -comporterebbe 15 tentativi di ritrasmissione, ma nel nostro caso le cose sono -andate diversamente, dato che le ritrasmissioni registrate da \cmd{tcpdump} -sono solo 8; inoltre l'errore riportato all'uscita del client non è stato -\errcode{ETIMEDOUT}, come dovrebbe essere in questo caso, ma -\errcode{EHOSTUNREACH}. +in questo caso in particolare da \file{tcp\_retries2} (vedi +sez.~\ref{sec:sock_sysctl}). Questo parametro infatti specifica il numero di +volte che deve essere ritentata la ritrasmissione di un pacchetto nel mezzo di +una connessione prima di riportare un errore di timeout. Il valore +preimpostato è pari a 15, il che comporterebbe 15 tentativi di ritrasmissione, +ma nel nostro caso le cose sono andate diversamente, dato che le +ritrasmissioni registrate da \cmd{tcpdump} sono solo 8; inoltre l'errore +riportato all'uscita del client non è stato \errcode{ETIMEDOUT}, come dovrebbe +essere in questo caso, ma \errcode{EHOSTUNREACH}. Per capire l'accaduto continuiamo ad analizzare l'output di \cmd{tcpdump}: esso ci mostra che a un certo punto i tentativi di ritrasmissione del @@ -2700,7 +2709,7 @@ riportando appunto come errore \errcode{ECONNRESET}. Occorre precisare che se si vuole che il client sia in grado di accorgersi del crollo del server anche quando non sta effettuando uno scambio di dati, è possibile usare una impostazione speciale del socket (ci torneremo in -sez.~\ref{sec:TCP_sock_options}) che provvede all'esecuzione di questo +sez.~\ref{sec:sock_generic_options}) che provvede all'esecuzione di questo controllo. \section{L'uso dell'I/O multiplexing} @@ -2748,8 +2757,8 @@ pronto per la lettura sono le seguenti: sufficiente a superare il valore di una \textsl{soglia di basso livello} (il cosiddetto \textit{low watermark}). Questo valore è espresso in numero di byte e può essere impostato con l'opzione del socket \const{SO\_RCVLOWAT} - (tratteremo le opzioni dei socket in sez.~\ref{sec:TCP_sock_options}); il - suo valore di default è 1 per i socket TCP e UDP. In questo caso una + (tratteremo l'uso di questa opzione in sez.~\ref{sec:sock_generic_options}); + il suo valore di default è 1 per i socket TCP e UDP. In questo caso una operazione di lettura avrà successo e leggerà un numero di byte maggiore di zero. \item il lato in lettura della connessione è stato chiuso; si è cioè ricevuto @@ -2761,7 +2770,7 @@ pronto per la lettura sono le seguenti: \item c'è stato un errore sul socket. In questo caso una operazione di lettura non si bloccherà ma restituirà una condizione di errore (ad esempio \func{read} restituirà -1) e imposterà la variabile \var{errno} al relativo - valore. Vedremo in sez.~\ref{sec:TCP_sock_options} come sia possibile + valore. Vedremo in sez.~\ref{sec:sock_generic_options} come sia possibile estrarre e cancellare errori pendenti su un socket usando l'opzione \const{SO\_ERROR}. \item quando si sta utilizzando un \textit{listening socket} ed ci sono delle @@ -2782,8 +2791,9 @@ pronto per la scrittura sono le seguenti: valore della \textsl{soglia di basso livello} in scrittura ed inoltre o il socket è già connesso o non necessita (ad esempio è UDP) di connessione. Il valore della soglia è espresso in numero di byte e può essere impostato con - l'opzione del socket \const{SO\_SNDLOWAT}; il suo valore di default è 2048 - per i socket TCP e UDP. In questo caso una operazione di scrittura non si + l'opzione del socket \const{SO\_SNDLOWAT} (trattata in + sez.~\ref{sec:sock_generic_options}); il suo valore di default è 2048 per i + socket TCP e UDP. In questo caso una operazione di scrittura non si bloccherà e restituirà un valore positivo pari al numero di byte accettati dal livello di trasporto. \item il lato in scrittura della connessione è stato chiuso. In questo caso @@ -2791,7 +2801,7 @@ pronto per la scrittura sono le seguenti: \item c'è stato un errore sul socket. In questo caso una operazione di scrittura non si bloccherà ma restituirà una condizione di errore ed imposterà opportunamente la variabile \var{errno}. Vedremo in - sez.~\ref{sec:TCP_sock_options} come sia possibile estrarre e cancellare + sez.~\ref{sec:sock_generic_options} come sia possibile estrarre e cancellare errori pendenti su un socket usando l'opzione \const{SO\_ERROR}. \end{itemize*} @@ -3400,9 +3410,9 @@ condizioni della rete. Inoltre deve essere specificato come viene classificato il traffico nella suddivisione fra dati normali e prioritari. In generale pertanto: \begin{itemize} -\item i dati trasmessi su un socket vengono considerati traffico normale, - pertanto vengono rilevati da una selezione con \const{POLLIN} o - \const{POLLRDNORM}. +\item i dati inviati su un socket vengono considerati traffico normale, + pertanto vengono rilevati alla loro ricezione sull'altro capo da una + selezione effettuata con \const{POLLIN} o \const{POLLRDNORM}; \item i dati \textit{out-of-band} su un socket TCP vengono considerati traffico prioritario e vengono rilevati da una condizione \const{POLLIN}, \const{POLLPRI} o \const{POLLRDBAND}. @@ -3410,6 +3420,10 @@ pertanto: viene considerato traffico normale, pertanto viene rilevato da una condizione \const{POLLIN} o \const{POLLRDNORM}, ma una conseguente chiamata a \func{read} restituirà 0. +\item la disponibilità di spazio sul socket per la scrittura di dati viene + segnalata con una condizione \const{POLLOUT}. +\item quando uno dei due capi del socket chiude un suo lato della connessione + con \func{shutdown} si riceve una condizione di \const{POLLHUP}. \item la presenza di un errore sul socket (sia dovuta ad un segmento RST che a timeout) viene considerata traffico normale, ma viene segnalata anche dalla condizione \const{POLLERR}.