X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=tcpsock.tex;h=b40881921daf1b3946323a5a8f19490d5b13bfcc;hp=fb63b0b7ee85f78ca2a12907062c648eb8383557;hb=43d2568d8a142054e3c8cf564da0e0cf4f839ec8;hpb=1add1cb54c1754ed030033b994a68c6678d7d275 diff --git a/tcpsock.tex b/tcpsock.tex index fb63b0b..b408819 100644 --- a/tcpsock.tex +++ b/tcpsock.tex @@ -1,6 +1,6 @@ %% tcpsock.tex %% -%% Copyright (C) 2000-2012 Simone Piccardi. Permission is granted to +%% Copyright (C) 2000-2016 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 "Un preambolo", @@ -16,7 +16,7 @@ In questo capitolo tratteremo le basi dei socket TCP, iniziando con una descrizione delle principali caratteristiche del funzionamento di una connessione TCP; vedremo poi le varie funzioni che servono alla creazione di una connessione fra client e server, fornendo alcuni esempi elementari, e -finiremo prendendo in esame l'uso dell'I/O multiplexing. +finiremo prendendo in esame l'uso dell'\textit{I/O multiplexing}. \section{Il funzionamento di una connessione TCP} @@ -40,8 +40,9 @@ significato di alcuni dei vari \textsl{stati} ad essa associati. \label{sec:TCP_conn_cre} \itindbeg{three~way~handshake} -Il processo che porta a creare una connessione TCP è chiamato \textit{three - way handshake}; la successione tipica degli eventi (e dei + +Il processo che porta a creare una connessione TCP viene chiamato +\textit{three way handshake}; la successione tipica degli eventi (e dei \textsl{segmenti}\footnote{si ricordi che il segmento è l'unità elementare di dati trasmessa dal protocollo TCP al livello successivo; tutti i segmenti hanno un header che contiene le informazioni che servono allo \textit{stack @@ -135,38 +136,35 @@ comunicare all'altro capo una serie di parametri utili a regolare la connessione. Normalmente vengono usate le seguenti opzioni: \begin{itemize} -\item \textit{MSS option}, dove MMS sta per - \itindex{Maximum~Segment~Size~(MSS)} \textit{Maximum Segment Size}, con - questa opzione ciascun capo della connessione annuncia all'altro il massimo - ammontare di dati che vorrebbe accettare per ciascun segmento nella - connessione corrente. È possibile leggere e scrivere questo valore +\item \textit{MSS option}, con questa opzione ciascun capo della connessione + annuncia all'altro il massimo ammontare di dati (MMS sta appunto per + \textit{Maximum Segment Size}) che vorrebbe accettare per ciascun segmento + nella connessione corrente. È possibile leggere e scrivere questo valore attraverso l'opzione del socket \const{TCP\_MAXSEG} (vedi sez.~\ref{sec:sock_tcp_udp_options}). \item \textit{window scale option}, il protocollo TCP implementa il controllo - di flusso attraverso una \itindex{advertised~window} \textit{advertised - window} (la ``\textsl{finestra annunciata}'', vedi - sez.~\ref{sec:tcp_protocol_xxx}) con la quale ciascun capo della - comunicazione dichiara quanto spazio disponibile ha in memoria per i dati. - Questo è un numero a 16 bit dell'header, che così può indicare un massimo di - 65535 byte;\footnote{in Linux il massimo è 32767 per evitare problemi con - alcune implementazioni che usano l'aritmetica con segno per implementare - lo stack TCP.} ma alcuni tipi di connessione come quelle ad alta velocità - (sopra i 45Mbit/sec) e quelle che hanno grandi ritardi nel cammino dei - pacchetti (come i satelliti) richiedono una finestra più grande per poter - ottenere il massimo dalla trasmissione. Per questo esiste questa opzione che - indica un fattore di scala da applicare al valore della - \itindex{advertised~window} finestra annunciata\footnote{essendo una nuova - opzione per garantire la compatibilità con delle vecchie implementazioni - del protocollo la procedura che la attiva prevede come negoziazione che - l'altro capo della connessione riconosca esplicitamente l'opzione - inserendola anche lui nel suo SYN di risposta dell'apertura della - connessione.} per la connessione corrente (espresso come numero di bit cui - spostare a sinistra il valore della finestra annunciata inserito nel - pacchetto). Con Linux è possibile indicare al kernel di far negoziare il - fattore di scala in fase di creazione di una connessione tramite la - \textit{sysctl} \itindex{TCP~window~scaling} \texttt{tcp\_window\_scaling} - (vedi sez.~\ref{sec:sock_ipv4_sysctl}).\footnote{per poter usare questa + di flusso attraverso una \textit{advertised window} (la ``\textsl{finestra + annunciata}'', vedi sez.~\ref{sec:tcp_protocol_xxx}) con la quale ciascun + capo della comunicazione dichiara quanto spazio disponibile ha in memoria + per i dati. Questo è un numero a 16 bit dell'header, che così può indicare + un massimo di 65535 byte;\footnote{in Linux il massimo è 32767 per evitare + problemi con alcune implementazioni che usano l'aritmetica con segno per + implementare lo stack TCP.} ma alcuni tipi di connessione come quelle ad + alta velocità (sopra i 45Mbit/sec) e quelle che hanno grandi ritardi nel + cammino dei pacchetti (come i satelliti) richiedono una finestra più grande + per poter ottenere il massimo dalla trasmissione. Per questo esiste questa + opzione che indica un fattore di scala da applicare al valore della finestra + annunciata\footnote{essendo una nuova opzione per garantire la compatibilità + con delle vecchie implementazioni del protocollo la procedura che la + attiva prevede come negoziazione che l'altro capo della connessione + riconosca esplicitamente l'opzione inserendola anche lui nel suo SYN di + risposta dell'apertura della connessione.} per la connessione corrente + (espresso come numero di bit cui spostare a sinistra il valore della + finestra annunciata inserito nel pacchetto). Con Linux è possibile indicare + al kernel di far negoziare il fattore di scala in fase di creazione di una + connessione tramite la \textit{sysctl} \texttt{tcp\_window\_scaling} (vedi + sez.~\ref{sec:sock_ipv4_sysctl}).\footnote{per poter usare questa funzionalità è comunque necessario ampliare le dimensioni dei buffer di ricezione e spedizione, cosa che può essere fatta sia a livello di sistema con le opportune \textit{sysctl} (vedi sez.~\ref{sec:sock_ipv4_sysctl}) @@ -180,8 +178,8 @@ connessione. Normalmente vengono usate le seguenti opzioni: \end{itemize} -La MSS \itindex{Maximum~Segment~Size~(MSS)} è generalmente supportata da quasi -tutte le implementazioni del protocollo, le ultime due opzioni (trattate +La MSS è generalmente supportata da quasi tutte le implementazioni del +protocollo, le ultime due opzioni (trattate nell'\href{http://www.ietf.org/rfc/rfc1323.txt}{RFC~1323}) sono meno comuni; vengono anche dette \textit{long fat pipe options} dato che questo è il nome che viene dato alle connessioni caratterizzate da alta velocità o da ritardi @@ -240,9 +238,9 @@ deve ancora eseguire la chiusura passiva a quello che sta eseguendo la chiusura attiva. Nella sequenza indicata i dati verrebbero persi, dato che si è chiuso il socket dal lato che esegue la chiusura attiva; esistono tuttavia situazioni in cui si vuole poter sfruttare questa possibilità, usando una -procedura che è chiamata \itindex{half-close} \textit{half-close}; torneremo -su questo aspetto e su come utilizzarlo in sez.~\ref{sec:TCP_shutdown}, quando -parleremo della funzione \func{shutdown}. +procedura che è chiamata \textit{half-close}; torneremo su questo aspetto e su +come utilizzarlo in sez.~\ref{sec:TCP_shutdown}, quando parleremo della +funzione \func{shutdown}. La emissione del FIN avviene quando il socket viene chiuso, questo però non avviene solo per la chiamata esplicita della funzione \func{close}, ma anche @@ -304,10 +302,9 @@ che il protocollo viene ad assumere per i due lati, server e client. \label{fig:TCP_conn_example} \end{figure} -La connessione viene iniziata dal client che annuncia una -\itindex{Maximum~Segment~Size~(MSS)} MSS di 1460, un valore tipico con Linux -per IPv4 su Ethernet, il server risponde con lo stesso valore (ma potrebbe -essere anche un valore diverso). +La connessione viene iniziata dal client che annuncia una MSS di 1460, un +valore tipico con Linux per IPv4 su Ethernet, il server risponde con lo stesso +valore (ma potrebbe essere anche un valore diverso). Una volta che la connessione è stabilita il client scrive al server una richiesta (che assumiamo stare in un singolo segmento, cioè essere minore dei @@ -520,11 +517,11 @@ che solo l'amministratore possa allocare queste porte per far partire i relativi servizi. Le \textsl{glibc} definiscono in \headfile{netinet/in.h} -\const{IPPORT\_RESERVED} e \const{IPPORT\_USERRESERVED}, in cui la prima (che -vale 1024) indica il limite superiore delle porte riservate, e la seconda (che -vale 5000) il limite inferiore delle porte a disposizione degli utenti. La -convenzione vorrebbe che le porte \textsl{effimere} siano allocate fra questi -due valori. Nel caso di Linux questo è vero solo in uno dei due casi di +\constd{IPPORT\_RESERVED} e \constd{IPPORT\_USERRESERVED}, in cui la prima +(che vale 1024) indica il limite superiore delle porte riservate, e la seconda +(che vale 5000) il limite inferiore delle porte a disposizione degli utenti. +La convenzione vorrebbe che le porte \textsl{effimere} siano allocate fra +questi due valori. Nel caso di Linux questo è vero solo in uno dei due casi di fig.~\ref{fig:TCP_port_alloc}, e la scelta fra i due possibili intervalli viene fatta dinamicamente dal kernel a seconda della memoria disponibile per la gestione delle relative tabelle. @@ -578,7 +575,7 @@ posto in ascolto per connessioni provenienti da uno qualunque degli indirizzi associati alle interfacce locali. La notazione \texttt{0.0.0.0} usata da \cmd{netstat} è equivalente all'asterisco utilizzato per il numero di porta, indica il valore generico, e corrisponde al valore \const{INADDR\_ANY} -definito in \headfile{arpa/inet.h} (vedi \ref{tab:TCP_ipv4_addr}). +definito in \headfiled{arpa/inet.h} (vedi \ref{tab:TCP_ipv4_addr}). Inoltre si noti come la porta e l'indirizzo di ogni eventuale connessione esterna non sono specificati; in questo caso la \textit{socket pair} associata @@ -654,7 +651,7 @@ figlio e quelli che arrivano alla porta 21101 al secondo. In questa sezione descriveremo in maggior dettaglio le varie funzioni che vengono usate per la gestione di base dei socket TCP, non torneremo però sulla funzione \func{socket}, che è già stata esaminata accuratamente nel capitolo -precedente in sez.~\ref{sec:sock_socket}. +precedente in sez.~\ref{sec:sock_creation}. \subsection{La funzione \func{bind}} @@ -731,9 +728,9 @@ Si noti che si è usato \func{htonl} per assegnare il valore \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 -\itindex{endianness} l'\textit{endianness} 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{endianness} 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 @@ -743,12 +740,11 @@ norma usare sempre la funzione \func{htonl}. \textbf{Costante} & \textbf{Significato} \\ \hline \hline - \const{INADDR\_ANY} & Indirizzo generico (\texttt{0.0.0.0})\\ - \const{INADDR\_BROADCAST}& Indirizzo di \itindex{broadcast} - \textit{broadcast}.\\ - \const{INADDR\_LOOPBACK} & Indirizzo di \textit{loopback} - (\texttt{127.0.0.1}).\\ - \const{INADDR\_NONE} & Indirizzo errato.\\ + \constd{INADDR\_ANY} & Indirizzo generico (\texttt{0.0.0.0})\\ + \constd{INADDR\_BROADCAST}& Indirizzo di \textit{broadcast}.\\ + \constd{INADDR\_LOOPBACK} & Indirizzo di \textit{loopback} + (\texttt{127.0.0.1}).\\ + \constd{INADDR\_NONE} & Indirizzo errato.\\ \hline \end{tabular} \caption{Costanti di definizione di alcuni indirizzi generici per IPv4.} @@ -762,12 +758,12 @@ con una struttura, perché il linguaggio C non consente l'uso di una struttura costante come operando a destra in una assegnazione. Per questo motivo nell'header \headfile{netinet/in.h} è definita una variabile -\macro{in6addr\_any} (dichiarata come \direct{extern}, ed inizializzata dal -sistema al valore \const{IN6ADRR\_ANY\_INIT}) che permette di effettuare una +\var{in6addr\_any} (dichiarata come \dirct{extern}, ed inizializzata dal +sistema al valore \constd{IN6ADRR\_ANY\_INIT}) che permette di effettuare una assegnazione del tipo: \includecodesnip{listati/serv_addr_sin6_addr.c} in -maniera analoga si può utilizzare la variabile \macro{in6addr\_loopback} per +maniera analoga si può utilizzare la variabile \var{in6addr\_loopback} per indicare l'indirizzo di \textit{loopback}, che a sua volta viene inizializzata -staticamente a \const{IN6ADRR\_LOOPBACK\_INIT}. +staticamente a \constd{IN6ADRR\_LOOPBACK\_INIT}. \subsection{La funzione \func{connect}} @@ -780,8 +776,8 @@ connessione con un server TCP,\footnote{di nuovo la funzione è generica e limiterà ad impostare l'indirizzo dal quale e verso il quale saranno inviati e ricevuti i pacchetti, mentre per socket di tipo \const{SOCK\_STREAM} o \const{SOCK\_SEQPACKET}, essa attiverà la procedura di avvio (nel caso del - TCP il \itindex{three~way~handshake} \textit{three way handshake}) della - connessione.} il prototipo della funzione è il seguente: + TCP il \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)} @@ -806,9 +802,8 @@ connessione con un server TCP,\footnote{di nuovo la funzione è generica e \item[\errcode{EAFNOSUPPORT}] l'indirizzo non ha una famiglia di indirizzi corretta nel relativo campo. \item[\errcode{EACCES}, \errcode{EPERM}] si è tentato di eseguire una - connessione ad un indirizzo \itindex{broadcast} \textit{broadcast} senza - che il socket fosse stato abilitato per il \itindex{broadcast} - \textit{broadcast}. + connessione ad un indirizzo \textit{broadcast} senza che il socket fosse + stato abilitato per il \textit{broadcast}. \end{errlist} altri errori possibili sono: \errval{EFAULT}, \errval{EBADF}, \errval{ENOTSOCK}, \errval{EISCONN} e \errval{EADDRINUSE}.} @@ -824,12 +819,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 -\itindex{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: +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: \begin{enumerate} \item Il client non riceve risposta al SYN: l'errore restituito è \errcode{ETIMEDOUT}. Stevens riporta che BSD invia un primo SYN alla @@ -924,27 +919,24 @@ 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 \itindex{three~way~handshake} \textit{three way - handshake} non si è ancora concluso. Questi socket sono tutti nello stato - \texttt{SYN\_RECV}. + arrivato un SYN ma il \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 - \itindex{three~way~handshake} \textit{three way handshake} è stato - completato ma ancora \func{accept} non è ritornata. Questi socket sono - tutti nello stato \texttt{ESTABLISHED}. + 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} Lo schema di funzionamento è descritto in fig.~\ref{fig:TCP_listen_backlog}: 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 -\itindex{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. +client o fino ad un timeout. Nel caso di completamento del \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 \includegraphics[width=11cm]{img/tcp_listen_backlog} @@ -961,9 +953,9 @@ kernel, compreso Linux 2.0, che mostrano le differenze fra diverse implementazioni. In Linux il significato di questo valore è cambiato a partire dal kernel 2.2 -per prevenire l'attacco chiamato \index{SYN~flood} \textit{SYN flood}. Questo -si basa sull'emissione da parte dell'attaccante di un grande numero di -pacchetti SYN indirizzati verso una porta, forgiati con indirizzo IP +per prevenire l'attacco chiamato \itindex{SYN~flood} \textit{SYN + flood}. Questo si basa sull'emissione da parte dell'attaccante di un grande +numero di pacchetti SYN indirizzati verso una porta, forgiati con indirizzo IP fasullo\footnote{con la tecnica che viene detta \textit{ip spoofing}.} così che i SYN$+$ACK vanno perduti e la coda delle connessioni incomplete viene saturata, impedendo di fatto ulteriori connessioni. @@ -971,14 +963,14 @@ saturata, impedendo di fatto ulteriori connessioni. Per ovviare a questo il significato del \param{backlog} è stato cambiato a indicare la lunghezza della coda delle connessioni complete. La lunghezza della coda delle connessioni incomplete può essere ancora controllata usando -la funzione \func{sysctl} con il parametro \const{NET\_TCP\_MAX\_SYN\_BACKLOG} -o scrivendola direttamente in -\sysctlfile{net/ipv4/tcp\_max\_syn\_backlog}. Quando si attiva la -protezione dei syncookies però (con l'opzione da compilare nel kernel e da -attivare usando \sysctlfile{net/ipv4/tcp\_syncookies}) questo valore -viene ignorato e non esiste più un valore massimo. In ogni caso in Linux il -valore di \param{backlog} viene troncato ad un massimo di \const{SOMAXCONN} se -è superiore a detta costante (che di default vale 128).\footnote{il valore di +la funzione \func{sysctl} con il parametro +\constd{NET\_TCP\_MAX\_SYN\_BACKLOG} o scrivendola direttamente in +\sysctlfile{net/ipv4/tcp\_max\_syn\_backlog}. Quando si attiva la protezione +dei syncookies però (con l'opzione da compilare nel kernel e da attivare +usando \sysctlfile{net/ipv4/tcp\_syncookies}) questo valore viene ignorato e +non esiste più un valore massimo. In ogni caso in Linux il valore +di \param{backlog} viene troncato ad un massimo di \const{SOMAXCONN} se è +superiore a detta costante (che di default vale 128).\footnote{il valore di questa costante può essere controllato con un altro parametro di \func{sysctl}, vedi sez.~\ref{sec:sock_ioctl_IP}.} @@ -997,7 +989,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 -\itindex{three~way~handshake} \textit{three way handshake}. +\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 @@ -1015,10 +1007,10 @@ trasparente dal protocollo TCP. \label{sec:TCP_func_accept} La funzione \funcd{accept} è chiamata da un server per gestire la connessione -una volta che sia stato completato il \itindex{three~way~handshake} -\textit{three way handshake},\footnote{la funzione è comunque generica ed è - utilizzabile su socket di tipo \const{SOCK\_STREAM}, \const{SOCK\_SEQPACKET} - e \const{SOCK\_RDM}.} la funzione restituisce un nuovo socket descriptor su +una volta che sia stato completato il \textit{three way + handshake},\footnote{la funzione è comunque generica ed è utilizzabile su + socket di tipo \const{SOCK\_STREAM}, \const{SOCK\_SEQPACKET} e + \const{SOCK\_RDM}.} la funzione restituisce un nuovo socket descriptor su cui si potrà operare per effettuare la comunicazione. Se non ci sono connessioni completate il processo viene messo in attesa. Il prototipo della funzione è il seguente: @@ -1101,8 +1093,9 @@ eventualmente ripetere la chiamata alla funzione come per l'errore di Un'altra differenza con BSD è che la funzione non fa ereditare al nuovo socket i flag del socket originale, come \const{O\_NONBLOCK},\footnote{ed in generale tutti quelli che si possono impostare con \func{fcntl}, vedi - sez.~\ref{sec:file_fcntl}.} che devono essere rispecificati ogni volta. Tutto -questo deve essere tenuto in conto se si devono scrivere programmi portabili. + sez.~\ref{sec:file_fcntl_ioctl}.} che devono essere rispecificati ogni +volta. Tutto questo deve essere tenuto in conto se si devono scrivere +programmi portabili. Il meccanismo di funzionamento di \func{accept} è essenziale per capire il funzionamento di un server: in generale infatti c'è sempre un solo socket in @@ -1241,9 +1234,9 @@ 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 l'emissione del FIN e la sequenza di chiusura di TCP non viene innescata fintanto che il numero di riferimenti non si annulla, questo si applica, come -visto in sez.~\ref{sec:file_sharing}, sia ai file descriptor duplicati che a -quelli ereditati dagli eventuali processi figli, ed è il comportamento che ci -si aspetta in una qualunque applicazione client/server. +visto in sez.~\ref{sec:file_shared_access}, sia ai file descriptor duplicati +che a quelli ereditati dagli eventuali processi figli, ed è il comportamento +che ci si aspetta in una qualunque applicazione client/server. Per attivare immediatamente l'emissione del FIN e la sequenza di chiusura descritta in sez.~\ref{sec:TCP_conn_term}, si può invece usare la funzione @@ -1298,7 +1291,7 @@ Quando ci si trova ad affrontare questo comportamento tutto quello che si deve fare è semplicemente ripetere la lettura (o la scrittura) per la quantità di byte restanti, tenendo conto che le funzioni si possono bloccare se i dati non sono disponibili: è lo stesso comportamento che si può avere scrivendo più di -\const{PIPE\_BUF} byte in una pipe (si riveda quanto detto in +\const{PIPE\_BUF} byte in una \textit{pipe} (si riveda quanto detto in sez.~\ref{sec:ipc_pipes}). Per questo motivo, seguendo l'esempio di R. W. Stevens in \cite{UNP1}, si sono @@ -1324,9 +1317,10 @@ disponibile fra i sorgenti allegati alla guida nei file \file{FullRead.c} e Come si può notare le due funzioni ripetono la lettura/scrittura in un ciclo fino all'esaurimento del numero di byte richiesti, in caso di errore viene -controllato se questo è \errcode{EINTR} (cioè un'interruzione della system -call dovuta ad un segnale), nel qual caso l'accesso viene ripetuto, altrimenti -l'errore viene ritornato al programma chiamante, interrompendo il ciclo. +controllato se questo è \errcode{EINTR} (cioè un'interruzione della +\textit{system call} dovuta ad un segnale), nel qual caso l'accesso viene +ripetuto, altrimenti l'errore viene ritornato al programma chiamante, +interrompendo il ciclo. Nel caso della lettura, se il numero di byte letti è zero, significa che si è arrivati alla fine del file (per i socket questo significa in genere che @@ -1384,7 +1378,7 @@ il numero della porta del servizio. Il primo passo (\texttt{\small 20}) è inizializzare tutto a zero, per poi inserire il tipo di indirizzo (\texttt{\small 21}) e la porta (\texttt{\small 22}), usando per quest'ultima la funzione \func{htons} per convertire il formato dell'intero usato dal -computer a quello usato nella rete, infine \texttt{\small 23--27} si può +computer a quello usato nella rete, infine (\texttt{\small 23--27}) si può utilizzare la funzione \func{inet\_pton} per convertire l'indirizzo numerico passato dalla linea di comando. @@ -1930,13 +1924,12 @@ 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 \itindex{three~way~handshake} -\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 \itindex{three~way~handshake} - \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 +\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} @@ -2038,17 +2031,17 @@ esaminato in sez.~\ref{sec:proc_termination}). In questo caso avremo l'invio del segnale \signal{SIGCHLD} al padre, ma dato che non si è installato un gestore 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 \itindex{zombie} -\textit{zombie} (si riveda quanto illustrato in sez.~\ref{sec:sig_sigchld}), -come risulterà ripetendo il comando \cmd{ps}: +otterremo che il processo figlio entrerà nello stato di \textit{zombie} (si +riveda quanto illustrato in sez.~\ref{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 ] \end{verbatim} -Dato che non è il caso di lasciare processi \itindex{zombie} \textit{zombie}, -occorrerà ricevere opportunamente lo stato di terminazione del processo (si -veda sez.~\ref{sec:proc_wait}), cosa che faremo utilizzando \signal{SIGCHLD} +Dato che non è il caso di lasciare processi \textit{zombie}, occorrerà +ricevere opportunamente lo stato di terminazione del processo (si veda +sez.~\ref{sec:proc_wait}), cosa che faremo utilizzando \signal{SIGCHLD} secondo quanto illustrato in sez.~\ref{sec:sig_sigchld}. Una prima modifica al nostro server è pertanto quella di inserire la gestione della terminazione dei processi figli attraverso l'uso di un gestore. Per questo useremo la funzione @@ -2061,17 +2054,17 @@ all'esempio illustrato in fig.~\ref{fig:TCP_echo_server_first_code}. In questo modo però si introduce un altro problema. Si ricordi infatti che, come spiegato in sez.~\ref{sec:sig_gen_beha}, quando un programma si trova in -stato di \texttt{sleep} durante l'esecuzione di una system call, questa viene -interrotta alla ricezione di un segnale. Per questo motivo, alla fine -dell'esecuzione del gestore del segnale, se questo ritorna, il programma -riprenderà l'esecuzione ritornando dalla system call interrotta con un errore -di \errcode{EINTR}. +stato di \texttt{sleep} durante l'esecuzione di una \textit{system call}, +questa viene interrotta alla ricezione di un segnale. Per questo motivo, alla +fine dell'esecuzione del gestore del segnale, se questo ritorna, il programma +riprenderà l'esecuzione ritornando dalla \textit{system call} interrotta con +un errore di \errcode{EINTR}. Vediamo allora cosa comporta tutto questo nel nostro caso: quando si chiude il client, il processo figlio che gestisce la connessione terminerà, ed il padre, -per evitare la creazione di \itindex{zombie} \textit{zombie}, riceverà il -segnale \signal{SIGCHLD} eseguendo il relativo gestore. Al ritorno del gestore -però l'esecuzione nel padre ripartirà subito con il ritorno della funzione +per evitare la creazione di \textit{zombie}, riceverà il segnale +\signal{SIGCHLD} eseguendo il relativo gestore. Al ritorno del gestore però +l'esecuzione nel padre ripartirà subito con il ritorno della funzione \func{accept} (a meno di un caso fortuito in cui il segnale arriva durante l'esecuzione del programma in risposta ad una connessione) con un errore di \errcode{EINTR}. Non avendo previsto questa eventualità il programma considera @@ -2082,13 +2075,13 @@ accept error: Interrupted system call \end{verbatim}%# Come accennato in sez.~\ref{sec:sig_gen_beha} le conseguenze di questo -comportamento delle system call possono essere superate in due modi diversi, -il più semplice è quello di modificare il codice di \func{Signal} per -richiedere il riavvio automatico delle system call interrotte secondo la -semantica di BSD, usando l'opzione \const{SA\_RESTART} di \func{sigaction}; -rispetto a quanto visto in fig.~\ref{fig:sig_Signal_code}. Definiremo allora la -nuova funzione \func{SignalRestart}\footnote{anche questa è definita, insieme - alle altre funzioni riguardanti la gestione dei segnali, nel file +comportamento delle \textit{system call} possono essere superate in due modi +diversi, il più semplice è quello di modificare il codice di \func{Signal} per +richiedere il riavvio automatico delle \textit{system call} interrotte secondo +la semantica di BSD, usando l'opzione \const{SA\_RESTART} di \func{sigaction}; +rispetto a quanto visto in fig.~\ref{fig:sig_Signal_code}. Definiremo allora +la nuova funzione \func{SignalRestart}\footnote{anche questa è definita, + insieme alle altre funzioni riguardanti la gestione dei segnali, nel file \file{SigHand.c}, il cui contento completo può essere trovato negli esempi allegati.} come mostrato in fig.~\ref{fig:sig_SignalRestart_code}, ed installeremo il gestore usando quest'ultima. @@ -2100,30 +2093,30 @@ installeremo il gestore usando quest'ultima. \end{minipage} \normalsize \caption{La funzione \func{SignalRestart}, che installa un gestore di - segnali in semantica BSD per il riavvio automatico delle system call - interrotte.} + segnali in semantica BSD per il riavvio automatico delle \textit{system + call} interrotte.} \label{fig:sig_SignalRestart_code} \end{figure} Come si può notare questa funzione è identica alla precedente \func{Signal}, -illustrata in fig.~\ref{fig:sig_Signal_code}, solo che in questo caso invece di -inizializzare a zero il campo \var{sa\_flags} di \struct{sigaction}, lo si +illustrata in fig.~\ref{fig:sig_Signal_code}, solo che in questo caso invece +di inizializzare a zero il campo \var{sa\_flags} di \struct{sigaction}, lo si inizializza (\texttt{\small 5}) al valore \const{SA\_RESTART}. Usando questa funzione al posto di \func{Signal} nel server non è necessaria nessuna altra -modifica: le system call interrotte saranno automaticamente riavviate, e -l'errore \errcode{EINTR} non si manifesterà più. +modifica: le \textit{system call} interrotte saranno automaticamente +riavviate, e l'errore \errcode{EINTR} non si manifesterà più. La seconda soluzione è più invasiva e richiede di controllare tutte le volte -l'errore restituito dalle varie system call, ripetendo la chiamata qualora -questo corrisponda ad \errcode{EINTR}. Questa soluzione ha però il pregio -della portabilità, infatti lo standard POSIX dice che la funzionalità di -riavvio automatico delle system call, fornita da \const{SA\_RESTART}, è -opzionale, per cui non è detto che essa sia disponibile su qualunque sistema. -Inoltre in certi casi,\footnote{Stevens in \cite{UNP1} accenna che la maggior - parte degli Unix derivati da BSD non fanno ripartire \func{select}; altri - non riavviano neanche \func{accept} e \func{recvfrom}, cosa che invece nel - caso di Linux viene sempre fatta.} anche quando questa è presente, non è -detto possa essere usata con \func{accept}. +l'errore restituito dalle varie \textit{system call}, ripetendo la chiamata +qualora questo corrisponda ad \errcode{EINTR}. Questa soluzione ha però il +pregio della portabilità, infatti lo standard POSIX dice che la funzionalità +di riavvio automatico delle \textit{system call}, fornita da +\const{SA\_RESTART}, è opzionale, per cui non è detto che essa sia disponibile +su qualunque sistema. Inoltre in certi casi,\footnote{Stevens in \cite{UNP1} + accenna che la maggior parte degli Unix derivati da BSD non fanno ripartire + \func{select}; altri non riavviano neanche \func{accept} e \func{recvfrom}, + cosa che invece nel caso di Linux viene sempre fatta.} anche quando questa è +presente, non è detto possa essere usata con \func{accept}. La portabilità nella gestione dei segnali però viene al costo di una @@ -2159,7 +2152,7 @@ programma. \normalsize \caption{La sezione nel codice della seconda versione del server per il servizio \textit{echo} modificata per tener conto dell'interruzione - delle system call.} + delle \textit{system call}.} \label{fig:TCP_echo_server_code_second} \end{figure} @@ -2181,14 +2174,14 @@ eventuale pausa con una condizione (\texttt{\small 21}) sulla variabile numero di secondi da aspettare (il valore preimpostato è nullo). Si è potuto lasciare inalterata tutta la sezione di creazione del socket -perché nel server l'unica chiamata ad una system call lenta, che può essere -interrotta dall'arrivo di \signal{SIGCHLD}, è quella ad \func{accept}, che è -l'unica funzione che può mettere il processo padre in stato di sleep nel +perché nel server l'unica chiamata ad una \textit{system call} lenta, che può +essere interrotta dall'arrivo di \signal{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 - 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 \signal{SIGCHLD}. +\textit{system call} lente (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 \signal{SIGCHLD}. Per questo l'unica modifica sostanziale nel ciclo principale (\texttt{\small 23--42}), rispetto precedente versione di fig.~\ref{fig:TCP_ServEcho_first}, @@ -2281,13 +2274,12 @@ 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 \textit{three - way handshake} \itindex{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. + 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 @@ -2391,33 +2383,32 @@ 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 \itindex{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 \itindex{advertised~window} \textit{advertised 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 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. +client, e corrispondono ai tre pacchetti del \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{advertised 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 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 \itindex{three~way~handshake} \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. +del \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 \signal{SIGTERM}): nel momento in cui @@ -2466,15 +2457,16 @@ avanti in sez.~\ref{sec:TCP_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 argomento un - altro comando e ne stampa a video tutte le invocazioni di una system call, - coi relativi argomenti 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. + altro comando e ne stampa a video tutte le invocazioni di una \textit{system + call}, coi relativi argomenti 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 @@ -2489,12 +2481,12 @@ Per capire come questa avvenga comunque, non avendo inserito nel codice nessun controllo di errore, occorre ricordare che, a parte la bidirezionalità del flusso dei dati, dal punto di vista del funzionamento nei confronti delle funzioni di lettura e scrittura, i socket sono del tutto analoghi a delle -pipe. Allora, da quanto illustrato in sez.~\ref{sec:ipc_pipes}, sappiamo che -tutte le volte che si cerca di scrivere su una pipe il cui altro capo non è -aperto il lettura il processo riceve un segnale di \signal{SIGPIPE}, e questo è -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. +\textit{pipe}. Allora, da quanto illustrato in sez.~\ref{sec:ipc_pipes}, +sappiamo che tutte le volte che si cerca di scrivere su una \textit{pipe} il +cui altro capo non è aperto il lettura il processo riceve un segnale di +\signal{SIGPIPE}, e questo è 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 @@ -2617,8 +2609,7 @@ 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 -\sysctlrelfile{net/ipv4}{tcp\_retries2} (vedi +in questo caso in particolare da \sysctlrelfile{net/ipv4}{tcp\_retries2} (vedi sez.~\ref{sec:sock_ipv4_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 @@ -2770,12 +2761,12 @@ sappiamo che la funzione ritorna quando uno o più dei file descriptor messi sotto controllo è pronto per la relativa operazione. In quell'occasione non abbiamo però definito cosa si intende per pronto, -infatti per dei normali file, o anche per delle pipe, la condizione di essere -pronti per la lettura o la scrittura è ovvia; invece lo è molto meno nel caso -dei socket, visto che possono intervenire tutte una serie di possibili -condizioni di errore dovute alla rete. Occorre allora specificare chiaramente -quali sono le condizioni per cui un socket risulta essere ``\textsl{pronto}'' -quando viene passato come membro di uno dei tre \itindex{file~descriptor~set} +infatti per dei normali file, o anche per delle \textit{pipe}, la condizione +di essere pronti per la lettura o la scrittura è ovvia; invece lo è molto meno +nel caso dei socket, visto che possono intervenire tutte una serie di +possibili condizioni di errore dovute alla rete. Occorre allora specificare +chiaramente quali sono le condizioni per cui un socket risulta essere +``\textsl{pronto}'' quando viene passato come membro di uno dei tre \textit{file descriptor set} usati da \func{select}. Le condizioni che fanno si che la funzione \func{select} ritorni segnalando @@ -2837,9 +2828,8 @@ pronto per la scrittura sono le seguenti: Infine c'è una sola condizione che fa si che \func{select} ritorni segnalando che un socket (che sarà riportato nel terzo insieme di file descriptor) ha una condizione di eccezione pendente, e cioè la ricezione sul socket di -\textsl{dati urgenti} (o \itindex{out-of-band} \textit{out-of-band}), una -caratteristica specifica dei socket TCP su cui torneremo in -sez.~\ref{sec:TCP_urgent_data}. +\textsl{dati urgenti} (o \textit{out-of-band}), una caratteristica specifica +dei socket TCP su cui torneremo in sez.~\ref{sec:TCP_urgent_data}. Si noti come nel caso della lettura \func{select} si applichi anche ad operazioni che non hanno nulla a che fare con l'I/O di dati come il @@ -2914,27 +2904,27 @@ codice completo si trova nel file \file{TCP\_echo\_third.c} dei sorgenti allegati alla guida. In questo caso la funzione comincia (\texttt{\small 8--9}) con l'azzeramento -del \itindex{file~descriptor~set} \textit{file descriptor set} \var{fset} e -l'impostazione del valore \var{maxfd}, da passare a \func{select} come massimo -per il numero di file descriptor. Per determinare quest'ultimo si usa la macro -\code{max} definita nel nostro file \file{macro.h} che raccoglie una -collezione di macro di preprocessore di varia utilità. +del \textit{file descriptor set} \var{fset} e l'impostazione del valore +\var{maxfd}, da passare a \func{select} come massimo per il numero di file +descriptor. Per determinare quest'ultimo si usa la macro \code{max} definita +nel nostro file \file{macro.h} che raccoglie una collezione di macro di +preprocessore di varia utilità. La funzione prosegue poi (\texttt{\small 10--41}) con il ciclo principale, che viene ripetuto indefinitamente. Per ogni ciclo si reinizializza -(\texttt{\small 11--12}) il \itindex{file~descriptor~set} \textit{file - descriptor set}, impostando i valori per il file descriptor associato al -socket \var{socket} e per lo standard input (il cui valore si recupera con la -funzione \func{fileno}). Questo è necessario in quanto la successiva -(\texttt{\small 13}) chiamata a \func{select} comporta una modifica dei due -bit relativi, che quindi devono essere reimpostati all'inizio di ogni ciclo. +(\texttt{\small 11--12}) il \textit{file descriptor set}, impostando i valori +per il file descriptor associato al socket \var{socket} e per lo standard +input (il cui valore si recupera con la funzione \func{fileno}). Questo è +necessario in quanto la successiva (\texttt{\small 13}) chiamata a +\func{select} comporta una modifica dei due bit relativi, che quindi devono +essere reimpostati all'inizio di ogni ciclo. Si noti come la chiamata a \func{select} venga eseguita usando come primo argomento il valore di \var{maxfd}, precedentemente calcolato, e passando poi -il solo \itindex{file~descriptor~set} \textit{file descriptor set} per il -controllo dell'attività in lettura, negli altri argomenti sono passati tutti -puntatori nulli, non interessando né il controllo delle altre attività, né -l'impostazione di un valore di timeout. +il solo \textit{file descriptor set} per il controllo dell'attività in +lettura, negli altri argomenti sono passati tutti puntatori nulli, non +interessando né il controllo delle altre attività, né l'impostazione di un +valore di timeout. Al ritorno di \func{select} si provvede a controllare quale dei due file descriptor presenta attività in lettura, cominciando (\texttt{\small 14--24}) @@ -3039,13 +3029,15 @@ capi chiuda la connessione, quando l'altro capo la lascia aperta.\footnote{abbiamo incontrato questa situazione nei vari scenari critici di sez.~\ref{sec:TCP_echo_critical}.} +\itindbeg{half-close} + È pertanto possibile avere una situazione in cui un capo della connessione non avendo più nulla da scrivere, possa chiudere il socket, segnalando così l'avvenuta terminazione della trasmissione (l'altro capo riceverà infatti un -end-of-file in lettura) mentre dall'altra parte si potrà proseguire la -trasmissione dei dati scrivendo sul socket che da quel lato è ancora aperto. -Questa è quella situazione in cui si dice che il socket è \textit{half - closed}. +\textit{end-of-file} in lettura) mentre dall'altra parte si potrà proseguire +la trasmissione dei dati scrivendo sul socket che da quel lato è ancora +aperto. Questa è quella situazione in cui si dice che il socket è +``\textit{half closed}''. Il problema che si pone è che se la chiusura del socket è effettuata con la funzione \func{close}, come spiegato in sez.~\ref{sec:TCP_func_close}, si perde @@ -3073,24 +3065,26 @@ vuole operare e come secondo argomento un valore intero \param{how} che indica la modalità di chiusura del socket, quest'ultima può prendere soltanto tre valori: \begin{basedescript}{\desclabelwidth{2.2cm}\desclabelstyle{\nextlinelabel}} -\item[\const{SHUT\_RD}] chiude il lato in lettura del socket, non sarà più +\item[\constd{SHUT\_RD}] chiude il lato in lettura del socket, non sarà più possibile leggere dati da esso, tutti gli eventuali dati trasmessi dall'altro capo del socket saranno automaticamente scartati dal kernel, che, in caso di socket TCP, provvederà comunque ad inviare i relativi segmenti di ACK. -\item[\const{SHUT\_WR}] chiude il lato in scrittura del socket, non sarà più +\item[\constd{SHUT\_WR}] chiude il lato in scrittura del socket, non sarà più possibile scrivere dati su di esso. Nel caso di socket TCP la chiamata causa l'emissione di un segmento FIN, secondo la procedura chiamata - \itindex{half-close} \textit{half-close}. Tutti i dati presenti nel buffer - di scrittura prima della chiamata saranno inviati, seguiti dalla sequenza di - chiusura illustrata in sez.~\ref{sec:TCP_conn_term}. -\item[\const{SHUT\_RDWR}] chiude sia il lato in lettura che quello in + \textit{half-close}. Tutti i dati presenti nel buffer di scrittura prima + della chiamata saranno inviati, seguiti dalla sequenza di chiusura + illustrata in sez.~\ref{sec:TCP_conn_term}. +\item[\constd{SHUT\_RDWR}] chiude sia il lato in lettura che quello in scrittura del socket. È equivalente alla chiamata in sequenza con \const{SHUT\_RD} e \const{SHUT\_WR}. \end{basedescript} +\itindend{half-close} + Ci si può chiedere quale sia l'utilità di avere introdotto \const{SHUT\_RDWR} -quando questa sembra rendere \funcd{shutdown} del tutto equivalente ad una +quando questa sembra rendere \func{shutdown} del tutto equivalente ad una \func{close}. In realtà non è così, esiste infatti un'altra differenza con \func{close}, più sottile. Finora infatti non ci siamo presi la briga di sottolineare in maniera esplicita che, come per i file e le fifo, anche per i @@ -3210,8 +3204,8 @@ precedente versione causava l'immediato ritorno della funzione; in questo caso prima (\texttt{\small 19}) si imposta opportunamente \var{eof} ad un valore non nullo, dopo di che (\texttt{\small 20}) si effettua la chiusura del lato in scrittura del socket con \func{shutdown}. Infine (\texttt{\small 21}) si -usa la macro \macro{FD\_CLR} per togliere lo standard input dal -\itindex{file~descriptor~set} \textit{file descriptor set}. +usa la macro \macro{FD\_CLR} per togliere lo standard input dal \textit{file + descriptor set}. In questo modo anche se la lettura del file in ingresso è conclusa, la funzione non esce dal ciclo principale (\texttt{\small 11--50}), ma continua @@ -3292,15 +3286,14 @@ aperti viene impostato a quello del socket in ascolto,\footnote{in quanto esso alto.} che verrà anche (\texttt{\small 4}) inserito nella tabella. La prima sezione (\texttt{\small 7--10}) del ciclo principale esegue la -costruzione del \itindex{file~descriptor~set} \textit{file descriptor set} -\var{fset} in base ai socket connessi in un certo momento; all'inizio ci sarà -soltanto il socket in ascolto, ma nel prosieguo delle operazioni, verranno -utilizzati anche tutti i socket connessi registrati nella tabella -\var{fd\_open}. Dato che la chiamata di \func{select} modifica il valore del -\itindex{file~descriptor~set} \textit{file descriptor set}, è necessario -ripetere (\texttt{\small 7}) ogni volta il suo azzeramento, per poi procedere -con il ciclo (\texttt{\small 8--10}) in cui si impostano i socket trovati -attivi. +costruzione del \textit{file descriptor set} \var{fset} in base ai socket +connessi in un certo momento; all'inizio ci sarà soltanto il socket in +ascolto, ma nel prosieguo delle operazioni, verranno utilizzati anche tutti i +socket connessi registrati nella tabella \var{fd\_open}. Dato che la chiamata +di \func{select} modifica il valore del \textit{file descriptor set}, è +necessario ripetere (\texttt{\small 7}) ogni volta il suo azzeramento, per poi +procedere con il ciclo (\texttt{\small 8--10}) in cui si impostano i socket +trovati attivi. Per far questo si usa la caratteristica dei file descriptor, descritta in sez.~\ref{sec:file_open_close}, per cui il kernel associa sempre ad ogni nuovo @@ -3350,13 +3343,13 @@ vi sono dati sui socket connessi, per questo si ripete un ciclo diverso da zero; in questo modo se l'unico socket con attività era quello connesso, avendo opportunamente decrementato il contatore, il ciclo verrà saltato, e si ritornerà immediatamente (ripetuta l'inizializzazione del -\itindex{file~descriptor~set} \textit{file descriptor set} con i nuovi valori -nella tabella) alla chiamata di \func{accept}. Se il socket attivo non è -quello in ascolto, o ce ne sono comunque anche altri, il valore di \var{n} non -sarà nullo ed il controllo sarà eseguito. Prima di entrare nel ciclo comunque -si inizializza (\texttt{\small 28}) il valore della variabile \var{i} che -useremo come indice nella tabella \var{fd\_open} al valore minimo, -corrispondente al file descriptor del socket in ascolto. +\textit{file descriptor set} con i nuovi valori nella tabella) alla chiamata +di \func{accept}. Se il socket attivo non è quello in ascolto, o ce ne sono +comunque anche altri, il valore di \var{n} non sarà nullo ed il controllo sarà +eseguito. Prima di entrare nel ciclo comunque si inizializza (\texttt{\small + 28}) il valore della variabile \var{i} che useremo come indice nella tabella +\var{fd\_open} al valore minimo, corrispondente al file descriptor del socket +in ascolto. Il primo passo (\texttt{\small 30}) nella verifica è incrementare il valore dell'indice \var{i} per posizionarsi sul primo valore possibile per un file @@ -3402,18 +3395,17 @@ successiva \func{select} ritornerà immediatamente segnalando l'ulteriore disponibilità. Il nostro server comunque soffre di una vulnerabilità per un attacco di tipo -\itindex{Denial~of~Service~(DoS)} \textit{Denial of Service}. Il problema è -che in caso di blocco di una qualunque delle funzioni di I/O, non avendo usato -processi separati, tutto il server si ferma e non risponde più a nessuna -richiesta. Abbiamo scongiurato questa evenienza per l'I/O in ingresso con -l'uso di \func{select}, ma non vale altrettanto per l'I/O in uscita. Il -problema pertanto può sorgere qualora una delle chiamate a \func{write} -effettuate da \func{FullWrite} si blocchi. Con il funzionamento normale questo -non accade in quanto il server si limita a scrivere quanto riceve in ingresso, -ma qualora venga utilizzato un client malevolo che esegua solo scritture e non -legga mai indietro l'\textsl{eco} del server, si potrebbe giungere alla -saturazione del buffer di scrittura, ed al conseguente blocco del server su di -una \func{write}. +\textit{Denial of Service}. Il problema è che in caso di blocco di una +qualunque delle funzioni di I/O, non avendo usato processi separati, tutto il +server si ferma e non risponde più a nessuna richiesta. Abbiamo scongiurato +questa evenienza per l'I/O in ingresso con l'uso di \func{select}, ma non vale +altrettanto per l'I/O in uscita. Il problema pertanto può sorgere qualora una +delle chiamate a \func{write} effettuate da \func{FullWrite} si blocchi. Con +il funzionamento normale questo non accade in quanto il server si limita a +scrivere quanto riceve in ingresso, ma qualora venga utilizzato un client +malevolo che esegua solo scritture e non legga mai indietro l'\textsl{eco} del +server, si potrebbe giungere alla saturazione del buffer di scrittura, ed al +conseguente blocco del server su di una \func{write}. Le possibili soluzioni in questo caso sono quelle di ritornare ad eseguire il ciclo di risposta alle richieste all'interno di processi separati, utilizzare @@ -3432,8 +3424,7 @@ maggior parte dei casi, in quanto essa è nata sotto BSD proprio per affrontare queste problematiche con i socket. Abbiamo però visto in sez.~\ref{sec:file_multiplexing} come la funzione \func{poll} possa costituire una alternativa a \func{select}, con alcuni vantaggi.\footnote{non soffrendo - delle limitazioni dovute all'uso dei \itindex{file~descriptor~set} - \textit{file descriptor set}.} + delle limitazioni dovute all'uso dei \textit{file descriptor set}.} Ancora una volta in sez.~\ref{sec:file_poll} abbiamo trattato la funzione in maniera generica, parlando di file descriptor, ma come per \func{select} @@ -3446,7 +3437,7 @@ pertanto: \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 urgenti \itindex{out-of-band} \textit{out-of-band} (vedi +\item i dati urgenti \textit{out-of-band} (vedi sez.~\ref{sec:TCP_urgent_data}) su un socket TCP vengono considerati traffico prioritario e vengono rilevati da una condizione \const{POLLIN}, \const{POLLPRI} o \const{POLLRDBAND}. @@ -3569,11 +3560,9 @@ uscita e notifica in caso si errore (\texttt{\small 49--52}). Come si può notare la logica del programma è identica a quella vista in fig.~\ref{fig:TCP_SelectEchod} per l'analogo server basato su \func{select}; la sola differenza significativa è che in questo caso non c'è bisogno di -rigenerare i \itindex{file~descriptor~set} \textit{file descriptor set} in -quanto l'uscita è indipendente dai dati in ingresso. Si applicano comunque -anche a questo server le considerazioni finali di -sez.~\ref{sec:TCP_serv_select}. - +rigenerare i \textit{file descriptor set} in quanto l'uscita è indipendente +dai dati in ingresso. Si applicano comunque anche a questo server le +considerazioni finali di sez.~\ref{sec:TCP_serv_select}.