Completato server elementare.
authorSimone Piccardi <piccardi@gnulinux.it>
Wed, 4 Apr 2001 23:09:57 +0000 (23:09 +0000)
committerSimone Piccardi <piccardi@gnulinux.it>
Wed, 4 Apr 2001 23:09:57 +0000 (23:09 +0000)
files.tex
main.tex
network.tex
sources/SimpleDaytimeTCPServer.c [new file with mode: 0644]

index 64482d02e46f3a3a3198ab484d5436bfe058b85a..a9f253b42f83c52eb13068c24339401fcfa4cf13 100644 (file)
--- 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 .
index da2fd9759030cb7b36008c5abd59453223a9f300..569f7086acebb9424631e2086f6f237199fdbfd5 100644 (file)
--- 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
 
index 3a72f11f1f9649d6c536204bc30c94d8028afcb4..80d37ebad9a5bd76f3290f8f92c1969a16bd6618 100644 (file)
@@ -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 <sys/types.h>   /* 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 <sys/types.h>   /* predefined types */
+#include <unistd.h>      /* include unix standard library */
+#include <arpa/inet.h>   /* IP addresses conversion utiliites */
+#include <sys/socket.h>  /* socket library */
+#include <stdio.h>       /* include standard I/O library */
+#include <time.h>
+#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 (file)
index 0000000..fdf99ec
--- /dev/null
@@ -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 <sys/types.h>   /* predefined types */
+#include <unistd.h>      /* include unix standard library */
+#include <arpa/inet.h>   /* IP addresses conversion utiliites */
+#include <sys/socket.h>  /* socket library */
+#include <stdio.h>      /* include standard I/O library */
+#include <time.h>
+
+#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);
+}