funzioni, che sono comunque utilizzabili in generale per la trasmissione di
dati attraverso qualunque tipo di socket. Esse hanno la caratteristica di
prevedere tre argomenti aggiuntivi attraveso i quali è possibile specificare
-la destinazione o l'origine dei dati trasmessi. La prima di queste funzioni è
-\funcd{sendto} ed il suo prototipo\footnote{il prototipo illustrato è quello
- utilizzato dalle \acr{glibc}, che seguono le \textit{Single Unix
- Specification}, l'argomento \param{flags} era di tipo \type{int} nei vari
- BSD4.*, mentre nelle \acr{libc4} e \acr{libc5} veniva usato un
- \type{unsigned int}; l'argomento \param{len} era \type{int} nei vari BSD4.*
- e nelle \acr{libc4}, ma \type{size\_t} nelle \acr{libc5}; infine l'argomento
- \param{tolen} era \type{int} nei vari BSD4.* nelle \acr{libc4} e nelle
- \acr{libc5}.} è:
+la destinazione dei dati trasmessi o ottenere l'origine dei dati ricevuti. La
+prima di queste funzioni è \funcd{sendto} ed il suo prototipo\footnote{il
+ prototipo illustrato è quello utilizzato dalle \acr{glibc}, che seguono le
+ \textit{Single Unix Specification}, l'argomento \param{flags} era di tipo
+ \type{int} nei vari BSD4.*, mentre nelle \acr{libc4} e \acr{libc5} veniva
+ usato un \type{unsigned int}; l'argomento \param{len} era \type{int} nei
+ vari BSD4.* e nelle \acr{libc4}, ma \type{size\_t} nelle \acr{libc5}; infine
+ l'argomento \param{tolen} era \type{int} nei vari BSD4.* nelle \acr{libc4} e
+ nelle \acr{libc5}.} è:
\begin{functions}
\headdecl{sys/types.h}
\headdecl{sys/socket.h}
I primi tre argomenti sono identici a quelli della funzione \func{write} e
specificano il socket \param{sockfd} a cui si fa riferimento, il buffer
-\param{buf} che contiene i dati da inviare e relativa lunghezza \param{len}.
-Come per \func{write} la funzione ritorna il numero di byte inviati; nel caso
-di UDP però questo deve sempre corrispondere alla dimensione totale
-specificata da \param{len} in quanto i dati vengono sempre inviati in forma di
-pacchetto e non possono essere spezzati in invii successivi. Qualora non ci
-sia spazio nel buffer di uscita la funzione si blocca (a meno di non avere
-aperto il socket in modalità non bloccante), se invece non è possibile inviare
-il messaggio all'interno di un unico pacchetto (ad esempio perché eccede le
-dimensioni massime del protocollo sottostante utilizzati) essa fallisce con
-l'errore di \errcode{EMSGSIZE}.
+\param{buf} che contiene i dati da inviare e la relativa lunghezza
+\param{len}. Come per \func{write} la funzione ritorna il numero di byte
+inviati; nel caso di UDP però questo deve sempre corrispondere alla dimensione
+totale specificata da \param{len} in quanto i dati vengono sempre inviati in
+forma di pacchetto e non possono essere spezzati in invii successivi. Qualora
+non ci sia spazio nel buffer di uscita la funzione si blocca (a meno di non
+avere aperto il socket in modalità non bloccante), se invece non è possibile
+inviare il messaggio all'interno di un unico pacchetto (ad esempio perché
+eccede le dimensioni massime del protocollo sottostante utilizzato) essa
+fallisce con l'errore di \errcode{EMSGSIZE}.
I due argomenti \param{to} e \param{tolen} servono a specificare la
destinazione del messaggio da inviare, e indicano rispettivamente la struttura
-contentento l'indirizzo di quest'ultima e la relativa lunghezza. Questi
-argomenti vanno specificati stessa forma in cui lo si userebbero con
-\func{connect}: \param{to} deve cioè puntare alla struttura contenente
-l'indirizzo IP e la porta di destinazione verso cui si vogliono inviare i dati
-mentre \param{tolen} specifica la dimensione di quest'ultima.
-
-Se il socket è di un tipo che prevede le connessioni (ad esempio qualora si
-usi la funzione con un socket TCP), questo deve essere già connesso prima di
-eseguire la funzione, altrimenti si avrà un errore di \errcode{ENOTCONN};
-inoltre in questo caso gli argomenti \param{to} e \param{tolen} devono essere
-inizializzati rispettivamente a \const{NULL} e 0 (di solito vengono ignorati,
-ma si potrebbe anche ricevere un errore di \errcode{EISCONN}).
-
-Infine l'argomento \param{flags} è un intero usato come maschera binaria che
-permette di impostare una serie di modalità di funzionamento della
-comunicazione attraverso il socket (come \const{MSG\_NOSIGNAL} che impedisce
-l'invio del segnale \const{SIGPIPE} quando si è già chiuso il capo locale
-della connessione). Torneremo con maggiori dettagli sul significato di questo
-argomento in \secref{sec:xxx_sendmsg}, per il momento ci si può limitare ad
-usare sempre un valore nullo.
+contentente l'indirizzo di quest'ultima e la sua dimensione; questi argomenti
+vanno specificati stessa forma in cui li si sarebbero usati con
+\func{connect}. Nel nostro caso \param{to} devrà puntare alla struttura
+contenente l'indirizzo IP e la porta di destinazione verso cui si vogliono
+inviare i dati (questo è indifferente rispetto all'uso di TCP o UDP, usando
+socket diversi si sarebbero dovute utilizzare le rispettive strutture degli
+indirizzi).
+
+Se il socket è di un tipo che prevede le connessioni (ad esempio un socket
+TCP), questo deve essere già connesso prima di poter eseguire la funzione, in
+caso contrario si riceverà un errore di \errcode{ENOTCONN}. In questo
+specifico caso in cui gli argomenti \param{to} e \param{tolen} non servono
+essi devranno essere inizializzati rispettivamente a \const{NULL} e 0;
+normalmente quando si opera su un socket conesso essi vengono ignorati, ma
+qualora si sia specificato un indirizzo è possibile ricevere un errore di
+\errcode{EISCONN}.
+
+Finora abbiamo tralasciato l'argomento \param{flags}; questo è un intero usato
+come maschera binaria che permette di impostare una serie di modalità di
+funzionamento della comunicazione attraverso il socket (come
+\const{MSG\_NOSIGNAL} che impedisce l'invio del segnale \const{SIGPIPE} quando
+si è già chiuso il capo locale della connessione). Torneremo con maggiori
+dettagli sul significato di questo argomento in \secref{sec:xxx_sendmsg}, dove
+tratteremo le funzioni avanzate dei socket, per il momento ci si può limitare
+ad usare sempre un valore nullo.
La seconda funzione utilizzata nella comunicazione fra socket UDP è
-\funcd{recvfrom} che serve a ricevere i dati inviati da un altro socket, il
+\funcd{recvfrom}, che serve a ricevere i dati inviati da un altro socket; il
suo prototipo\footnote{il prototipo è quello delle \acr{glibc} che seguono le
\textit{Single Unix Specification}, i vari BSD4.*, le \acr{libc4} e le
\acr{libc5} usano un \type{int} come valore di ritorno; per gli argomenti
Come per \func{sendto} i primi tre argomenti sono identici agli analoghi di
\func{read}: dal socket vengono letti \param{len} byte che vengono salvati nel
buffer \param{buf}. A seconda del tipo di socket (se di tipo \textit{datagram}
-o \textit{stream}) inoltre i byte in eccesso che non sono stati letti possono
+o di tipo \textit{stream}) i byte in eccesso che non sono stati letti possono
rispettivamente andare persi o restare disponibili per una lettura successiva.
Se non sono disponibili dati la funzione si blocca, a meno di non aver aperto
il socket in modalità non bloccante, nel qual caso si avrà il solito errore di
argoment}). Se non si è interessati a questa informazione, entrambi gli
argomenti devono essere inizializzati al valore \const{NULL}.
-Un'altra differenza fondamentale di queste funzioni rispetto alle usuali
-\func{read} e \func{write} che abbiamo usato con i socket TCP è che in questo
-caso è perfettamente legale inviare con \func{sendto} un pacchetto vuoto (che
-nel caso conterrà solo le intestazioni di IP e di UDP), scrivendo 0 byte. Allo
-stesso modo è possibile ricevere con \func{recvfrom} un valore di ritorno di 0
-byte, senza che questo possa configurarsi come una chiusura della
-connessione\footnote{dato che la connessione non esiste, non ha senso parlare
- di chiusura della connessione, questo significa anche che con i socket UDP
- non è necessario usare \func{close} o \func{shutdown} per terminare la
- cominicazione.} o come una cessazione delle comunicazioni.
+Una differenza fondamentale del comportamento di queste funzioni rispetto alle
+usuali \func{read} e \func{write} che abbiamo usato con i socket TCP è che in
+questo caso è perfettamente legale inviare con \func{sendto} un pacchetto
+vuoto (che nel caso conterrà solo le intestazioni di IP e di UDP),
+specificando un valore nullo per \param{len}. Allo stesso modo è possibile
+ricevere con \func{recvfrom} un valore di ritorno di 0 byte, senza che questo
+possa configurarsi come una chiusura della connessione\footnote{dato che la
+ connessione non esiste, non ha senso parlare di chiusura della connessione,
+ questo significa anche che con i socket UDP non è necessario usare
+ \func{close} o \func{shutdown} per terminare la cominicazione.} o come una
+cessazione delle comunicazioni.
-\subsection{Un client elementare}
-\label{sec:UDP_simple_server}
+\subsection{Un client UDP elementare}
+\label{sec:UDP_daytime_client}
Vediamo allora come implementare un primo client elementare con dei socket
-UDP; ricalcando quanto fatto nel caso dei socket TCP prenderemo come primo
+UDP. Ricalcando quanto fatto nel caso dei socket TCP prenderemo come primo
esempio l'uso del servizio \textit{daytime}, utilizzando questa volta UDP. Il
servizio è definito nell'\href{http://www.ietf.org/rfc/rfc0862.txt}{RFC~867},
che nel caso di uso di UDP prescrive che il client debba inviare un pacchetto
\normalsize
\caption{Sezione principale del client per il servizio \textit{daytime} su
UDP.}
- \label{fig:UDP_daytime}
+ \label{fig:UDP_daytime_client}
\end{figure}
-In \figref{fig:UDP_daytime} è riportato la sezione principale del codice del
-nostro client, il contenuto completo si trova nel file \file{UDP\_daytime.c}
-dei sorgenti allegati; al solito si è tralasciata la gestione delle opzioni a
-riga di comando (nel caso praticamente assenti).
+In \figref{fig:UDP_daytime_client} è riportato la sezione principale del
+codice del nostro client, il sorgente completo si trova nel file
+\file{UDP\_daytime.c} distribuito con gli esempi allegati alla guida; al
+solito si è tralasciato di riportare in figura la sezione relativa alla
+gestione delle opzioni a riga di comando (nel caso praticamente assenti).
Il programma inizia (\texttt{\small 9--12}) con la creazione del socket, al
solito uscendo dopo aver stampato un messaggio in caso errore. Si noti come in
risposta del server. Si controlla poi (\texttt{\small 29--32}) che non vi
siano stati errori in ricezione (uscendo con un messaggio in caso contrario);
se è tutto a posto la variabile \var{nread} conterrà la dimensione del
-messaggio di risposta inviato dal server memorizzato su \var{buffer}, se
-(\texttt{\small 34}) pertanto il valore è positivo si provvederà
-(\texttt{\small 35}) a terminare la stringa contenuta nel buffer di
+messaggio di risposta inviato dal server che è stato memorizzato su
+\var{buffer}, se (\texttt{\small 34}) pertanto il valore è positivo si
+provvederà (\texttt{\small 35}) a terminare la stringa contenuta nel buffer di
lettura\footnote{si ricordi che, come illustrato in
\secref{sec:TCP_daytime_client}, il server invia in risposta una stringa
contenente la data, terminata dai due carratteri CR e LF, che pertanto prima
- di essere stampata deve essere opportunamente terminata con NUL.} e a
-stamparla (\texttt{\small 36}) sullo standard output, controllando
-(\texttt{\small 36--38}) anche in questo caso l'esito dell'operazione ed
-uscendo in caso di errore.
+ di essere stampata deve essere opportunamente terminata con un NUL.} e a
+stamparla (\texttt{\small 36}) sullo standard output, controllando anche in
+questo caso (\texttt{\small 36--38}) l'esito dell'operazione, ed uscendo con
+un messaggio in caso di errore.
Se pertanto si è avuto cura di attivare il server del servizio
\textit{daytime}\footnote{di norma questo è un servizio standard fornito dal
tcpdump: listening on lo
23:41:21.645579 localhost.32780 > localhost.daytime: udp 0 (DF)
23:41:21.645710 localhost.daytime > localhost.32780: udp 26 (DF)
-\end{verbatim}
+\end{verbatim}
+
+Una differenza fondamentale del nostro client è che in questo caso, non
+disponendo di una connessione, è per lui impossibile riconoscere errori di
+invio relativi alla rete. La funzione \func{sendto} infatti riporta solo
+errori locali, i dati vengono comunque scritti e la funzione ritorna senza
+errori anche se il server non è raggiungibile o non esiste un server in
+ascolto sull'indirizzo di destinazione. Questo comporta ad esempio che se si
+usa il nostro programma interrogando un server inesistente questo resterà
+perennemente bloccato nella chiamata a \func{recvfrom}, fin quando non lo
+interromperemo. Vedremo in \secref{sec:UDP_connect} come si può porre rimedio
+a questa problematica.
+
+
+
+\subsection{Un server UDP elementare}
+\label{sec:UDP_daytime_server}
+
+Nella sezione precedente abbiamo visto come scrivere un client elementare per
+servizio \textit{daytime}, vediamo in questa come deve essere scritto un
+server. Si ricordi che il compito di quest'ultimo è quello di ricevere un
+pacchetto di richiesta ed inviare in risposta un pacchetto contenente una
+stringa con la data corrente.
+
+\begin{figure}[!htb]
+ \footnotesize \centering
+ \begin{minipage}[c]{15.6cm}
+ \includecodesample{listati/UDP_daytimed.c}
+ \end{minipage}
+ \normalsize
+ \caption{Sezione principale del server per il servizio \textit{daytime} su
+ UDP.}
+ \label{fig:UDP_daytime_server}
+\end{figure}
+
+In \figref{fig:UDP_daytime_server} è riportato la sezione principale del
+codice del nostro client, il sorgente completo si trova nel file
+\file{UDP\_daytimed.c} distribuito con gli esempi allegati alla guida; anche
+in questo caso si è omessa la sezione relativa alla gestione delle opzioni a
+riga di comando (la sola presente è \texttt{-v} che permette di stampare a
+video l'indirizzo associato ad ogni richiesta).
+
+Anche in questo caso la prima parte del server (\texttt{\small 9--23}) è
+sostanzialmente identica a quella dell'analogo server per TCP illustrato in
+\figref{fig:TCP_daytime_cunc_server_code}; si inizia (\texttt{\small 10}) con
+il creare il socket, uscendo con un messaggio in caso di errore
+(\texttt{\small 10--13}), e di nuovo la sola differenza con il caso precedente
+è il diverso tipo di socket utilizzato. Dopo di che (\texttt{\small 14--18})
+si inizializza la struttura degli indirizzi che poi (\texttt{\small 20}) verrà
+usata da \func{bind}; si cancella (\texttt{\small 15}) preventivamente il
+contenuto, si imposta (\texttt{\small 16}) la famiglia dell'indirizzo, la
+porta (\texttt{\small 17}) e l'indirizzo (\texttt{\small 18}) su cui si
+riceveranno i pacchetti. Si noti come in quest'ultimo sia l'indirizzo
+generico \const{INADDR\_ANY}; questo significa (si ricordi quanto illustrato
+in \secref{sec:TCP_func_bind}) che il server accetterà pacchetti su uno
+qualunque degli indirizzi presenti sulle interfacce di rete della macchina.
+
+Completata l'inizializzazione tutto quello che resta da fare è eseguire
+(\texttt{\small 20--23}) la chiamata a \func{bind}, controllando la presenza
+di eventuali errori, ed uscendo con un avviso qualora questo fosse il caso.
+Nel caso di socket UDP questo è tutto quello che serve per consentire al
+server di ricevere i pacchetti a lui indirizzati, e non è più necessario
+chiamare successivamente \func{listen}. In questo caso infatti non esiste il
+concetto di connessione, e quindi non deve essere predisposta una coda delle
+connessioni entranti. Nel caso di UDP i pacchetti arrivano al kernel con un
+certo indirizzo ed una certa porta di destinazione, il kernel controlla se
+corrispondono ad un socket che è stato \textsl{legato} ad essi con
+\func{bind}, qualora questo sia il caso scriverà il contenuto all'interno del
+socket, così che il programma possa leggerlo, altrimenti risponderà alla
+macchina che ha inviato il pacchetto con un messaggio ICMP di tipo
+\textit{port unreachable}.
+
--- /dev/null
+/* UDP_daytimed.c
+ *
+ * Copyright (C) 2004 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 daytimed:
+ * Elementary TCP server for daytime service (port 13)
+ *
+ * Author: Simone Piccardi
+ * Mar. 2004
+ *
+ * Usage: daytimed -h give all info
+ *
+ * $Id: UDP_daytimed.c,v 1.1 2004/03/21 18:30:35 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
+/* Program begin */
+void usage(void);
+int main(int argc, char *argv[])
+{
+/*
+ * Variables definition
+ */
+ int sock;
+ int i, n, len, verbose=0;
+ struct sockaddr_in addr;
+ 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, "hv")) != -1) {
+ switch (i) {
+ /*
+ * Handling options
+ */
+ case 'h':
+ printf("Wrong -h option use\n");
+ usage();
+ return(0);
+ break;
+ case 'v':
+ verbose = 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_DGRAM, 0)) < 0) {
+ perror("Socket creation error");
+ exit(-1);
+ }
+ /* initialize address */
+ memset((void *)&addr, 0, sizeof(addr)); /* clear server address */
+ addr.sin_family = AF_INET; /* address type is INET */
+ addr.sin_port = htons(13); /* daytime port is 13 */
+ addr.sin_addr.s_addr = htonl(INADDR_ANY); /* connect from anywhere */
+ /* bind socket */
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("bind error");
+ exit(-1);
+ }
+ /* write daytime to client */
+ while (1) {
+ timeval = time(NULL);
+ n = recvfrom(sock, buffer, MAXLINE, 0, (struct sockaddr *)&addr, &len);
+ if (n < 0) {
+ perror("recvfrom error");
+ exit(-1);
+ }
+ if (verbose) {
+ inet_ntop(AF_INET, &addr.sin_addr, buffer, sizeof(buffer));
+ printf("Request from host %s, port %d\n", buffer,
+ ntohs(addr.sin_port));
+ }
+ snprintf(buffer, sizeof(buffer), "%.24s\r\n", ctime(&timeval));
+ n = sendto(sock, buffer, strlen(buffer), 0,
+ (struct sockaddr *)&addr, sizeof(addr));
+ if (n < 0) {
+ perror("sendto error");
+ exit(-1);
+ }
+ }
+ /* normal exit */
+ 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);
+}