From: Simone Piccardi Date: Sat, 18 Oct 2003 16:30:23 +0000 (+0000) Subject: Messi alcuni riferimenti giusti all'urgent data e scritta la versione di X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=commitdiff_plain;h=65cdd0498d1d9473110f86ebddede62c298e60dd Messi alcuni riferimenti giusti all'urgent data e scritta la versione di client che usa l'I/O multiplexing --- diff --git a/fileadv.tex b/fileadv.tex index 445d9c7..fb516a3 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -149,7 +149,7 @@ il primo, \param{readfds}, verr effettuare una lettura, il secondo, \param{writefds}, per verificare la possibilità effettuare una scrittura ed il terzo, \param{exceptfds}, per verificare l'esistenza di condizioni eccezionali (come i messaggi urgenti su -un \textit{socket}\index{socket}, vedi \secref{sec:xxx_urgent}). +un \textit{socket}\index{socket}, vedi \secref{sec:TCP_urgent_data}). Dato che in genere non si tengono mai sotto controllo fino a \const{FD\_SETSIZE} file contemporaneamente la funzione richiede di diff --git a/html/gapil.html b/html/gapil.html index 7024ee8..133db1e 100644 --- a/html/gapil.html +++ b/html/gapil.html @@ -151,7 +151,7 @@ . Grazie all'opera di Mirko Maischberger abbiamo anche una bellissima versione HTML, accessibile nella sezione online , finalmente + face="sans-serif"> online, finalmente all'altezza della versione stampabile. diff --git a/signal.tex b/signal.tex index 296d579..20c0f90 100644 --- a/signal.tex +++ b/signal.tex @@ -584,8 +584,8 @@ L'azione predefinita questo può essere usato anche per i file, posto che la \func{fcntl} abbia avuto successo. \item[\const{SIGURG}] Questo segnale è inviato quando arrivano dei dati - urgenti o \textit{out of band} su di un socket\index{socket}; per maggiori - dettagli al proposito si veda \secref{sec:xxx_urgent_data}. + urgenti o \textit{out-of-band} su di un socket\index{socket}; per maggiori + dettagli al proposito si veda \secref{sec:TCP_urgent_data}. \item[\const{SIGPOLL}] Questo segnale è equivalente a \const{SIGIO}, è definito solo per compatibilità con i sistemi System V. \end{basedescript} diff --git a/sources/TCP_echo.c b/sources/TCP_echo.c index 86b78c2..c8c5c1a 100644 --- a/sources/TCP_echo.c +++ b/sources/TCP_echo.c @@ -26,7 +26,7 @@ * * Usage: echo -h give all info's * - * $Id: TCP_echo.c,v 1.8 2003/08/17 23:03:44 piccardi Exp $ + * $Id: TCP_echo.c,v 1.9 2003/10/18 16:30:23 piccardi Exp $ * ****************************************************************/ /* @@ -40,6 +40,8 @@ #include /* include error codes */ #include /* include erroro strings definitions */ +#include "macros.h" + #define MAXLINE 256 void usage(void); void ClientEcho(FILE * filein, int socket); @@ -138,26 +140,41 @@ void ClientEcho(FILE * filein, int socket) { char sendbuff[MAXLINE+1], recvbuff[MAXLINE+1]; int nread, nwrite; - while (fgets(sendbuff, MAXLINE, filein) != NULL) { - nwrite = FullWrite(socket, sendbuff, strlen(sendbuff)); - if (nwrite < 0) { - printf("Errore in scrittura: %s", strerror(errno)); - return; - } - nread = read(socket, recvbuff, strlen(sendbuff)); - if (nread < 0) { - printf("Errore in lettura: %s\n", strerror(errno)); - return; - } - if (nread == 0) { - printf("EOF sul socket\n"); - return; + int maxfd; + fd_set fset; + /* initialize file descriptor set */ + FD_ZERO(&fset); + maxfd = max(fileno(stdin), socket) + 1; + while (1) { + FD_SET(socket, &fset); /* set for the socket */ + FD_SET(fileno(stdin), &fset); /* set for the standard input */ + select(maxfd, &fset, NULL, NULL, NULL); /* wait for read ready */ + if (FD_ISSET(fileno(stdin), &fset)) { /* if ready on stdin */ + if (fgets(sendbuff, MAXLINE, filein) == NULL) { /* if no input */ + return; /* we stopped client */ + } else { /* else we have to write to socket */ + nwrite = FullWrite(socket, sendbuff, strlen(sendbuff)); + if (nwrite < 0) { /* on error stop */ + printf("Errore in scrittura: %s", strerror(errno)); + return; + } + } } - recvbuff[nread] = 0; - if (fputs(recvbuff, stdout) == EOF) { - perror("Errore in scrittura su terminale"); - return; + if (FD_ISSET(socket, &fset)) { /* if ready on socket */ + nread = read(socket, recvbuff, strlen(sendbuff)); /* do read */ + if (nread < 0) { /* error condition, stop client */ + printf("Errore in lettura: %s\n", strerror(errno)); + return; + } + if (nread == 0) { /* server closed connection, stop */ + printf("EOF sul socket\n"); + return; + } + recvbuff[nread] = 0; /* else read is ok, write on stdout */ + if (fputs(recvbuff, stdout) == EOF) { + perror("Errore in scrittura su terminale"); + return; + } } } - return; } diff --git a/sources/TCP_echo_second.c b/sources/TCP_echo_second.c new file mode 100644 index 0000000..fcf5a4f --- /dev/null +++ b/sources/TCP_echo_second.c @@ -0,0 +1,163 @@ +/* TCP_echo.c + * + * Copyright (C) 2001-2003 Simone Piccardi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/**************************************************************** + * + * Program TCP_echo.c + * Simple TCP client for echo service (port 7) + * + * Author: Simone Piccardi + * Jun. 2001 + * + * Usage: echo -h give all info's + * + * $Id: TCP_echo_second.c,v 1.1 2003/10/18 16:30:23 piccardi Exp $ + * + ****************************************************************/ +/* + * Include needed headers + */ +#include /* predefined types */ +#include /* include unix standard library */ +#include /* IP addresses conversion utiliites */ +#include /* socket library */ +#include /* include standard I/O library */ +#include /* include error codes */ +#include /* include erroro strings definitions */ + +#define MAXLINE 256 +void usage(void); +void ClientEcho(FILE * filein, int socket); +void SigTERM_hand(int sig); + +/* Program begin */ +int main(int argc, char *argv[]) +{ +/* + * Variables definition + */ + int sock, i; + int reset = 0; + struct sockaddr_in serv_add; + struct linger ling; + /* + * Input section: decode parameters passed in the calling + * Use getopt function + */ + opterr = 0; /* don't want writing to stderr */ + while ( (i = getopt(argc, argv, "hr")) != -1) { + switch (i) { + /* + * Handling options + */ + case 'h': + printf("Wrong -h option use\n"); + usage(); + return(1); + break; + case 'r': + reset = 1; + break; + case '?': /* unrecognized options */ + printf("Unrecognized options -%c\n",optopt); + usage(); + default: /* should not reached */ + usage(); + } + } + /* *********************************************************** + * + * Options processing completed + * + * Main code beginning + * + * ***********************************************************/ + /* create socket */ + if ( (sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("Socket creation error"); + return 1; + } + /* initialize address */ + memset((void *) &serv_add, 0, sizeof(serv_add)); /* clear server address */ + serv_add.sin_family = AF_INET; /* address type is INET */ + serv_add.sin_port = htons(7); /* echo port is 7 */ + /* build address using inet_pton */ + if ( (inet_pton(AF_INET, argv[optind], &serv_add.sin_addr)) <= 0) { + perror("Address creation error"); + return 1; + } + /* extablish connection */ + if (connect(sock, (struct sockaddr *)&serv_add, sizeof(serv_add)) < 0) { + perror("Connection error"); + return 1; + } + /* check if resetting on close is required */ + if (reset) { + printf("Setting reset on close \n"); + ling.l_onoff = 1; + ling.l_linger = 0; + if (setsockopt(sock, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling))) { + perror("Cannot set linger"); + exit(1); + } + } + /* do read/write operations */ + ClientEcho(stdin, sock); + /* normal exit */ + return 0; +} +/* + * routine to print usage info and exit + */ +void usage(void) { + printf("Take daytime from a remote host \n"); + printf("Usage:\n"); + printf(" daytime [-h] [-v] [host in dotted decimal form] \n"); +// printf(" -v set verbosity on\n"); + printf(" -r require reset on closing\n"); + printf(" -h print this help\n"); + exit(1); +} + +void ClientEcho(FILE * filein, int socket) +{ + char sendbuff[MAXLINE+1], recvbuff[MAXLINE+1]; + int nread, nwrite; + while (fgets(sendbuff, MAXLINE, filein) != NULL) { + nwrite = FullWrite(socket, sendbuff, strlen(sendbuff)); + if (nwrite < 0) { + printf("Errore in scrittura: %s", strerror(errno)); + return; + } + nread = read(socket, recvbuff, strlen(sendbuff)); + if (nread < 0) { + printf("Errore in lettura: %s\n", strerror(errno)); + return; + } + if (nread == 0) { + printf("EOF sul socket\n"); + return; + } + recvbuff[nread] = 0; + if (fputs(recvbuff, stdout) == EOF) { + perror("Errore in scrittura su terminale"); + return; + } + } + return; +} diff --git a/sources/macros.h b/sources/macros.h index 9784c7a..1c978ed 100644 --- a/sources/macros.h +++ b/sources/macros.h @@ -23,10 +23,12 @@ (v>>24) | ((v>>8)&0xFF00) | (v<<24) | ((v<<8)&0xFF0000); } ) #define BE16_TO_LE16(val) ({typeof (val) v= (val); (v>>8) | (v<<8); } ) /* - * Define a protected, right typed, no side effects macro for min + * Define a protected, right typed, no side effects macro for min and max */ #define min(x, y) ({typeof (x) x_ = (x); typeof (y) y_ = (y); \ x_ < y_ ? x_ : y_;}) +#define max(x, y) ({typeof (x) x_ = (x); typeof (y) y_ = (y); \ + x_ > y_ ? x_ : y_;}) /* * debugging print definition */ diff --git a/tcpsock.tex b/tcpsock.tex index ae53fd0..2318fb0 100644 --- a/tcpsock.tex +++ b/tcpsock.tex @@ -8,7 +8,7 @@ %% license is included in the section entitled "GNU Free Documentation %% License". %% -\chapter{Socket TCP} +\chapter{Socket TCP elementari} \label{cha:TCP_socket} In questo capitolo iniziamo ad approfondire la conoscenza dei socket TCP, @@ -2430,7 +2430,7 @@ la nuova versione della funzione in \figref{fig:TCP_ClientEcho_second}. \end{minipage} \normalsize \caption{La sezione nel codice della seconda versione della funzione - \func{CleintEcho} usata dal client per il servizio \textit{echo} + \func{ClientEcho} usata dal client per il servizio \textit{echo} modificata per tener conto degli eventuali errori.} \label{fig:TCP_ClientEcho_second} \end{figure} diff --git a/tcpsockadv.tex b/tcpsockadv.tex index 24c45d2..4498652 100644 --- a/tcpsockadv.tex +++ b/tcpsockadv.tex @@ -12,45 +12,47 @@ \label{cha:TCP_advanced} Esamineremo in questo capitolo le funzionalità più evolute della gestione dei -socket TCP. +socket TCP, come l'uso del I/O multiplexing (trattato in +\secref{sec:file_multiplexing}) con i socket, l'uso delle opzioni dei socket e +la gestione dei dati urgenti e \textit{out-of-band}. -\section{Socket multiplexing} +\section{Socket I/O multiplexing} \label{sec:TCP_sock_multiplexing} Affronteremo in questa sezione l'utilizzo dell'I/O multiplexing, affrontato in \secref{sec:file_multiplexing}, nell'ambito delle applicazioni di rete. Già in -\ref{sec:TCP_server_crash} era emerso il problema relativo al client del +\secref{sec:TCP_server_crash} era emerso il problema relativo al client del servizio echo che non era in grado di accorgersi della terminazione precoce -del server essendo bloccato nella lettura dei dati immessi da tastiera. +del server, essendo bloccato nella lettura dei dati immessi da tastiera. Abbiamo visto in \secref{sec:file_multiplexing} quali sono le funzionalità del sistema che ci permettono di tenere sotto controllo più file descriptor in contemporanea; in quella occasione non abbiamo fatto esempi, in quanto quando -si tratta con file normali questa tipologia di I/O non viene usata, è invece -un caso tipico delle applicazioni di rete quello di dover gestire varie -connessioni da cui possono arrivare dati comuni in maniera asincrona, per cui -riprenderemo l'argomento in questa sezione. - +si tratta con file normali questa tipologia di I/O normalmente non viene +usata, è invece un caso tipico delle applicazioni di rete quello di dover +gestire varie connessioni da cui possono arrivare dati comuni in maniera +asincrona, per cui riprenderemo l'argomento in questa sezione. \subsection{Il comportamento della funzione \func{select} con i socket.} \label{sec:TCP_sock_select} Iniziamo con la prima delle funzioni usate per l'I/O multiplexing, -\func{select}, il suo funzionamento è già stato descritto in dettaglio in +\func{select}; il suo funzionamento è già stato descritto in dettaglio in \secref{sec:file_multiplexing} e non staremo a ripetere quanto detto lì; 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, lo è un po' meno di meno nel -caso dei socket, visto che intervengono tutte le possibili condizioni dovute -alla rete. Occorre allora specificare quali sono le condizioni in cui un -socket risulta \textsl{pronto} quando viene passato come membro di uno dei tre -\textit{file descriptor set} usati da \func{select}. +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 che un socket (che sarà riportato nel primo insieme di file descriptor) è @@ -80,8 +82,8 @@ pronto per la lettura sono le seguenti: \secref{sec:TCP_conn_early_abort} una connessione può essere abortita dalla ricezione di un segmento RST una volta che è stata completata, allora se questo avviene dopo che \func{select} è ritornata, ma prima - della chiamata ad \func{accept} quest'ultima, in assenza di altre - connessiioni, potrà bloccarsi.} + della chiamata ad \func{accept}, quest'ultima, in assenza di altre + connessioni, potrà bloccarsi.} \end{itemize*} Le condizioni che fanno si che la funzione \func{select} ritorni segnalando @@ -109,14 +111,14 @@ Infine c' 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 dati \textsl{fuori banda} (o \textit{out-of-band}), una caratteristica specifica -dei socket TCP su cui torneremo in \secref{sec:TCP_outofband}. +dei socket TCP su cui torneremo in \secref{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 come il riconoscimento -della presenza di connessioni pronte, in modo da consentire l'utilizzo di -\func{accept} in modalità non bloccante. Si noti infine come in caso di errore -un socket venga sempre riportato come pronto sia per la lettura che per la -scrittura. +operazioni che non hanno nulla a che fare con l'I/O di dati come il +riconoscimento della presenza di connessioni pronte, in modo da consentire +anche l'utilizzo di \func{accept} in modalità non bloccante. Si noti infine +come in caso di errore un socket venga sempre riportato come pronto sia per la +lettura che per la scrittura. Lo scopo dei due valori di soglia per i buffer di ricezione e di invio è quello di consentire maggiore flessibilità nell'uso di \func{select} da parte @@ -129,11 +131,95 @@ quando c' sempre quanti dati invia, mentre non è detto possa conoscere la quantità di dati in ricezione; per cui, nella situazione in cui si conosce almeno un valore minimo, per evitare la penalizzazione dovuta alla ripetizione delle - operazioni di lettura quando per accumulare dati sufficienti, si può - lasciare al kernel il compito di impostare un minimo al di sotto del quale - il file descriptor, pur avendo disponibili dei dati, non viene letto.} - - + operazioni di lettura per accumulare dati sufficienti, si può lasciare al + kernel il compito di impostare un minimo al di sotto del quale il file + descriptor, pur avendo disponibili dei dati, non viene dato per pronto in + lettura.} + + + +\subsection{Un esempio di I/O multiplexing} +\label{sec:TCP_multiplex_example} + +Abbiamo incontrato la problematica tipica che conduce all'uso dell'I/O +multiplexing nella nostra analisi degli errori in +\secref{sec:TCP_conn_early_abort}, quando il nostro client non era in grado di +rendersi conto di errori sulla connessione essendo impegnato nella attesa di +dati in ingresso dallo standard input. + +In questo caso il problema è quello di dover tenere sotto controllo due +diversi file descriptor, lo standard input, da cui viene letto il testo che +vogliamo inviare al server, e il socket connesso con il server su cui detto +testo sarà scritto e dal quale poi si vorrà ricevere la risposta. L'uso +dell'I/O multiplexing consente di tenere sotto controllo entrambi, senza +restare bloccati. + +Nel nostro caso quello che ci interessa è non essere bloccati in lettura sullo +standard input in caso di errori sulla connessione o chiusura della stessa da +parte del server. Entrambi questi casi possono essere rilevati usando +\func{select}, per quanto detto in \secref{sec:TCP_sock_select}, mettendo +sotto osservazione i file descriptor per la condizione di essere pronti in +lettura: sia infatti che si ricevano dati, che la connessione sia chiusa +regolarmente (con la ricezione di un segmento FIN) che si riceva una +condizione di errore (con un segmento RST) il socket connesso sarà pronto in +lettura (nell'ultimo caso anche in scrittura, ma questo non è necessario ai +nostri scopi). + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \includecodesample{listati/ClientEcho_third.c} + \end{minipage} + \normalsize + \caption{La sezione nel codice della terza versione della funzione + \func{ClientEcho} usata dal client per il servizio \textit{echo} + modificata per l'uso di \func{select}.} + \label{fig:TCP_ClientEcho_third} +\end{figure} + +Riprendiamo allora il codice del client, modificandolo per l'uso di +\func{select}. Quello che dobbiamo modificare è la funzione \func{ClientEcho} +di \figref{fig:TCP_ClientEcho_second}, dato che tutto il resto, che riguarda +le modalità in cui viene stabilita la connessione con il server, resta +assolutamente identico. La nostra nuova versione di \func{ClientEcho}, la +terza della serie, è riportata in \figref{fig:TCP_ClientEcho_third}. + +In questo caso la funzione comincia (\texttt{\small 8--9}) con la +cancellazione del file descriptor set \var{fset} e del valore \var{maxfd} da +passare a \func{select} come massimo per il numero dei file descriptor. Per +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 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. + +Si noti come la chiamata a \func{select} venga eseguita usando come primo +argomento il valore di \var{maxfd}, precedentemente calcolato, passando poi il +solo file descriptor set per il controllo dell'attività in lettura, gli altri +argomenti sono tutti passati come 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 file +descriptor presneta attività, si comincia (\texttt{\small 14--24}) con il file +descriptor associato allo standard input. In caso di attività (quando cioè +\macro{FD_ISSET} ritorna una valore diverso da zero) si esegue (\texttt{\small + 15}) una \func{fgets} per leggere gli eventuali dati presenti; se non ve ne +sono (e la funzione restituisce pertanto un puntatore nullo) si ritorna +immediatamente (\texttt{\small 16}) dato che questo significa che si è chiuso +lo standard input; altrimenti (\texttt{\small 18--22}) si scrivono i dati sul +socket, uscendo immediatamente in caso di errore di scrittura. + +Controllato lo standard input si passa a controllare (\texttt{\small 25--40}) +il socket connesso, in caso di attività si esegue (\texttt{\small 26}) subito +una \func{read} di cui si controlla il valore di ritorno; se questo è negativo +(\texttt{\small 27--30}) si è avuto un errore e pertanto si esce +immediatamente segnalandolo, se è nullo (\texttt{\small 31--34}) \section{Le opzioni dei socket} @@ -141,13 +227,13 @@ quando c' Dato che la maggior parte delle opzioni dei socket sono relative ai socket TCP, ed hanno poi significato analogo quando usate con altri socket, abbiamo -preferito trattare l'argomento in generale in questa sezione piuttosto che -nel capitolo dedicato alla trattazione generica dei socket. +preferito trattare l'argomento in generale in questa sezione piuttosto che nel +capitolo dedicato alla trattazione generica dei socket. \section{I dati \textit{out-of-band}} -\label{sec:TCP_outofband} +\label{sec:TCP_urgent_data} Una caratteristica speciale dei socket TCP è quella della presenza dei cosiddetti dati \textit{out-of-band}