From 6c1923829690f7edc97225c27707d3924803c2d4 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sun, 17 Aug 2003 23:03:44 +0000 Subject: [PATCH] Completato capitolo sui socket TCP elementari (almeno come infrastruttura), aggiunti altri esempi e funzioni --- listati/ClientEcho_second.c | 27 ++++ sources/TCP_echo.c | 17 ++- tcpsock.tex | 297 +++++++++++++++++++++++++++++++++--- 3 files changed, 314 insertions(+), 27 deletions(-) create mode 100644 listati/ClientEcho_second.c diff --git a/listati/ClientEcho_second.c b/listati/ClientEcho_second.c new file mode 100644 index 0000000..2aebe9a --- /dev/null +++ b/listati/ClientEcho_second.c @@ -0,0 +1,27 @@ +void ClientEcho(FILE * filein, int socket) +{ + char sendbuff[MAXLINE+1], recvbuff[MAXLINE+1]; + int nread, nwrite; + while (fgets(sendbuff, MAXLINE, filein) != NULL) { + nwrite = FullWrite(socket, sendbuff, strlen(sendbuff)); + if (nwrite < 0) { + printf("Errore in scrittura: %s", strerror(errno)); + return; + } + nread = read(socket, recvbuff, strlen(sendbuff)); + if (nread < 0) { + printf("Errore in lettura: %s\n", strerror(errno)); + return; + } + if (nread == 0) { + printf("End of file in lettura %s\n"); + return; + } + recvbuff[nread] = 0; + if (fputs(recvbuff, stdout) == EOF) { + perror("Errore in scrittura su terminale"); + return; + } + } + return; +} diff --git a/sources/TCP_echo.c b/sources/TCP_echo.c index 7fdb837..86b78c2 100644 --- a/sources/TCP_echo.c +++ b/sources/TCP_echo.c @@ -1,4 +1,4 @@ -/* TCP_echo1.c +/* TCP_echo.c * * Copyright (C) 2001-2003 Simone Piccardi * @@ -18,7 +18,7 @@ */ /**************************************************************** * - * Program ElemEchoTCPClient.c + * Program TCP_echo.c * Simple TCP client for echo service (port 7) * * Author: Simone Piccardi @@ -26,7 +26,7 @@ * * Usage: echo -h give all info's * - * $Id: TCP_echo.c,v 1.7 2003/08/03 18:12:47 piccardi Exp $ + * $Id: TCP_echo.c,v 1.8 2003/08/17 23:03:44 piccardi Exp $ * ****************************************************************/ /* @@ -141,15 +141,22 @@ void ClientEcho(FILE * filein, int socket) while (fgets(sendbuff, MAXLINE, filein) != NULL) { nwrite = FullWrite(socket, sendbuff, strlen(sendbuff)); if (nwrite < 0) { - printf("Errore in scrittura %s", strerror(errno)); + printf("Errore in scrittura: %s", strerror(errno)); + return; } nread = read(socket, recvbuff, strlen(sendbuff)); if (nread < 0) { - printf("Errore in lettura %s\n", strerror(errno)); + printf("Errore in lettura: %s\n", strerror(errno)); + return; + } + if (nread == 0) { + printf("EOF sul socket\n"); + return; } recvbuff[nread] = 0; if (fputs(recvbuff, stdout) == EOF) { perror("Errore in scrittura su terminale"); + return; } } return; diff --git a/tcpsock.tex b/tcpsock.tex index 4c411cd..6d23c10 100644 --- a/tcpsock.tex +++ b/tcpsock.tex @@ -1083,7 +1083,7 @@ relativi alla socket pair associata ad un certo socket. La prima funzione è \funcd{getsockname} e serve ad ottenere l'indirizzo locale associato ad un socket; il suo prototipo è: \begin{prototype}{sys/socket.h} - {int getsockname(int sockfd, struct sockaddr * name, socklen\_t * namelen)} + {int getsockname(int sockfd, struct sockaddr *name, socklen\_t *namelen)} Legge l'indirizzo locale di un socket. \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di @@ -1201,7 +1201,8 @@ si aspetta in una qualunque applicazione client/server. Per attivare immediatamente l'emissione del FIN e la sequenza di chiusura descritta in \secref{sec:TCP_conn_term}, si può invece usare la funzione -\func{shutdown} su cui torneremo in seguito (vedi \secref{sec:xxx_shutdown}). +\func{shutdown} su cui torneremo in seguito (vedi +\secref{sec:TCP_xxx_shutdown}). @@ -2244,7 +2245,7 @@ nessun errore al ritorno di \funcd{accept} quanto un errore di \subsection{La terminazione precoce del server} \label{sec:TCP_server_crash} -Un secondo caso critico è quello in cui si ha una terminazione prococe del +Un secondo caso critico è quello in cui si ha una terminazione precoce del server, ad esempio perché il programma ha un crash. In tal caso si suppone che il processo termini per un errore fatale, cosa che potremo simulare inviandogli un segnale di terminazione. La conclusione del processo comporta @@ -2273,9 +2274,9 @@ errore. Per capire meglio cosa è successo conviene analizzare il flusso dei pacchetti utilizzando un analizzatore di traffico come \cmd{tcpdump}. Il comando -permette di selezionare, nel treffico di rete generato su una macchina, i +permette di selezionare, nel traffico di rete generato su una macchina, i pacchetti che interessano, stampando a video (o salvando su disco) il loro -conteuto. Non staremo qui ad entrare nei dettagli dell'uso del programma, che +contenuto. Non staremo qui ad entrare nei dettagli dell'uso del programma, che sono spiegati dalla pagina di manuale; per l'uso che vogliamo farne quello che ci interessa è, posizionandosi sulla macchina che fa da client, selezionare tutti i pacchetti che sono diretti o provengono dalla macchina che fa da @@ -2324,7 +2325,7 @@ compattezza. Il campo \texttt{win} in ogni riga indica la \textit{advertising window} di cui parlavamo in \secref{sec:TCP_TCP_opt}. Allora si può verificare dall'output del comando come venga appunto realizzata la sequenza di pacchetti descritta in \secref{sec:TCP_conn_cre}: prima viene inviato dal -clinet un primo pacchetto con il SYN che inizia la connessione, a cui il +client un primo pacchetto con il SYN che inizia la connessione, a cui il 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. @@ -2351,7 +2352,7 @@ pacchetti, contraddistinto dalla lettera \texttt{F}, cui seguir ACK da parte del client. A questo punto la connessione dalla parte del server è chiusa, ed infatti se -usiamo \cmd{netstat} per controllarne lo stato sul server otteremo che: +usiamo \cmd{netstat} per controllarne lo stato otterremo che sul server si ha: \begin{verbatim} anarres:/home/piccardi# netstat -ant Active Internet connections (servers and established) @@ -2359,8 +2360,9 @@ Proto Recv-Q Send-Q Local Address Foreign Address State ... ... ... ... ... ... tcp 0 0 192.168.1.141:7 192.168.1.2:34626 FIN_WAIT2 \end{verbatim} -cioè, come aspettato, essa è nello stato \texttt{FIN_WAIT2}, mentre sul client -otterremo che essa è andata nello stato \texttt{CLOSE\_WAIT}: +cioè essa è andata nello stato \texttt{FIN\_WAIT2}, che indica l'avvenuta +emissione del segmento FIN, mentre sul client otterremo che essa è andata +nello stato \texttt{CLOSE\_WAIT}: \begin{verbatim} [root@gont gapil]# netstat -ant Active Internet connections (servers and established) @@ -2369,8 +2371,6 @@ Proto Recv-Q Send-Q Local Address Foreign Address State tcp 1 0 192.168.1.2:34582 192.168.1.141:7 CLOSE_WAIT \end{verbatim} - - Il problema è che in questo momento il client è bloccato dentro la funzione \texttt{ClientEcho} nella chiamata a \func{fgets}, e sta attendendo dell'input dal terminale, per cui non è in grado di accorgersi di nulla. Solo quando @@ -2380,19 +2380,31 @@ pacchetti riportati da \cmd{tcpdump}: il primo, inviato dal client contenente i 25 caratteri della riga appena letta, e ad esso la macchina server risponderà, non essendoci più niente in ascolto sulla porta 7, con un segmento di RST, contraddistinto dalla lettera \texttt{R}, che causa la conclusione -definitiva della connessione anche nel client, dove non comparrà più +definitiva della connessione anche nel client, dove non comparirà più nell'output di \cmd{netstat}. -Non avendo controllato lo stato di uscita della nostra funzione di scrittura -(si riveda il codice illustrato in \secref{fig:TCP_client_echo_sub}) che a -questo punto riporterebbe un errore, il client proseguirà leggendo dal socket, -e dato che questo è stato chiuso avremo che, come spiegato in -\secref{sec:TCP_conn_term}, la funzione \func{read} ritorna normalmente con un -valore nullo. Questo comporta che la seguente chiamata a \func{fputs} non ha -effetto (viene stampata una stringa nulla) ed il client si blocca di nuovo -nella successiva chiamata a \func{fgets}. Per questo diventa possibile -inserire una terza riga e solo dopo averlo fatto si avrà la terminazione del -programma. +Come abbiamo accennato in \secref{sec:TCP_conn_term} e come vedremo più avanti +in \secref{sec:TCP_xxx_shutdown}} la chiusura di un solo capo di un socket è +una operazione lecita, per cui la nostra scrittura avrà comunque successo +(come si può constatare lanciando usando \cmd{strace}\footnote{il comando + \cmd{strace} è un comando di debug molto utile che prende come parametro un + altro comando e ne stampa a video tutte le invocazioni di una system call, + coi relativi parametri e valori di ritorno, per cui usandolo in questo + contesto potremo verificare che effettivamente la \func{write} ha scritto la + riga, che in effetti è stata pure trasmessa via rete.}), in quanto il nostro +programma non ha a questo punto alcun modo di sapere che dall'altra parte non +c'è più nessuno processo in grado di leggere quanto scriverà. Questo sarà +chiaro solo dopo il tentativo di scrittura, e la ricezione del segmento RST di +risposta che indica che dall'altra parte non si è semplicemente chiuso un capo +del socket, ma è completamente terminato il programma. + +Per questo motivo il nostro client proseguirà leggendo dal socket, e dato che +questo è stato chiuso avremo che, come spiegato in \secref{sec:TCP_conn_term}, +la funzione \func{read} ritorna normalmente con un valore nullo. Questo +comporta che la seguente chiamata a \func{fputs} non ha effetto (viene +stampata una stringa nulla) ed il client si blocca di nuovo nella successiva +chiamata a \func{fgets}. Per questo diventa possibile inserire una terza riga +e solo dopo averlo fatto si avrà la terminazione del programma. Per capire come questa avvenga comunque, non avendo inserito nel codice nessun controllo di errore, occorre ricordare che, a parte la bidirezionalità del @@ -2405,6 +2417,247 @@ esattamente quello che avviene in questo caso, e siccome non abbiamo un gestore per questo segnale, viene eseguita l'azione preimpostata, che è quella di terminare il processo. +Per gestire in maniera più corretta questo tipo di evento dovremo allora +modificare il nostro client perché sia in grado di trattare le varie tipologie +di errore, per questo dovremo riscrivere la funzione \func{ClientEcho}, in +modo da controllare gli stati di uscita delle varie chiamate. Si è riportata +la nuova versione della funzione in \figref{fig:TCP_ClientEcho_second}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \includecodesample{listati/ClientEcho_second.c} + \end{minipage} + \normalsize + \caption{La sezione nel codice della seconda versione della funzione + \func{CleintEcho} usata dal client per il servizio \textit{echo} + modificata per tener conto degli eventuali errori.} + \label{fig:TCP_ClientEcho_second} +\end{figure} + +Come si può vedere in questo caso si controlla il valore di ritorno di tutte +le funzioni, ed inoltre si verifica la presenza di un eventuale end of file in +caso di lettura. Con questa modifica il nostro client echo diventa in grado di +accorgersi della chiusura del socket da parte del server, per cui ripetendo la +sequenza di operazioni precedenti stavolta otterremo che: +\begin{verbatim} +[piccardi@gont sources]$ ./echo 192.168.1.141 +Prima riga +Prima riga +Seconda riga dopo il C-c +EOF sul socket +\end{verbatim}%$ +ma di nuovo si tenga presente che non c'è modo di accorgersi della chiusura +del socket fin quando non si esegue la scrittura della seconda riga; il +protocollo infatti prevede che ci debba essere una scrittura prima di ricevere +un RST che confermi la chiusura del file, e solo alle successive scritture si +potrà ottenere un errore. + +Questa caratteristica dei socket ci mette di fronte ad un altro problema +relativo al nostro client, e che cioè esso non è in grado di accorgersi di +nulla fintanto che è bloccato nella lettura del terminale fatta con +\func{gets}. In questo caso il problema è minimo, ma esso riemergerà più +avanti, ed è quello che si deve affrontare tutte le volte quando si ha a che +fare con la necessità di lavorare con più descrittori, nel qual caso diventa +si pone la questione di come fare a non restare bloccati su un socket quando +altri potrebbero essere liberi. Vedremo come affrontare questa problematica in +\secref{sec:TCP_xxx_advanced}. + + +\subsection{Altri scenari di terminazione della connessione} +\label{sec:TCP_conn_crash} + +La terminazione del server è solo uno dei possibili scenari di terminazione +della connessione, un altro caso è ad esempio quello in cui si ha un crollo +della rete, cosa che potremo simulare facilmente staccando il cavo di rete. +Un'altra condizione è quella di un blocco della macchina completo della su cui +gira il server che deve essere riavviata, cosa che potremo simulare sia +premendo il bottone di reset,\footnote{un normale shutdown non va bene; in tal + caso infatti il sistema provvede a terminare tutti i processi, per cui la + situazione sarebbe sostanzialmente identica alla precedente.} che, in +maniera più gentile, riavviando la macchina dopo aver interrotto la +connessione di rete. + +Cominciamo ad analizzare il primo caso, il crollo della rete. Ripetiamo la +nostra sessione di lavoro precedente, lanciamo il client, scriviamo una prima +riga, poi stacchiamo il cavo e scriviamo una seconda riga. Il risultato che +otterremo è: +\begin{verbatim} +[piccardi@gont sources]$ ./echo 192.168.1.141 +Prima riga +Prima riga +Seconda riga dopo l'interruzione +Errore in lettura: No route to host +\end{verbatim}%$ + +Quello che succede in questo è che il programma, dopo aver scritto la seconda +riga, resta bloccato per un tempo molto lungo, prima di dare l'errore +\errcode{EHOSTUNREACH}. Se andiamo ad osservare con \cmd{strace} cosa accade +nel periodo in cui il programma è bloccato vedremo che stavolta, a differenza +del caso precedente, il programma è bloccato nella lettura dal socket. + +Se poi, come nel caso precedente, usiamo l'accortezza di analizzare il +traffico di rete fra client e server con \cmd{tcpdump}, otterremo il seguente +risultato: +\begin{verbatim} +[root@gont sources]# tcpdump src 192.168.1.141 or dst 192.168.1.141 -N -t +tcpdump: listening on eth0 +gont.34685 > anarres.echo: S 1943495663:1943495663(0) win 5840 +anarres.echo > gont.34685: S 1215783131:1215783131(0) ack 1943495664 win 5792 +gont.34685 > anarres.echo: . ack 1 win 5840 +gont.34685 > anarres.echo: P 1:12(11) ack 1 win 5840 +anarres.echo > gont.34685: . ack 12 win 5792 +anarres.echo > gont.34685: P 1:12(11) ack 12 win 5792 +gont.34685 > anarres.echo: . ack 12 win 5840 +gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 +gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 +gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 +gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 +gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 +gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 +gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 +gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 +gont.34685 > anarres.echo: P 12:45(33) ack 12 win 5840 +arp who-has anarres tell gont +arp who-has anarres tell gont +arp who-has anarres tell gont +arp who-has anarres tell gont +arp who-has anarres tell gont +arp who-has anarres tell gont +... +\end{verbatim} + +In questo caso l'andamento dei primi sette pacchetti è esattamente lo stesso +di prima. Solo che stavolta, non appena inviata la seconda riga, il programma +si bloccherà nella successiva chiamata a \func{read}, non ottendo nessuna +risposta. Quello che succede è che nel frattempo il kernel provvede, come +richiesto dal protocollo TCP, a tentare la ritrasmissione della nostra riga un +certo numero di volte, con tempi di attesa crescente fra un tentativo ed il +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 trovane 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}. + +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 +pacchetto sono cessati, per essere sostituiti da una serie di richieste di +protocollo ARP in cui il client richiede l'indirizzo del server. + +Come abbiamo accennato in \secref{sec:net_tcpip_general} ARP è il protocollo +che si incarica di trovare le corrispondenze corrispondenze fra indirizzo IP e +indirizzo hardware sulla scheda di rete. È evidente allora che nel nostro +caso, essendo client e server sulla stessa rete, è scaduta la voce nella +\textit{ARP cache}\footnote{la \textit{ARP chache} è una tabella mantenuta + internamente dal kernel che contiene tutte le corrispondenze fra indirizzi + IP e indirizzi fisici, ottenute appunto attraverso il protocollo ARP; le + voci della tabella hanno un tempo di vita limitato, passato il quale scadono + e devono essere nuovamente richieste.} relativa ad \texttt{anarres}, ed il +nostro client ha iniziato ad effettuare richieste ARP sulla rete per sapere +l'IP di quest'ultimo, che essendo scollegato non poteva rispondere. Anche per +questo tipo di richieste esiste un timeout, per cui dopo un certo numero di +tentativi il meccanismo si è interrotto, e l'errore riportato al programma a +questo punto è stato \errcode{EHOSTUNREACH}, in quanto non si era più in grado +di contattare il server. + +Un altro errore possibile in questo tipo di situazione, che si può avere +quando la macchina è su una rete remota, è \errcode{ENETUNREACH}; esso viene +riportato alla ricezione di un pacchetto ICMP di \textit{destination + unreachable} da parte del router che individua l'interruzione della +connessione. Di nuovo anche qui tutto dipende da chi è il meccanismo più +veloce ad accorgersi del problema. + +Se però agiamo sui parametri del kernel, e scriviamo in \file{tcp\_retries2} +un valore di tentativi più basso, possiamo evitare la scadenza della +\textit{ARP cache} e vedere cosa succede. Così se ad esempio richiediamo 4 +tentativi di ritrasmissione, l'analisi di \cmd{tcpdump} ci riporterà il +seguente scambio di pacchetti: +\begin{verbatim} +[root@gont gapil]# tcpdump src 192.168.1.141 or dst 192.168.1.141 -N -t +tcpdump: listening on eth0 +gont.34752 > anarres.echo: S 3646972152:3646972152(0) win 5840 +anarres.echo > gont.34752: S 2735190336:2735190336(0) ack 3646972153 win 5792 +gont.34752 > anarres.echo: . ack 1 win 5840 +gont.34752 > anarres.echo: P 1:12(11) ack 1 win 5840 +anarres.echo > gont.34752: . ack 12 win 5792 +anarres.echo > gont.34752: P 1:12(11) ack 12 win 5792 +gont.34752 > anarres.echo: . ack 12 win 5840 +gont.34752 > anarres.echo: P 12:45(33) ack 12 win 5840 +gont.34752 > anarres.echo: P 12:45(33) ack 12 win 5840 +gont.34752 > anarres.echo: P 12:45(33) ack 12 win 5840 +gont.34752 > anarres.echo: P 12:45(33) ack 12 win 5840 +gont.34752 > anarres.echo: P 12:45(33) ack 12 win 5840 +\end{verbatim} +e come si vede in questo caso i tentativi di ritrasmissione del pacchetto +iniziale sono proprio 4 (per un totale di 5 voci con quello trasmesso la prima +volta), ed in effetti, dopo un tempo molto più breve rispetto a prima ed in +corrispondenza dell'invio dell'ultimo tentativo, quello che otterremo come +errore all'uscita del client sarà diverso, e cioè: +\begin{verbatim} +[piccardi@gont sources]$ ./echo 192.168.1.141 +Prima riga +Prima riga +Seconda riga dopo l'interruzione +Errore in lettura: Connection timed out +\end{verbatim}%$ +che corrisponde appunto, come ci aspettavamo, alla ricezione di un +\errcode{ETIMEDOUT}. + +Analizziamo ora il secondo scenario, in cui si ha un crollo della macchina che +fa da server. Al solito lanciamo il nostro client, scriviamo una prima riga +per verificare che sia tutto a posto, poi stacchiamo il cavo e riavviamo il +server. A questo punto, ritornato attivo il server, scriviamo una seconda +riga. Quello che otterremo in questo caso è: +\begin{verbatim} +[piccardi@gont sources]$ ./echo 192.168.1.141 +Prima riga +Prima riga +Seconda riga dopo l'interruzione +Errore in lettura Connection reset by peer +\end{verbatim}%$ +e l'errore ricevuti da \func{read} stavolta è \errcode{ECONNRESET}. Se al +solito riportiamo l'analisi dei pacchetti effettuata con \cmd{tcpdump}, +avremo: +\begin{verbatim} +[root@gont gapil]# tcpdump src 192.168.1.141 or dst 192.168.1.141 -N -t +tcpdump: listening on eth0 +gont.34756 > anarres.echo: S 904864257:904864257(0) win 5840 +anarres.echo > gont.34756: S 4254564871:4254564871(0) ack 904864258 win 5792 +gont.34756 > anarres.echo: . ack 1 win 5840 +gont.34756 > anarres.echo: P 1:12(11) ack 1 win 5840 +anarres.echo > gont.34756: . ack 12 win 5792 +anarres.echo > gont.34756: P 1:12(11) ack 12 win 5792 +gont.34756 > anarres.echo: . ack 12 win 5840 +gont.34756 > anarres.echo: P 12:45(33) ack 12 win 5840 +anarres.echo > gont.34756: R 4254564883:4254564883(0) win 0 +\end{verbatim} + +Ancora una volta i primi sette pacchetti sono gli stessi; ma in questo caso +quello che succede dopo lo scambio iniziale è che, non avendo inviato nulla +durante il periodo in cui si è riavviato il server, il client è del tutto +ignaro dell'accaduto per cui quando effettuerà una scrittura, dato che la +macchina server è stata riavviata e che tutti gli stati relativi alle +precedenti connessioni sono completamente persi, anche in presenza di una +nuova istanza del server echo non sarà possibile consegnare i dati in arrivo, +per cui alla loro ricezione il kernel risponderà con un segmento di RST. + +Il client da parte sua, dato che neanche in questo caso non è stato emesso un +FIN, dopo aver scritto verrà bloccato nella successiva chiamata a \func{read}, +che però adesso ritornerà immediatamente alla ricezione del segmento RST, +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 +\secref{sec:TCP_xxx_sockopt}) che provvede all'esecuzione di questo controllo. -- 2.30.2