[Avanti]  [Indietro]  [Su]  

15.3.2 Il client daytime

Il primo esempio di applicazione delle funzioni di base illustrate in sez. 15.2 è relativo alla creazione di un client elementare per il servizio daytime, un servizio elementare, definito nell'RFC 867, che restituisce l'ora locale della macchina a cui si effettua la richiesta, e che è assegnato alla porta 13.

In fig. 15.8 è riportata la sezione principale del codice del nostro client. Il sorgente completo del programma (TCP_daytime.c, che comprende il trattamento delle opzioni ed una funzione per stampare un messaggio di aiuto) è allegato alla guida nella sezione dei codici sorgente e può essere compilato su una qualunque macchina GNU/Linux.


01: #include <sys/types.h>   /* predefined types */ 
02: #include <unistd.h>      /* include unix standard library */ 
03: #include <arpa/inet.h>   /* IP addresses conversion utilities */ 
04: #include <sys/socket.h>  /* socket library */ 
05: #include <stdio.h>       /* include standard I/O library */ 
06:  
07: int main(int argc, char *argv[]) 
08: { 
09:     int sock_fd; 
10:     int i, nread; 
11:     struct sockaddr_in serv_add; 
12:     char buffer[MAXLINE]; 
13:      ... 
14:     /* create socket */ 
15:     if ( (sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 
16:         perror("Socket creation error"); 
17:         return -1; 
18:     } 
19:     /* initialize address */ 
20:     memset((void *) &serv_add, 0, sizeof(serv_add)); /* clear server address */ 
21:     serv_add.sin_family = AF_INET;                   /* address type is INET */ 
22:     serv_add.sin_port = htons(13);                   /* daytime post is 13 */ 
23:     /* build address using inet_pton */ 
24:     if ( (inet_pton(AF_INET, argv[optind], &serv_add.sin_addr)) <= 0) { 
25:         perror("Address creation error"); 
26:         return -1; 
27:     } 
28:     /* extablish connection */ 
29:     if (connect(sock_fd, (struct sockaddr *)&serv_add, sizeof(serv_add)) < 0) { 
30:         perror("Connection error"); 
31:         return -1; 
32:     } 
33:     /* read daytime from server */ 
34:     while ( (nread = read(sock_fd, buffer, MAXLINE)) > 0) { 
35:         buffer[nread]=0; 
36:         if (fputs(buffer, stdout) == EOF) {          /* write daytime */ 
37:             perror("fputs error"); 
38:             return -1; 
39:         } 
40:     } 
41:     /* error on read */ 
42:     if (nread < 0) { 
43:         perror("Read error"); 
44:         return -1; 
45:     } 
46:     /* normal exit */ 
47:     return 0; 
48: } 
Figura 15.8: Esempio di codice di un client elementare per il servizio daytime.

Il programma anzitutto (1–5) include gli header necessari; dopo la dichiarazione delle variabili (9–12) si è omessa tutta la parte relativa al trattamento degli argomenti passati dalla linea di comando (effettuata con le apposite funzioni illustrate in sez. 2.3.2).

Il primo passo (14–18) è creare un socket TCP (quindi di tipo SOCK_STREAM e di famiglia AF_INET). La funzione socket ritorna il descrittore che viene usato per identificare il socket in tutte le chiamate successive. Nel caso la chiamata fallisca si stampa un errore (16) con la funzione perror e si esce (17) con un codice di errore.

Il passo seguente (19–27) è quello di costruire un'apposita struttura sockaddr_in in cui sarà inserito l'indirizzo del server ed il numero della porta del servizio. Il primo passo (20) è inizializzare tutto a zero, per poi inserire il tipo di indirizzo (21) e la porta (22), usando per quest'ultima la funzione htons per convertire il formato dell'intero usato dal computer a quello usato nella rete, infine 23–27 si può utilizzare la funzione inet_pton per convertire l'indirizzo numerico passato dalla linea di comando.

A questo punto (28–32) usando la funzione connect sul socket creato in precedenza (29) si può stabilire la connessione con il server. Per questo si deve utilizzare come secondo argomento la struttura preparata in precedenza con il relativo indirizzo; si noti come, esistendo diversi tipi di socket, si sia dovuto effettuare un cast. Un valore di ritorno della funzione negativo implica il fallimento della connessione, nel qual caso si stampa un errore (30) e si ritorna (31).

Completata con successo la connessione il passo successivo (34–40) è leggere la data dal socket; il protocollo prevede che il server invii sempre una stringa alfanumerica, il formato della stringa non è specificato dallo standard, per cui noi useremo il formato usato dalla funzione ctime, seguito dai caratteri di terminazione \r\n, cioè qualcosa del tipo:

Wed Apr 4 00:53:00 2001\r\n
questa viene letta dal socket (34) con la funzione read in un buffer temporaneo; la stringa poi deve essere terminata (35) con il solito carattere nullo per poter essere stampata (36) sullo standard output con l'uso di fputs.

Come si è già spiegato in sez. 15.3.1 la risposta dal socket potrà arrivare 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 ciclo in cui si continui a leggere fintanto che la funzione 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 (anche questo è quanto richiesto dal protocollo); 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 \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.

Se abilitiamo il servizio daytime17 possiamo verificare il funzionamento del nostro client, avremo allora:

                                                                                       
                                                                                       
[piccardi@gont sources]$ ./daytime 127.0.0.1
Mon Apr 21 20:46:11 2003
e come si vede tutto funziona regolarmente.


[Avanti]  [Indietro]  [Su]  
© 2000-2003 Simone Piccardi
Pubblicazione web curata da Mirko Maischberger