X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=tcpsock.tex;h=dfa8540bf72873a0369106d839ff6fe9115d0378;hp=695603e744b7bb9b3f4dc81aca2098b5acd431d6;hb=9949b501aea36905b12f069e11743b70b3e2df57;hpb=4cbeb0e4fa1d31da798c8e68108eb6785586ab34 diff --git a/tcpsock.tex b/tcpsock.tex index 695603e..dfa8540 100644 --- a/tcpsock.tex +++ b/tcpsock.tex @@ -1,6 +1,6 @@ %% tcpsock.tex %% -%% Copyright (C) 2000-2011 Simone Piccardi. Permission is granted to +%% Copyright (C) 2000-2014 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", @@ -135,12 +135,13 @@ 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} - \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 attraverso l'opzione del socket - \const{TCP\_MAXSEG} (vedi sez.~\ref{sec:sock_tcp_udp_options}). +\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 + 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 @@ -179,8 +180,8 @@ connessione. Normalmente vengono usate le seguenti opzioni: \end{itemize} -La MSS \itindex{Maximum~Segment~Size} è generalmente supportata da quasi tutte -le implementazioni del protocollo, le ultime due opzioni (trattate +La MSS \itindex{Maximum~Segment~Size~(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 @@ -304,9 +305,9 @@ che il protocollo viene ad assumere per i due lati, server e client. \end{figure} La connessione viene iniziata dal client che annuncia una -\itindex{Maximum~Segment~Size} 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). +\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). Una volta che la connessione è stabilita il client scrive al server una richiesta (che assumiamo stare in un singolo segmento, cioè essere minore dei @@ -477,11 +478,10 @@ dall'\href{http://www.ietf.org/rfc/rfc1700.txt}{RFC~1700} che contiene l'elenco delle porte assegnate dalla IANA (la \textit{Internet Assigned Number Authority}) ma l'elenco viene costantemente aggiornato e pubblicato su internet (una versione aggiornata si può trovare all'indirizzo -\href{http://www.iana.org/assignments/port-numbers} -{\textsf{http://www.iana.org/assignments/port-numbers}}); inoltre in un -sistema unix-like un analogo elenco viene mantenuto nel file -\conffile{/etc/services}, con la corrispondenza fra i vari numeri di porta ed -il nome simbolico del servizio. I numeri sono divisi in tre intervalli: +\url{http://www.iana.org/assignments/port-numbers}); inoltre in un sistema +unix-like un analogo elenco viene mantenuto nel file \conffile{/etc/services}, +con la corrispondenza fra i vari numeri di porta ed il nome simbolico del +servizio. I numeri sono divisi in tre intervalli: \begin{enumerate*} \item \textsl{le porte note}. I numeri da 0 a 1023. Queste sono controllate e @@ -519,7 +519,7 @@ un socket solo da un processo con i privilegi di amministratore, per far sì che solo l'amministratore possa allocare queste porte per far partire i relativi servizi. -Le \textsl{glibc} definiscono (in \texttt{netinet/in.h}) +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 @@ -578,7 +578,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 \file{arpa/inet.h} (vedi \ref{tab:TCP_ipv4_addr}). +definito in \headfile{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 @@ -731,7 +731,7 @@ 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{endianess} l'\textit{endianess} della macchina, ed anche se esse +\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}. @@ -761,7 +761,7 @@ metodo con IPv6, in cui l'indirizzo deve necessariamente essere specificato 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 \file{netinet/in.h} è definita una variabile +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 assegnazione del tipo: \includecodesnip{listati/serv_addr_sin6_addr.c} in @@ -839,7 +839,7 @@ nella chiamata della funzione sono le seguenti: secondi per un numero di volte che può essere stabilito dall'utente. Questo può essere fatto a livello globale con una opportuna \func{sysctl},\footnote{o più semplicemente scrivendo il valore voluto in - \procfile{/proc/sys/net/ipv4/tcp\_syn\_retries}, vedi + \sysctlfile{net/ipv4/tcp\_syn\_retries}, vedi sez.~\ref{sec:sock_ipv4_sysctl}.} e a livello di singolo socket con l'opzione \const{TCP\_SYNCNT} (vedi sez.~\ref{sec:sock_tcp_udp_options}). Il valore predefinito per la ripetizione dell'invio è di 5 volte, che comporta @@ -973,9 +973,9 @@ 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 -\procfile{/proc/sys/net/ipv4/tcp\_max\_syn\_backlog}. Quando si attiva la +\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 \procfile{/proc/sys/net/ipv4/tcp\_syncookies}) questo valore +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 @@ -1101,8 +1101,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 @@ -1222,9 +1223,9 @@ socket BSD fanno questa assunzione. \subsection{La funzione \func{close}} \label{sec:TCP_func_close} -La funzione standard Unix \func{close} (vedi sez.~\ref{sec:file_close}) che si -usa sui file può essere usata con lo stesso effetto anche sui file descriptor -associati ad un socket. +La funzione standard Unix \func{close} (vedi sez.~\ref{sec:file_open_close}) +che si usa sui file può essere usata con lo stesso effetto anche sui file +descriptor associati ad un socket. L'azione di questa funzione quando applicata a socket è di marcarlo come chiuso e ritornare immediatamente al processo. Una volta chiamata il socket @@ -1241,9 +1242,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 @@ -1384,7 +1385,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. @@ -1537,8 +1538,8 @@ quello in questione, non è opportuno bloccare un server nel servizio di un client per volta; per questo si ricorre alle capacità di multitasking del sistema. -Come accennato anche in sez.~\ref{sec:proc_gen} una delle modalità più comuni -di funzionamento da parte dei server è quella di usare la funzione \func{fork} +Come spiegato in sez.~\ref{sec:proc_fork} una delle modalità più comuni di +funzionamento da parte dei server è quella di usare la funzione \func{fork} per creare, ad ogni richiesta da parte di un client, un processo figlio che si incarichi della gestione della comunicazione. Si è allora riscritto il server \textit{daytime} dell'esempio precedente in forma concorrente, inserendo anche @@ -2010,7 +2011,7 @@ quando affronteremo il comportamento in caso di conclusioni anomale: restituendo un puntatore nullo che causa l'uscita dal ciclo di \code{while}, così la funzione \code{ClientEcho} ritorna. \item al ritorno di \code{ClientEcho} ritorna anche la funzione \code{main}, e - come parte del processo terminazione tutti i file descriptor vengono chiusi + come parte del processo di terminazione tutti i file descriptor vengono chiusi (si ricordi quanto detto in sez.~\ref{sec:proc_term_conclusion}); questo causa la chiusura del socket di comunicazione; il client allora invierà un FIN al server a cui questo risponderà con un ACK. A questo punto il client @@ -2038,19 +2039,19 @@ 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 \index{zombie} 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 \itindex{zombie} +\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 \index{zombie} 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 +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} +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 \code{Signal} (che abbiamo illustrato in fig.~\ref{fig:sig_Signal_code}), per installare il gestore che riceve i segnali dei processi figli terminati già @@ -2069,13 +2070,13 @@ 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 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 questo un errore fatale -terminando a sua volta con un messaggio del tipo: +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 +\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 +questo un errore fatale terminando a sua volta con un messaggio del tipo: \begin{verbatim} [root@gont sources]# ./echod -i accept error: Interrupted system call @@ -2618,7 +2619,7 @@ 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 -\procrelfile{/proc/sys/net/ipv4}{tcp\_retries2} (vedi +\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 @@ -3073,23 +3074,23 @@ 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[\macro{SHUT\_RD}] chiude il lato in lettura del socket, non sarà più +\item[\const{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[\macro{SHUT\_WR}] chiude il lato in scrittura del socket, non sarà più +\item[\const{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[\macro{SHUT\_RDWR}] chiude sia il lato in lettura che quello in +\item[\const{SHUT\_RDWR}] chiude sia il lato in lettura che quello in scrittura del socket. È equivalente alla chiamata in sequenza con - \macro{SHUT\_RD} e \macro{SHUT\_WR}. + \const{SHUT\_RD} e \const{SHUT\_WR}. \end{basedescript} -Ci si può chiedere quale sia l'utilità di avere introdotto \macro{SHUT\_RDWR} +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 \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 @@ -3116,7 +3117,7 @@ dato che restano altri riferimenti attivi, uno al socket connesso nel figlio ed uno a quello in ascolto nel padre. Questo non avviene affatto se si usa \func{shutdown} con argomento -\macro{SHUT\_RDWR} al posto di \func{close}; in questo caso infatti la +\const{SHUT\_RDWR} al posto di \func{close}; in questo caso infatti la chiusura del socket viene effettuata immediatamente, indipendentemente dalla presenza di altri riferimenti attivi, e pertanto sarà efficace anche per tutti gli altri file descriptor con cui, nello stesso o in altri processi, si fa @@ -3144,7 +3145,7 @@ velocità consentitagli dalla rete, sul socket. Dato che la connessione è con una macchina remota occorre un certo tempo perché i pacchetti vi arrivino, vengano processati, e poi tornino indietro. Considerando trascurabile il tempo di processo, questo tempo è quello impiegato nella trasmissione via rete, che -viene detto RTT (dalla denominazione inglese \itindex{Round~Trip~Time} +viene detto RTT (dalla denominazione inglese \itindex{Round~Trip~Time~(RTT)} \textit{Round Trip Time}) ed è quello che viene stimato con l'uso del comando \cmd{ping}. @@ -3303,17 +3304,17 @@ 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}, per cui il kernel associa sempre ad ogni nuovo file -il file descriptor con il valore più basso disponibile. Questo fa sì che si -possa eseguire il ciclo (\texttt{\small 8}) a partire da un valore minimo, che -sarà sempre quello del socket in ascolto, mantenuto in \var{list\_fd}, fino al -valore massimo di \var{max\_fd} che dovremo aver cura di tenere aggiornato. -Dopo di che basterà controllare (\texttt{\small 9}) nella nostra tabella se il -file descriptor è in uso o meno,\footnote{si tenga presente che benché il - kernel assegni sempre il primo valore libero, dato che nelle operazioni i - socket saranno aperti e chiusi in corrispondenza della creazione e - conclusione delle connessioni, si potranno sempre avere dei \textsl{buchi} - nella nostra tabella.} e impostare \var{fset} di conseguenza. +sez.~\ref{sec:file_open_close}, per cui il kernel associa sempre ad ogni nuovo +file il file descriptor con il valore più basso disponibile. Questo fa sì che +si possa eseguire il ciclo (\texttt{\small 8}) a partire da un valore minimo, +che sarà sempre quello del socket in ascolto, mantenuto in \var{list\_fd}, +fino al valore massimo di \var{max\_fd} che dovremo aver cura di tenere +aggiornato. Dopo di che basterà controllare (\texttt{\small 9}) nella nostra +tabella se il file descriptor è in uso o meno,\footnote{si tenga presente che + benché il kernel assegni sempre il primo valore libero, dato che nelle + operazioni i socket saranno aperti e chiusi in corrispondenza della + creazione e conclusione delle connessioni, si potranno sempre avere dei + \textsl{buchi} nella nostra tabella.} e impostare \var{fset} di conseguenza. Una volta inizializzato con i socket aperti il nostro \textit{file descriptor set} potremo chiamare \func{select} per fargli osservare lo stato degli @@ -3577,7 +3578,7 @@ sez.~\ref{sec:TCP_serv_select}. -\subsection{I/O multiplexing con \func{epoll}} +\subsection{I/O multiplexing con \textit{epoll}} \label{sec:TCP_serv_epoll} Da fare. @@ -3601,7 +3602,7 @@ Da fare. % LocalWords: lsof SOCK sys int sockfd const struct sockaddr serv addr socklen % LocalWords: addrlen errno EBADF descriptor EINVAL ENOTSOCK EACCES EADDRINUSE % LocalWords: EADDRNOTAVAIL EFAULT ENOTDIR ENOENT ENOMEM ELOOP ENOSR EROFS RPC -% LocalWords: portmapper htonl tab endianess BROADCAST broadcast any extern fd +% LocalWords: portmapper htonl tab endianness BROADCAST broadcast any extern fd % LocalWords: ADRR INIT DGRAM SEQPACKET servaddr ECONNREFUSED ETIMEDOUT EAGAIN % LocalWords: ENETUNREACH EINPROGRESS EALREADY EAFNOSUPPORT EPERM EISCONN proc % LocalWords: sysctl filesystem syn retries reset ICMP backlog EOPNOTSUPP RECV