From 5077cb02e6a65499d7fddcd01d68e8800c73a6fa Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Wed, 5 Oct 2016 09:42:13 +0000 Subject: [PATCH] Modifiche aeroportuali... --- tcpsock.tex | 209 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 131 insertions(+), 78 deletions(-) diff --git a/tcpsock.tex b/tcpsock.tex index 2ebac7b..09759f4 100644 --- a/tcpsock.tex +++ b/tcpsock.tex @@ -1,4 +1,4 @@ -%% tcpsock.tex +S%% tcpsock.tex %% %% Copyright (C) 2000-2016 Simone Piccardi. Permission is granted to %% copy, distribute and/or modify this document under the terms of the GNU Free @@ -1051,12 +1051,13 @@ TCP. \subsection{La funzione \func{accept}} \label{sec:TCP_func_accept} -La funzione \funcd{accept} è chiamata da un server per gestire la connessione -una volta che sia stato completato il \textit{three way - handshake},\footnote{come le precedenti, la funzione è generica ed è +La funzione \funcd{accept} è chiamata da un server per gestire la connessione, +nel caso di TCP una volta che sia stato completato il \textit{three way + handshake};\footnote{come le precedenti, la funzione è 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 + e \const{SOCK\_RDM}, ma qui la tratteremo solo per gli aspetti riguardanti + le connessioni con TCP.} 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: @@ -1072,11 +1073,15 @@ funzione è il seguente: \begin{errlist} \item[\errcode{EAGAIN} o \errcode{EWOULDBLOCK}] il socket è stato impostato come non bloccante (vedi sez.~\ref{sec:file_noblocking}), e non ci sono - connessioni in attesa di essere accettate. + connessioni in attesa di essere accettate. In generale possono essere + restituiti entrambi i valori, per cui se si ha a cuore la portabilità + occorre controllare entrambi. \item[\errcode{EBADF}] l'argomento \param{sockfd} non è un file descriptor valido. \item[\errcode{ECONNABORTED}] la connessione è stata abortita. \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. + \item[\errcode{EINVAL}] il socket non è in ascolto o \param{addrlen} non ha + un valore valido. \item[\errcode{ENOBUFS}, \errcode{ENOMEM}] questo spesso significa che l'allocazione della memoria è limitata dai limiti sui buffer dei socket, non dalla memoria di sistema. @@ -1085,11 +1090,11 @@ funzione è il seguente: operazione. \item[\errcode{EPERM}] le regole del firewall non consentono la connessione. \end{errlist} - ed inoltre nel loro significato generico: \errval{EFAULT}, \errval{EINVAL}, - \errval{EMFILE}, \errval{ENFILE}; infine a seconda del protocollo e del - kernel possono essere restituiti errori di rete relativi al nuovo socket - come: \errval{ENOSR}, \errval{ESOCKTNOSUPPORT}, \errval{EPROTONOSUPPORT}, - \errval{ETIMEDOUT}, \errval{ERESTARTSYS}.} + ed inoltre nel loro significato generico: \errval{EFAULT}, \errval{EMFILE}, + \errval{ENFILE}; infine a seconda del protocollo e del kernel possono essere + restituiti errori di rete relativi al nuovo socket come: \errval{ENOSR}, + \errval{ESOCKTNOSUPPORT}, \errval{EPROTONOSUPPORT}, \errval{ETIMEDOUT}, + \errval{ERESTARTSYS}.} \end{funcproto} La funzione estrae la prima connessione relativa al socket \param{sockfd} in @@ -1101,12 +1106,11 @@ posto nello stato \texttt{ESTABLISHED}. I due argomenti \param{addr} e \param{addrlen} (si noti che quest'ultimo è un \textit{valure-result argument} passato con un puntatore per riavere indietro il valore) sono usati rispettivamente per ottenere l'indirizzo del client da -cui proviene la connessione e la lunghezza dello stesso (la dimensione dipende -da quale famiglia di indirizzi si sta utilizzando). - -Prima della chiamata \param{addrlen} deve essere inizializzato alle dimensioni -della struttura degli indirizzi cui punta \param{addr}; al ritorno della -funzione \param{addrlen} conterrà il numero di byte scritti +cui proviene la connessione e la lunghezza dello stesso; la dimensione dipende +da quale famiglia di indirizzi si sta utilizzando. Prima della +chiamata \param{addrlen} deve essere inizializzato alle dimensioni della +struttura degli indirizzi cui punta \param{addr} (un numero positivo); al +ritorno della funzione \param{addrlen} conterrà il numero di byte scritti dentro \param{addr}. Se questa informazione non interessa basterà inizializzare a \val{NULL} detti puntatori. @@ -1130,20 +1134,66 @@ esplicita della connessione,\footnote{attualmente in Linux solo DECnet ha questo comportamento.} la funzione opera solo l'estrazione dalla coda delle connessioni, la conferma della connessione viene eseguita implicitamente dalla prima chiamata ad una \func{read} o una \func{write}, mentre il rifiuto della -connessione viene eseguito con la funzione \func{close}. - -È da chiarire che Linux presenta un comportamento diverso nella gestione degli -errori rispetto ad altre realizzazioni dei socket BSD, infatti la funzione -\func{accept} passa gli errori di rete pendenti sul nuovo socket come codici -di errore per \func{accept}, per cui l'applicazione deve tenerne conto ed -eventualmente ripetere la chiamata alla funzione come per l'errore di -\errcode{EAGAIN} (torneremo su questo in sez.~\ref{sec:TCP_echo_critical}). +connessione viene eseguito con la funzione \func{close}. + +Si tenga presente che con Linux, seguendo POSIX.1, è sufficiente includere +\texttt{sys/socket.h}, ma alcune implementazioni di altri sistemi possono +richiedere l'inclusione di \texttt{sys/types.h}, per cui dovendo curare la +portabilità può essere il caso di includere anche questo file. Inoltre Linux +presenta un comportamento diverso nella gestione degli errori rispetto ad +altre realizzazioni dei socket BSD, infatti la funzione \func{accept} passa +gli errori di rete pendenti sul nuovo socket come codici di errore per +\func{accept}, per cui l'applicazione deve tenerne conto ed eventualmente +ripetere la chiamata alla funzione come per l'errore di \errcode{EAGAIN} +(torneremo su questo in sez.~\ref{sec:TCP_echo_critical}). + 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_ioctl}.} che devono essere rispecificati ogni volta. Tutto questo deve essere tenuto in conto se si devono scrivere -programmi portabili. +programmi portabili. Per poter effettuare questa impostazione in maniera +atomica, senza dover ricorrere ad ulteriori chiamate a \func{fcntl} su Linux è +disponibile anche la funzione \funcd{accept4}, il cui prototipo è:\footnote{la + funzione è utilizzabile solo se si è definito la macro \macro{\_GNU\_SOURCE} + ed ovviamente non è portabile.} + +\begin{funcproto}{ +\fhead{sys/socket.h} +\fdecl{int accept4(int sockfd, struct sockaddr *addr, socklen\_t *addrlen, int + flags)} +\fdesc{Accetta una connessione sul socket specificato.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà gli stessi valori di \func{accept}.} +\end{funcproto} + +La funzione aggiunge un quarto argomento \param{flags} usato come maschera +binaria, e se questo è nullo il suo comportamento è identico a quello di +\func{accept}. Con \param{flags} si possono impostare contestualmente +all'esecuzione sul file descriptor restituito i due flag di +\const{O\_NONBLOCK} e \const{O\_CLOEXEC}, fornendo un valore che sia un OR +aritmetico delle costanti in tab.\ref{tab:accept4_flags_arg}. + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|l|} + \hline + \textbf{Costante} & \textbf{Significato} \\ + \hline + \hline + \constd{SOCK\_NONBLOCK} & imposta sul file descriptor restituito il flag + di \const{O\_NONBLOCK}\\ + \constd{SOCK\_NOXEC} & imposta sul file descriptor restituito il flag + di \const{O\_CLOEXEC}\\ + \hline + \end{tabular} + \caption{Costanti per i possibili valori dell'argomento \param{flags} di + \func{accept4}.} + \label{tab:accept4_flags_arg} +\end{table} Il meccanismo di funzionamento di \func{accept} è essenziale per capire il funzionamento di un server: in generale infatti c'è sempre un solo socket in @@ -1164,66 +1214,71 @@ dati. Oltre a tutte quelle viste finora, dedicate all'utilizzo dei socket, esistono alcune funzioni ausiliarie che possono essere usate per recuperare alcune informazioni relative ai socket ed alle connessioni ad essi associate. Le due -funzioni più elementari sono queste, che vengono usate per ottenere i dati -relativi alla \textit{socket pair} associata ad un certo socket. +più elementari sono le seguenti, usate per ottenere i dati relativi alla +\textit{socket pair} associata ad un certo socket. La prima è +\funcd{getsockname} e serve ad ottenere l'indirizzo locale associato ad un +socket; il suo prototipo è: -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)} - Legge l'indirizzo locale di un socket. +\begin{funcproto}{ +\fhead{sys/socket.h} +\fdecl{int getsockname(int sockfd, struct sockaddr *name, socklen\_t *namelen)} +\fdesc{Legge l'indirizzo locale di un socket.} +} -\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di - errore. I codici di errore restituiti in \var{errno} sono i seguenti: +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: \begin{errlist} \item[\errcode{EBADF}] l'argomento \param{sockfd} non è un file descriptor valido. - \item[\errcode{ENOTSOCK}] l'argomento \param{sockfd} non è un socket. \item[\errcode{ENOBUFS}] non ci sono risorse sufficienti nel sistema per + \item[\errcode{ENOTSOCK}] l'argomento \param{sockfd} non è un socket. eseguire l'operazione. - \item[\errcode{EFAULT}] l'indirizzo \param{name} non è valido. - \end{errlist}} -\end{prototype} + \end{errlist} + ed \errval{EFAULT} nel suo significato generico.} +\end{funcproto} La funzione restituisce la struttura degli indirizzi del socket \param{sockfd} nella struttura indicata dal puntatore \param{name} la cui lunghezza è -specificata tramite l'argomento \param{namlen}. Quest'ultimo viene passato -come indirizzo per avere indietro anche il numero di byte effettivamente -scritti nella struttura puntata da \param{name}. Si tenga presente che se si è -utilizzato un buffer troppo piccolo per \param{name} l'indirizzo risulterà -troncato. +specificata tramite l'argomento \param{namlen}. Quest'ultimo è un +\textit{value result argument} e pertanto viene passato come indirizzo per +avere indietro anche il numero di byte effettivamente scritti nella struttura +puntata da \param{name}. Si tenga presente che se si è utilizzato un buffer +troppo piccolo per \param{name} l'indirizzo risulterà troncato. La funzione si usa tutte le volte che si vuole avere l'indirizzo locale di un socket; ad esempio può essere usata da un client (che usualmente non chiama -\func{bind}) per ottenere numero IP e porta locale associati al socket +\func{bind}) per ottenere l'indirizzo IP e la porta locale associati al socket restituito da una \func{connect}, o da un server che ha chiamato \func{bind} -su un socket usando 0 come porta locale per ottenere il numero di porta +su un socket usando $0$ come porta locale per ottenere il numero di porta effimera assegnato dal kernel. Inoltre quando un server esegue una \func{bind} su un indirizzo generico, se chiamata dopo il completamento di una connessione sul socket restituito da \func{accept}, restituisce l'indirizzo locale che il kernel ha assegnato a -quella connessione. +quella connessione. Invece tutte le volte che si vuole avere l'indirizzo +remoto di un socket si usa la funzione \funcd{getpeername}, il cui prototipo +è: -Tutte le volte che si vuole avere l'indirizzo remoto di un socket si usa la -funzione \funcd{getpeername}, il cui prototipo è: -\begin{prototype}{sys/socket.h} - {int getpeername(int sockfd, struct sockaddr * name, socklen\_t * namelen)} - Legge l'indirizzo remoto di un socket. - - \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di - errore. I codici di errore restituiti in \var{errno} sono i seguenti: +\begin{funcproto}{ +\fhead{sys/socket.h} +\fdecl{int getpeername(int sockfd, struct sockaddr * name, socklen\_t * + namelen)} +\fdesc{Legge l'indirizzo remoto di un socket.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: \begin{errlist} \item[\errcode{EBADF}] l'argomento \param{sockfd} non è un file descriptor valido. - \item[\errcode{ENOTSOCK}] l'argomento \param{sockfd} non è un socket. - \item[\errcode{ENOTCONN}] il socket non è connesso. \item[\errcode{ENOBUFS}] non ci sono risorse sufficienti nel sistema per eseguire l'operazione. - \item[\errcode{EFAULT}] l'argomento \param{name} punta al di fuori dello - spazio di indirizzi del processo. - \end{errlist}} -\end{prototype} + \item[\errcode{ENOTCONN}] il socket non è connesso. + \item[\errcode{ENOTSOCK}] l'argomento \param{sockfd} non è un socket. + \end{errlist} + ed \errval{EFAULT} nel suo significato generico.} +\end{funcproto} + La funzione è identica a \func{getsockname}, ed usa la stessa sintassi, ma restituisce l'indirizzo remoto del socket, cioè quello associato all'altro @@ -1237,10 +1292,10 @@ Il fatto è che in generale quest'ultimo caso non è sempre possibile. In particolare questo avviene quando il server, invece di gestire la connessione direttamente in un processo figlio, come vedremo nell'esempio di server concorrente di sez.~\ref{sec:TCP_daytime_cunc_server}, lancia per ciascuna -connessione un altro programma, usando \func{exec}.\footnote{questa ad esempio - è la modalità con cui opera il \textsl{super-server} \cmd{inetd}, che può - gestire tutta una serie di servizi diversi, eseguendo su ogni connessione - ricevuta sulle porte tenute sotto controllo, il relativo server.} +connessione un altro programma, usando \func{exec}. Questa ad esempio è la +modalità con cui opera il \textsl{super-server} \cmd{xinetd}, che può gestire +tutta una serie di servizi diversi, eseguendo su ogni connessione ricevuta +sulle porte tenute sotto controllo, il relativo server. In questo caso benché il processo figlio abbia una immagine della memoria che è copia di quella del processo padre (e contiene quindi anche la struttura @@ -1248,15 +1303,15 @@ ritornata da \func{accept}), all'esecuzione di \func{exec} verrà caricata in memoria l'immagine del programma eseguito, che a questo punto perde ogni riferimento ai valori tornati da \func{accept}. Il socket descriptor però resta aperto, e se si è seguita una opportuna convenzione per rendere noto al -programma eseguito qual è il socket connesso, \footnote{ad esempio il solito - \cmd{inetd} fa sempre in modo che i file descriptor 0, 1 e 2 corrispondano - al socket connesso.} quest'ultimo potrà usare la funzione \func{getpeername} -per determinare l'indirizzo remoto del client. +programma eseguito qual è il socket connesso (ad esempio il solito +\cmd{xinetd} fa sempre in modo che i file descriptor 0, 1 e 2 corrispondano al +socket connesso) quest'ultimo potrà usare la funzione \func{getpeername} per +determinare l'indirizzo remoto del client. Infine è da chiarire (si legga la pagina di manuale) che, come per \func{accept}, il terzo argomento, che è specificato dallo standard POSIX.1g come di tipo \code{socklen\_t *} in realtà deve sempre corrispondere ad un -\ctyp{int *} come prima dello standard perché tutte le realizzazioni dei +\ctyp{int *} come prima dello standard, perché tutte le realizzazioni dei socket BSD fanno questa assunzione. @@ -1272,11 +1327,10 @@ chiuso e ritornare immediatamente al processo. Una volta chiamata il socket descriptor non è più utilizzabile dal processo e non può essere usato come argomento per una \func{write} o una \func{read} (anche se l'altro capo della connessione non avesse chiuso la sua parte). Il kernel invierà comunque tutti -i dati che ha in coda prima di iniziare la sequenza di chiusura. - -Vedremo più avanti in sez.~\ref{sec:sock_generic_options} come sia possibile -cambiare questo comportamento, e cosa può essere fatto perché il processo -possa assicurarsi che l'altro capo abbia ricevuto tutti i dati. +i dati che ha in coda prima di iniziare la sequenza di chiusura. Vedremo più +avanti in sez.~\ref{sec:sock_generic_options} come sia possibile cambiare +questo comportamento, e cosa può essere fatto perché il processo possa +assicurarsi che l'altro capo abbia ricevuto tutti i dati. Come per tutti i file descriptor anche per i socket viene mantenuto un numero di riferimenti, per cui se più di un processo ha lo stesso socket aperto @@ -1292,7 +1346,6 @@ descritta in sez.~\ref{sec:TCP_conn_term}, si può invece usare la funzione sez.~\ref{sec:TCP_shutdown}). - \section{Un esempio elementare: il servizio \textit{daytime}} \label{sec:TCP_daytime_application} @@ -1376,7 +1429,7 @@ l'altro capo è stato chiuso, e quindi non sarà più possibile leggere niente) pertanto si ritorna senza aver concluso la lettura di tutti i byte richiesti. Entrambe le funzioni restituiscono 0 in caso di successo, ed un valore negativo in caso di errore, \func{FullRead} restituisce il numero di -byte non letti in caso di end-of-file prematuro. +byte non letti in caso di \textit{end-of-file} prematuro. \subsection{Il client \textit{daytime}} -- 2.30.2