+
+\section{I server concorrenti 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 \func{fork} per far creare al server per ogni richiesta da
+parte di un client un processo figlio che si incarichi della gestione della
+comunicazione.
+
+
+\subsection{Un esempio di server \textit{daytime} concorrente}
+\label{sec:TCPel_cunc_daytime}
+
+Per illustrare il meccanismo usato in generale per creare un server
+concorrente abbiamo riscritto il server \texttt{daytime} dell'esempio
+precedente in forma concorrente, inserendo anche una opzione per la stampa
+degli indirizzi delle connessioni ricevute.
+
+In \figref{fig:TCPel_serv_code} è mostrato un estratto del codice, in cui si
+sono tralasciati il trattamento delle opzioni e le parti rimaste invariate
+rispetto al precedente esempio. Al solito il sorgente completo del server
+\file{ElemDaytimeTCPCuncServ.c} è allegato nella directory dei sorgenti.
+
+\begin{figure}[!htb]
+ \footnotesize
+ \begin{lstlisting}{}
+#include <sys/types.h> /* predefined types */
+#include <unistd.h> /* include unix standard library */
+#include <arpa/inet.h> /* IP addresses conversion utililites */
+#include <sys/socket.h> /* socket library */
+#include <stdio.h> /* include standard I/O library */
+#include <time.h>
+
+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:TCPel_serv_code}
+\end{figure}
+
+Come si può vedere (alle linee \texttt{\small 21--25}) la funzione
+\func{accept} stavolta è chiamata fornendo una struttura di indirizzi in cui
+saranno ritornati numero IP e porta da cui il client effettua la connessione,
+che stamperemo, se avremo abilitato il logging, sullo standard output
+(\texttt{\small 39--43}).
+
+Quando \func{accept} ritorna il server chiama la funzione \func{fork}
+(\texttt{\small 26--30}) per creare il processo figlio che effettuerà tutte le
+operazioni relative a quella connessione (\texttt{\small 31--45}), mentre il
+padre resterà in attesa di ulteriori connessioni.
+
+Si noti come il figlio operi solo sul socket connesso, chiudendo
+immediatamente il socket \var{list\_fd}; mentre il padre continua ad operare
+solo sul socket in ascolto chiudendo \var{sock\_fd} dopo ciascuna
+\func{accept}. Per quanto abbiamo detto in \secref{sec:TCPel_func_close}
+queste due chiusure non causano l'innesco della sequenza di chiusura perché il
+numero di riferimenti non si è annullato.
+
+Infatti subito dopo la creazione del socket \var{list\_fd} ha una
+referenza, e lo stesso vale per \var{sock\_fd} dopo il ritorno di
+\func{accept}, ma dopo la fork i descrittori vengono duplicati nel padre e
+nel figlio per cui entrambi i socket si trovano con due referenze. Questo fa
+si che quando il padre chiude \var{sock\_fd} esso resta con una referenza
+da parte del figlio, e sarà definitivamente chiuso solo quando quest'ultimo,
+dopo aver completato le sue operazioni, chiamerà la funzione \func{close}.
+
+In realtà per il figlio non sarebbero necessarie nessuna delle due chiamate a
+\func{close} in quanto nella \func{exit} tutti i file ed i socket vengono
+chiusi, ma si è preferito effettuare la chiusura esplicitamente per avere una
+maggiore chiarezza del codice ed evitare possibili errori.
+
+Si noti come sia essenziale che il padre chiuda ogni volta il socket connesso
+dopo la \func{accept}; se così non fosse nessuno di questi socket sarebbe
+effettivamente chiuso dato che alla chiusura da parte del figlio resterebbe
+ancora un riferimento. Si avrebbero così due effetti, il padre potrebbe
+esaurire i descrittori disponibili (che sono un numero limitato per ogni
+processo) e soprattutto nessuna delle connessioni con i client verrebbe
+chiusa.
+
+
+\subsection{Le funzioni \func{getsockname} e \func{getpeername}}
+\label{sec:TCPel_get_names}
+
+Queste due funzioni vengono usate per ottenere i dati relativi alla socket
+pair associata ad un certo socket; la prima è \funcd{getsockname} e
+restituisce l'indirizzo locale; il suo prototipo è:
+\begin{prototype}{sys/socket.h}
+ {int getsockname(int sockfd, struct sockaddr * name, socklen\_t * namelen)}
+ Legge l'indirizzo locale del socket \param{sockfd} nella struttura
+ \param{name}.
+
+\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di
+ errore. I codici di errore restituiti in \var{errno} sono i seguenti:
+ \begin{errlist}
+ \item[\errcode{EBADF}] l'argomento \param{sockfd} non è un file descriptor
+ valido.
+ \item[\errcode{ENOTSOCK}] l'argomento \param{sockfd} non è un socket.
+ \item[\errcode{ENOBUFS}] non ci sono risorse sufficienti nel sistema per
+ eseguire l'operazione.
+ \item[\errcode{EFAULT}] l'argomento \param{name} punta al di fuori dello
+ spazio di indirizzi del processo.
+ \end{errlist}}
+\end{prototype}
+
+La funzione si usa tutte le volte che si vuole avere l'indirizzo locale di un
+socket; ad esempio può essere usata da un client (che usualmente non chiama
+\func{bind}) per ottenere numero IP e porta locale associati al socket
+restituito da una \func{connect}, o da un server che ha chiamato \func{bind}
+su un socket usando 0 come porta locale per ottenere il numero di porta
+effimera assegnato dal kernel.
+
+Inoltre quando un server esegue una \func{bind} su un indirizzo generico, se
+chiamata dopo il completamento di una connessione sul socket restituito da
+\func{accept}, restituisce l'indirizzo locale che il kernel ha assegnato a
+quella connessione.
+
+Tutte le volte che si vuole avere l'indirizzo remoto di un socket si usa la
+funzione \funcd{getpeername}, il cui prototipo è:
+\begin{prototype}{sys/socket.h}
+ {int getpeername(int sockfd, struct sockaddr * name, socklen\_t * namelen)}
+ Legge l'indirizzo remoto del socket \param{sockfd} nella struttura
+ \param{name}.
+
+ \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di
+ errore. I codici di errore restituiti in \var{errno} sono i seguenti:
+ \begin{errlist}
+ \item[\errcode{EBADF}] l'argomento \param{sockfd} non è un file descriptor
+ valido.
+ \item[\errcode{ENOTSOCK}] l'argomento \param{sockfd} non è un socket.
+ \item[\errcode{ENOTCONN}] il socket non è connesso.
+ \item[\errcode{ENOBUFS}] non ci sono risorse sufficienti nel sistema per
+ eseguire l'operazione.
+ \item[\errcode{EFAULT}] l'argomento \param{name} punta al di fuori dello
+ spazio di indirizzi del processo.
+ \end{errlist}}
+\end{prototype}
+
+Ci si può chiedere a cosa serva questa funzione dato che dal lato client
+l'indirizzo remoto è sempre noto quando si esegue la \func{connect} mentre
+dal lato server si possono usare, come si è fatto nell'esempio precedente, i
+valori di ritorno di \func{accept}.
+
+In generale però questa ultima possibilità è sempre possibile. In particolare
+questo avviene quando il server invece di far gestire la connessione
+direttamente a un processo figlio, come nell'esempio precedente, lancia un
+opportuno programma per ciascuna connessione usando \func{exec} (questa ad
+esempio è la modalità con cui opera il \textsl{super-server} \cmd{inetd}
+che gestisce tutta una serie di servizi lanciando per ogni connessione
+l'opportuno server).
+
+In questo caso benché il processo figlio abbia una immagine della memoria che
+è copia di quella del processo padre (e contiene quindi anche la struttura
+ritornata da \func{accept}), all'esecuzione di \func{exec} viene caricata
+in memoria l'immagine del programma eseguito che a questo punto perde ogni
+riferimento. Il socket descriptor però resta aperto. Allora se una opportuna
+convenzione è seguita per rendere noto al programma eseguito qual'è il socket
+connesso (\cmd{inetd} ad esempio fa sempre in modo che i file descriptor 0,
+1 e 2 corrispondano al socket connesso) quest'ultimo potrà usare la funzione
+\func{getpeername} per determinare l'indirizzo remoto del client.
+
+Infine è da chiarire (si legga la pagina di manuale) che, come per
+\func{accept}, il terzo parametro, che è specificato dallo standard POSIX.1g
+come di tipo \code{socklen\_t *} in realtà deve sempre corrispondere ad un
+\ctyp{int *} come prima dello standard perché tutte le implementazioni dei
+socket BSD fanno questa assunzione.
+
+
+
+%%% Local Variables:
+%%% mode: latex
+%%% TeX-master: "gapil"
+%%% End: