From 45c2192b1414b9dfc1cef8bb718761c495ca33f4 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sat, 19 May 2001 20:47:03 +0000 Subject: [PATCH] Aggiunta versione concorrente del server daytime. Iniziata la relativa spiegazione. --- elemtcp.tex | 101 ++++++++++++++++++++-- main.tex | 4 +- sources/ElemDaytimeTCPCuncServ.c | 141 +++++++++++++++++++++++++++++++ sources/SimpleDaytimeTCPServer.c | 4 +- 4 files changed, 238 insertions(+), 12 deletions(-) create mode 100644 sources/ElemDaytimeTCPCuncServ.c diff --git a/elemtcp.tex b/elemtcp.tex index fcb3b32..fd2d34a 100644 --- a/elemtcp.tex +++ b/elemtcp.tex @@ -876,7 +876,7 @@ Storicamente il valore del parametro \texttt{backlog} era corrispondente al massimo valore della somma del numero di entrate possibili per ciascuna di dette code. Stevens riporta che BSD ha sempre applicato un fattore di 1.5 al valore, e provvede una tabella con i risultati ottenuti con vari kernel, -compreso linux 2.0, che mostrano le differenze fra diverse implementazioni. +compreso linux 2.0, che mostrano le differenze fra diverse implementazioni. Ma in linux il significato di questo valore è cambiato a partire dal kernel 2.2 per prevenire l'attacco chiamato \texttt{syn flood}. Questo si basa @@ -893,7 +893,9 @@ la \texttt{sysctl} o scrivendola direttamente in \texttt{/proc/sys/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 \texttt{/proc/sys/net/ipv4/tcp\_syncookies}) questo valore -viene ignorato e non esiste più un valore massimo. +viene ignorato e non esiste più un valore massimo. In ogni caso in linux il +valore di \texttt{backlog} viene troncato ad un massimo di \texttt{SOMAXCONN} +se è superiore a detta constante (che di default vale 128). La scelta storica per il valore di questo parametro è di 5, e alcuni vecchi kernel non supportavano neanche valori superiori, ma la situazione corrente è @@ -904,11 +906,11 @@ conviene specificare questo valore con una costante (il cui cambiamento richiederebbe la ricompilazione del server) ma usare piuttosto una variabile di ambiente (vedi \secref{sec:xxx_env_var}). Lo Stevens tratta accuratamente questo argomento, con esempi presi da casi reali su web server, ed in -particolare evidenzia come non sia più vero che la ragione della coda è quella -di gestire il caso in cui il server è occupato fra chiamate successive alla -\texttt{accept} (per cui la coda più occupata sarebbe quella delle connessioni -compeltate), ma è invece necessaria a gestire la presenza di un gran numero di -SYN in attesa di completare il three-way handshake. +particolare evidenzia come non sia più vero che il compito principale della +coda sia quello di gestire il caso in cui il server è occupato fra chiamate +successive alla \texttt{accept} (per cui la coda più occupata sarebbe quella +delle connessioni compeltate), ma piuttosto quello di gestire la presenza di +un gran numero di SYN in attesa di completare il three-way handshake. Come accennato nel caso del TCP se un SYN arriva con tutte le code piene, il pacchetto sarà ignorato. Questo viene fatto perché la condizione delle code @@ -981,7 +983,7 @@ E da chiarire che linux presenta un comportamento diverso nella gestione degli errori rispetto ad altre implementazioni dei socket BSD, infatti la funzione \texttt{accept} passa gli errori di rete pendenti sul nuovo socket come codici di errore per \texttt{accept}. Inoltre la funzione non fa ereditare ai nuovi -socket flag come \texttt{O_NONBLOCK}, che devono essere rispecificati volta +socket flag come \texttt{O\_NONBLOCK}, che devono essere rispecificati volta volta, questo è un comportamento diverso rispetto a quanto accade con BSD e deve essere tenuto in conto per scrivere programmi portabili. @@ -1021,4 +1023,87 @@ creato da \texttt{accept} viene chiuso dopo l'invio dei dati. \section{Un server concorrente su TCP} \label{sec:TCPel_cunc_serv} +Il server \texttt{daytime} dell'esempio in \secref{sec:net_cli_sample} è un +tipico esempio di server iterativo, in cui viene servita una richiesta alla +volta; in generale però, specie se il servizio è più complesso e comporta uno +scambio di dati più sostanzioso di 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. + +Il modo più immediato per creare un server concorrente è allora quello di +usare la funzione \texttt{fork} per far creare al server per ogni richiesta da +parte di un client un processo figlio che si incarichi della gestione della +comunicazione. + +Per illustrare questo meccanismo abbiamo allora riscritto il server +\texttt{daytime} in forma concorrente, inserendo anche una opzione per la +stampa degli indirizzi delle connessioni ricevute. + +In \nfig\ è mostrato un estratto del codice, in cui si sono tralasciate il +trattamento delle opzioni e le parti rimaste invariate rispetto al precedente +esempio. Al solito il sorgente completo del server +\texttt{ElemDaytimeTCPCuncServ.c} è allegato nella directory dei sorgenti. + +\begin{figure}[!htb] + \footnotesize + \begin{lstlisting}{} +#include /* predefined types */ +#include /* include unix standard library */ +#include /* IP addresses conversion utiliites */ +#include /* socket library */ +#include /* include standard I/O library */ +#include + +int main(int argc, char *argv[]) +{ + int list_fd, conn_fd; + int i; + struct sockaddr_in serv_add, client; + char buffer[MAXLINE]; + socklen_t len; + time_t timeval; + pid_t pid; + int logging=0; + ... + /* write daytime to client */ + while (1) { + if ( (conn_fd = accept(list_fd, (struct sockaddr *)&client, &len)) + <0 ) { + perror("accept error"); + exit(-1); + } + /* fork to handle connection */ + if ( (pid = fork()) < 0 ){ + perror("fork error"); + exit(-1); + } + if (pid == 0) { /* child */ + close(list_fd); + timeval = time(NULL); + snprintf(buffer, sizeof(buffer), "%.24s\r\n", ctime(&timeval)); + if ( (write(conn_fd, buffer, strlen(buffer))) < 0 ) { + perror("write error"); + exit(-1); + } + if (logging) { + inet_ntop(AF_INET, &client.sin_addr, buffer, sizeof(buffer)); + printf("Request from host %s, port %d\n", buffer, + ntohs(client.sin_port)); + } + close(conn_fd); + exit(0); + } else { /* parent */ + close(conn_fd); + } + } + /* normal exit, never reached */ + exit(0); +} + \end{lstlisting} + \caption{Esempio di codice di un server concorrente elementare per il + servizio daytime.} + \label{fig:net_cli_code} +\end{figure} + +Come si può vedere (\texttt{\small 21--25}) alla funzione \texttt{accept} diff --git a/main.tex b/main.tex index 097db57..55daeec 100644 --- a/main.tex +++ b/main.tex @@ -1,4 +1,4 @@ -%% +%% %% GaPiL : Guida alla Programmazione in Linux %% %% S. Piccardi Feb. 2001 @@ -21,7 +21,7 @@ \usepackage{color} % % Setting page layout -% +% \oddsidemargin=0.5cm \evensidemargin=-0.5cm \textwidth=16cm diff --git a/sources/ElemDaytimeTCPCuncServ.c b/sources/ElemDaytimeTCPCuncServ.c new file mode 100644 index 0000000..e6e9c49 --- /dev/null +++ b/sources/ElemDaytimeTCPCuncServ.c @@ -0,0 +1,141 @@ +/**************************************************************** + * + * Program ElemDaytimeTCPCuncServ.c + * Elementary TCP cuncurrent server for daytime service (port 13) + * + * Author: Simone Piccardi + * May. 2001 + * + * Usage: daytimed + * + * $Id: ElemDaytimeTCPCuncServ.c,v 1.1 2001/05/19 20:47:03 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 + +#define MAXLINE 80 +#define BACKLOG 10 +/* Program begin */ +void usage(void); +int main(int argc, char *argv[]) +{ +/* + * Variables definition + */ + int list_fd, conn_fd; + int i; + struct sockaddr_in serv_add, client; + char buffer[MAXLINE]; + socklen_t len; + time_t timeval; + pid_t pid; + int logging=0; + /* + * Input section: decode parameters passed in the calling + * Use getopt function + */ + opterr = 0; /* don't want writing to stderr */ + while ( (i = getopt(argc, argv, "hv")) != -1) { + switch (i) { + /* + * Handling options + */ + case 'h': + printf("Wrong -h option use\n"); + usage(); + return(0); + break; + case '?': /* unrecognized options */ + printf("Unrecognized options -%c\n",optopt); + usage(); + return(0); + break; + case 'v': + logging = 1; + break; + default: /* should not reached */ + usage(); + return(0); + } + } + /* *********************************************************** + * + * Options processing completed + * + * Main code beginning + * + * ***********************************************************/ + /* create socket */ + if ( (list_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("Socket creation error"); + exit(-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(13); /* daytime port is 13 */ + serv_add.sin_addr.s_addr = htonl(INADDR_ANY); /* connect from anywhere */ + /* bind socket */ + if (bind(list_fd, (struct sockaddr *)&serv_add, sizeof(serv_add)) < 0) { + perror("bind error"); + exit(-1); + } + /* listen on socket */ + if (listen(list_fd, BACKLOG) < 0 ) { + perror("listen error"); + exit(-1); + } + /* write daytime to client */ + while (1) { + if ( (conn_fd = accept(list_fd, (struct sockaddr *)&client, &len)) + <0 ) { + perror("accept error"); + exit(-1); + } + /* fork to handle connection */ + if ( (pid = fork()) < 0 ){ + perror("fork error"); + exit(-1); + } + if (pid == 0) { /* child */ + close(list_fd); + timeval = time(NULL); + snprintf(buffer, sizeof(buffer), "%.24s\r\n", ctime(&timeval)); + if ( (write(conn_fd, buffer, strlen(buffer))) < 0 ) { + perror("write error"); + exit(-1); + } + if (logging) { + inet_ntop(AF_INET, &client.sin_addr, buffer, sizeof(buffer)); + printf("Request from host %s, port %d\n", buffer, + ntohs(client.sin_port)); + } + close(conn_fd); + exit(0); + } else { /* parent */ + close(conn_fd); + } + } + /* normal exit, never reached */ + exit(0); +} +/* + * routine to print usage info and exit + */ +void usage(void) { + printf("Simple daytime server\n"); + printf("Usage:\n"); + printf(" daytimed [-hv] \n"); + printf(" -h print this help\n"); + printf(" -v print request source on stdout\n"); + exit(1); +} + diff --git a/sources/SimpleDaytimeTCPServer.c b/sources/SimpleDaytimeTCPServer.c index fdf99ec..9f6b9be 100644 --- a/sources/SimpleDaytimeTCPServer.c +++ b/sources/SimpleDaytimeTCPServer.c @@ -1,14 +1,14 @@ /**************************************************************** * * Program daytime_tcp_server.c: - * Simple TCP server for daytime service (port 13) + * Elementary TCP server for daytime service (port 13) * * Author: Simone Piccardi * Apr. 2001 * * Usage: daytimed * - * $Id: SimpleDaytimeTCPServer.c,v 1.1 2001/04/04 23:09:57 piccardi Exp $ + * $Id: SimpleDaytimeTCPServer.c,v 1.2 2001/05/19 20:47:03 piccardi Exp $ * ****************************************************************/ /* -- 2.30.2