From 60ea832813214f6bc4a9761a85d9d2b2038991aa Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Wed, 4 Apr 2001 23:09:57 +0000 Subject: [PATCH] Completato server elementare. --- files.tex | 2 +- main.tex | 3 + network.tex | 231 ++++++++++++++++++++++++------- sources/SimpleDaytimeTCPServer.c | 114 +++++++++++++++ 4 files changed, 300 insertions(+), 50 deletions(-) create mode 100644 sources/SimpleDaytimeTCPServer.c diff --git a/files.tex b/files.tex index 64482d0..a9f253b 100644 --- a/files.tex +++ b/files.tex @@ -617,7 +617,7 @@ nell'header file \texttt{unistd.h}. \item \texttt{EEXIST} Un file (o una directory) con quel nome esiste di già. \item \texttt{EMLINK} Ci sono troppi link al file \texttt{oldname} (il - numero massimo è specificato dalla variabile \texttt{LINK_MAX}, vedi + numero massimo è specificato dalla variabile \texttt{LINK\_MAX}, vedi \ref{sec:sys_limits}. \item \texttt{ENOSPC} La directory in cui si vuole creare il link è piena e non può essere . diff --git a/main.tex b/main.tex index da2fd97..569f708 100644 --- a/main.tex +++ b/main.tex @@ -28,6 +28,8 @@ %\includeonly{macro,pref,intro,fdl} + + \title{Guida alla Programmazione in Linux.} \author{Simone Piccardi} @@ -56,6 +58,7 @@ \end{quote} \clearemptydoublepage + \tableofcontents \clearemptydoublepage diff --git a/network.tex b/network.tex index 3a72f11..80d37eb 100644 --- a/network.tex +++ b/network.tex @@ -1,18 +1,20 @@ -\chapter{Introduzione alla rete} +\chapter{Introduzione alla programmazione di rete} \label{cha:network} In questo capitolo sarà fatta un'introduzione ai contetti generali che servono -come prerequisiti per capire la programmazione di rete ed esamineremo a grandi -linee i protocolli di rete e come questi sono organizzati e interagiscono. +come prerequisiti per capire la programmazione di rete, partiremo con due +semplici esempi per poi passare ad un esame a grandi linee dei protocolli di +rete e di come questi sono organizzati e interagiscono. In particolare, avendo assunto l'ottica di un'introduzione mirata alla -programmazione, ci concentreremo sul protocollo più diffuso che è quello che -sta alla base di internet, ed in particolare sulle parti più importanti ai -fini della programmazione. +programmazione, ci concentreremo sul protocollo più diffuso, TCP/IP, che è +quello che sta alla base di internet, ed in particolare prenderemo in esame in +questa introduzione i concetti più importanti da conoscere ai fini della +programmazione. \section{Il modello client-server} -\label{sec:net_cliserv}. +\label{sec:net_cliserv} La differenza principale fra un'applicazione di rete e un programma normale è che quest'ultima per definizione concerne la comunicazione fra ``processi'' @@ -33,8 +35,8 @@ servizio che viene fornito tramite la rete, ma il modello generale anche per programmi che non fanno necessariamente uso della rete, come il sistema a finestre. -Normalmente si dividono i server in due categorie principali, -\textit{concorrenti} e \textit{iterativi}, sulla base del loro comportamento. +Normalmente si dividono i server in due categorie principali, e vengono detti +\textit{concorrenti} o \textit{iterativi}, sulla base del loro comportamento. Un server iterativo risponde alla richiesta inviando i dati e resta occupato (non rispondendo ad ulteriori richieste) fintanto che non ha concluso la @@ -44,20 +46,20 @@ disponibile. Un server concorrente al momento di trattare la richiesta crea un processo figlio incaricato di fornire i servizi richiesti, per poi porsi in attesa di ulteriori richieste. In questo modo più richieste possono essere soddisfatte -contemporaneamente, una volta che il processo figlio ha concluso il suo lavoro +contemporaneamente; una volta che il processo figlio ha concluso il suo lavoro viene terminato, mentre il server originale resta sempre attivo. \subsection{Un primo esempio di client} \label{sec:net_cli_sample} -Per evitare di rendere l'esposizione dei concetti generali puramente teorica -iniziamo con il mostrare un semplice esempio di client TCP. In \nfig è -riportata la sezione principale del codice del nostro client elementare per il -servizio \textit{daytime}, un servizio standard che restituisce l'ora locale -della macchina a cui si effettua la richesta. +Per evitare di rendere l'esposizione dei concetti generali sulla rete +puramente teorica iniziamo con il mostrare un semplice esempio di client TCP. +In \nfig\ è riportata la sezione principale del codice del nostro client +elementare per il servizio \textit{daytime}, un servizio standard che +restituisce l'ora locale della macchina a cui si effettua la richesta. -\begin{figure}[htbp] +\begin{figure}[!htbp] \footnotesize \begin{lstlisting}{} #include /* predefined types */ @@ -109,62 +111,193 @@ int main(int argc, char *argv[]) return 0; } \end{lstlisting} - \caption{Esempio di codice di un semplice client per il servizio daytime.} + \caption{Esempio di codice di un client elementare per il servizio daytime.} \label{fig:net_cli_code} \end{figure} - Scopo di questo esempio è fornire un primo approccio alla programmazione di rete, per questo motivo non ci dilungheremo nel trattare il significato dei termini o il funzionamento delle varie funzioni utilizzate. Tutto questo sarà esaminato in dettaglio nel seguito, per cui qui ci limiteremo a citarli senza ulteriori spiegazioni. -Il listato completo del programma (che comprende il trattamento delle opzioni -e una funzione per stampare un messaggio di aiuto) è allegato alla guida nella -sezione dei codici sorgente e può essere compilato su una qualunque macchina -linux. +Il sorgente completo del programma (\texttt{SimpleDaytimeTCPClient.c}, che +comprende il trattamento delle opzioni e una funzione per stampare un +messaggio di aiuto) è allegato alla guida nella sezione dei codici sorgente e +può essere compilato su una qualunque macchina linux. Il programma anzitutto include gli header necessari (\texttt{\small 1--5}); -dopo la dichiarazione delle variabili (\texttt{\small 9--12}), si è omessa +dopo la dichiarazione delle variabili (\texttt{\small 9--12}) si è omessa tutta la parte relativa al trattamento degli argomenti passati dalla linea di -comando effettuata con le apposite routines illustrate in -\ref{cha:parameter_options}. +comando (effettuata con le apposite routines illustrate in +\ref{cha:parameter_options}). -Il primo passo (\texttt{\small 14--18}è creare un \textit{socket} internet -(\texttt{AF\_INET}), di tipo TCP \texttt{SOCK\_STREAM}), la funzione ritorna -un descrittore, analogo a quello dei file, che viene usato per identificare il -socket in tutte le chiamate successive. Nel caso la chiamata fallisca si -stampa un errore con la relativa routine e si esce. +Il primo passo (\texttt{\small 14--18}) è creare un \textit{socket} IPv4 +(\texttt{AF\_INET}), di tipo TCP \texttt{SOCK\_STREAM} (in sostanza un canale +di comunicazione attraverso internet, questi termini verranno spiegati con +precisione più avanti). La funzione \texttt{socket} ritorna un descrittore, +analogo a quello dei file, che viene usato per identificare il socket in tutte +le chiamate successive. Nel caso la chiamata fallisca si stampa un errore con +la relativa routine e si esce. Il passo seguente (\texttt{\small 19--27}) è quello di costruire una apposita struttura \texttt{sockaddr\_in} in cui sarà inserito l'indirizzo del server ed il numero della porta del servizio. Il primo passo è inizializzare tutto a -zero, poi si setta il tipo di protocollo e la porta (usando la funzione -\texttt{htons} per convertire il formato dell'intero a quello usato nella -rete), infine si utilizza la funzione \texttt{inet\_pton} per convertire -l'indirizzo numerico passato dalla linea di comando. - -Usando la funzione \texttt{connect} (\texttt{\small 28--32}) si provvede poi a -stabilire la connessione con il server. Un valore negativo +zero, per poi inserire il tipo di protocollo e la porta (usando per +quest'ultima la funzione \texttt{htons} per convertire il formato dell'intero +usato dal computer a quello usato nella rete), infine si utilizza la funzione +\texttt{inet\_pton} per convertire l'indirizzo numerico passato dalla linea di +comando. + +Usando la funzione \texttt{connect} sul socket creato in precedenza +(\texttt{\small 28--32}) si provvede poi a stabilire la connessione con il +server specificato dall'indirizzo immesso nella struttura possata come secondo +argomento, il terzo argomento è la dimensione di detta struttura. Dato che +esistono diversi tipi di socket, si è dovuto effettuare un cast della +struttura inizializzata in precedenza, che è specifica per i socket IPv4. Un +valore di ritorno negativo implica il fallimento della connessione. + +Completata con successo la connessione il passo successivo (\texttt{\small + 34--40}) è leggere la data dal socket; il server invierà sempre una stringa +di 26 caratteri della forma \verb|Wed Apr 4 00:53:00 2001\r\n|, che viene +letta dalla funzione \texttt{read} e scritta su \texttt{stdout}. + +Dato il funzionamento di TCP la risposta potrà tornare in un unico pacchetto +di 26 byte (come avverrà senz'altro nel caso in questione) ma potrebbe anche +arrivare in 26 pacchetti di un byte. Per questo nel caso generale non si può +mai assumere che tutti i dati arrivino con una singola lettura, pertanto +quest'ultima deve essere effettuata in un loop in cui si continui a leggere +fintanto che la funzione \texttt{read} non ritorni uno zero (che significa che +l'altro capo ha chiuso la connessione) o un numero minore di zero (che +significa un errore nella connessione). + +Si noti come in questo caso la fine dei dati sia specificata dal server che +chiude la connessione; questa è una delle tecniche possibili (è quella usata +pure dal protocollo HTTP), ma ce ne possono essere altre, ad esempio FTP marca +la conclusione di un blocco di dati con la sequenza ASCII \verb|\r\n| +(carriage return e line feed), mentre il DNS mette la lunghezza in testa ad +ogni blocco che trasmette. Il punto essenziale è che TCP non provvede nessuna +indicazione che permetta di marcare dei blocchi di dati, per cui se questo è +necessario deve provvedere il programma stesso. \subsection{Un primo esempio di server} \label{sec:net_serv_sample} -Dopo aver visto il client facciamo vedere adesso anche il corrispettivo -server, in questo modo sarà possibile fare delle prove +Dopo aver illustrato il client daremo anche un esempio di un server +elementare, in grado di rispondere al precedente client. Il listato è +nuovamente mostrato in \nfig, il sorgente completo +(\texttt{SimpleDaytimeTCPServer.c}) è allegato insieme agli altri file nella +directory \texttt{sources}. -\begin{figure}[htbp] - \begin{center} - \begin{verbatim} +\begin{figure}[!htbp] + \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 +#define MAXLINE 80 +#define BACKLOG 10 +int main(int argc, char *argv[]) +{ +/* + * Variables definition + */ + int list_fd, conn_fd; + int i; + struct sockaddr_in serv_add; + char buffer[MAXLINE]; + time_t timeval; + ... - \end{verbatim} - \caption{Esempio di codice di un semplice server per il servizio daytime.} - \label{fig:net_serv_code} - \end{center} + /* 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 *) NULL, NULL)) <0 ) { + perror("accept error"); + exit(-1); + } + 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); + } + close(conn_fd); + } + /* normal exit */ + exit(0); +} + \end{lstlisting} + \caption{Esempio di codice di un semplice server per il servizio daytime.} + \label{fig:net_serv_code} \end{figure} +Come per il client si includono gli header necessari a cui è aggiunto quello +per trattare i tempi, e si definiscono alcune costanti e le variabili +necessarie in seguito (\texttt{\small 1--18}), come nel caso precedente si +sono omesse le parti relative al trattamento delle opzioni da riga di comando. + +La creazione del socket (\texttt{\small 22--26}) è analoga al caso precedente, +come pure l'inizializzazione della struttura \texttt{sockaddr\_in}, anche in +questo caso si usa la porta standard del servizio daytime, ma come indirizzo +IP si il valore predefinito \texttt{INET\_ANY} che corrisponde ad un indirizzo +generico (\texttt{\small 27--31}). + +Si effettua poi (\texttt{\small 32--36}) la chiamata alla funzione +\texttt{bind} che permette di associare la precedente struttura al socket, in +modo che quest'ultimo possa essere usato per accettare connessioni su una +qualunque delle interfacce di rete locali. + +Il passo successivo (\texttt{\small 37--41}) è mettere ``in ascolto'' il +socket, questo viene effettuato con la funzione \texttt{listen} che dice al +kernel di accettare connessioni per il socket specificato, la funzione indica +inoltre, con il secondo parametro, il numero massimo di connessioni che il +kernel accetterà di mettere in coda per il suddetto socket. + +Questa ultima chiamata completa la preparazione del socket per l'ascolto (che +viene chiamato anche \textit{listening descriptor}) a questo punto il processo +è mandato in sleep (\texttt{\small 44--47}) con la successiva chiamata alla +funzione \texttt{accept}, fin quando non arriva e viene accettata una +connessione da un client. + +Quando questo avviene \texttt{accept} ritorna un secondo descrittore di +socket, che viene chiamato \textit{connected descriptor} che è quello che +viene usato dalla successiva chiamata alla \texttt{write} per scrivere la +risposta al client, una volta che si è opportunamente (\texttt{\small 48--49}) +costruita la stringa con la data da trasmettere. Completata la trasmissione il +nuovo socket viene chiuso (\texttt{\small 54}). +Il tutto è inserito in un loop infinito (\texttt{\small 42--55}) in modo da +poter ripetere l'invio della data ad una successiva connessione. + +È impostante notare che questo server è estremamente elementare, infatti a +parte il fatto di essere dipendente da IPv4, esso è in grado di servire solo +un client alla volta, è cioè un \textsl{server iterativo}, inoltre esso è +scritto per essere lanciato da linea di comando, se lo si volesse utilizzare +come demone di sistema (che è in esecuzione anche quando non c'è nessuna shell +attiva), occorrerebbero delle opportune modifiche. \section{I protocolli di rete} \label{sec:net_protocols} @@ -174,7 +307,7 @@ eterogeneo di mezzi di comunicazione che vanno dal cavo telefonico, alla fibra ottica, alle comunicazioni via satellite; per rendere possibile la comunicazione attraverso un così variegato insieme di mezzi sono stati adottati una serie di protocolli, il più famoso dei quali, quello alla base -del funzionamento di internet, è il cosiddetto TCP/IP. +del funzionamento di internet, è il protocollo TCP/IP. \subsection{Il modello ISO/OSI} \label{sec:net_iso_osi} @@ -219,7 +352,7 @@ della Difesa Americano. \label{sec:net_tcpip_overview} Così come ISO/OSI anche TCP/IP è stato strutturato in livelli (riassunti in -\ntab); un confronto fra i due è riportato in \nfig dove viene evidenziata +\ntab); un confronto fra i due è riportato in \nfig\ dove viene evidenziata anche la corrispondenza fra i rispettivi livelli (che comunque è approssimativa) e su come essi vanno ad inserirsi all'interno del sistema operativo rispetto alla divisione fra user space e kernel space spiegata in @@ -354,7 +487,7 @@ spiegazioni. \subsection{Il quadro generale} Benché si parli di TCP/IP questa famiglia di protocolli è composta anche da -altri membri. In \nfig si è riportato una figura di quadro che mostra un +altri membri. In \nfig\ si è riportato una figura di quadro che mostra un panorama sull'intera famiglia, e di come i vari protocolli vengano usati dalle applicazioni. diff --git a/sources/SimpleDaytimeTCPServer.c b/sources/SimpleDaytimeTCPServer.c new file mode 100644 index 0000000..fdf99ec --- /dev/null +++ b/sources/SimpleDaytimeTCPServer.c @@ -0,0 +1,114 @@ +/**************************************************************** + * + * Program daytime_tcp_server.c: + * Simple 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 $ + * + ****************************************************************/ +/* + * 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; + char buffer[MAXLINE]; + time_t timeval; + /* + * Input section: decode parameters passed in the calling + * Use getopt function + */ + opterr = 0; /* don't want writing to stderr */ + while ( (i = getopt(argc, argv, "h")) != -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(); + default: /* should not reached */ + usage(); + } + } + /* *********************************************************** + * + * 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 *) NULL, NULL)) <0 ) { + perror("accept error"); + exit(-1); + } + 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); + } + close(conn_fd); + } + + /* normal exit */ + exit(0); +} +/* + * routine to print usage info and exit + */ +void usage(void) { + printf("Simple daytime server\n"); + printf("Usage:\n"); + printf(" daytimed [-h] \n"); + printf(" -h print this help\n"); + exit(1); +} -- 2.30.2