Aggiunta altre fregnacce
[gapil.git] / simpltcp.tex
index 5ee5a87a309861395ea5b76d4b224f1ec50f2eea..4b2efecbfd09d47887d2f1a33cc069da0ffc7de8 100644 (file)
@@ -25,7 +25,7 @@ uscita, fintanto che il chiamante non ha chiude la connessione; il servizio
 opera sulla porta 7.
 
 Nel nostro caso l'esempio sarà costituito da un client che legge una linea di
 opera sulla porta 7.
 
 Nel nostro caso l'esempio sarà costituito da un client che legge una linea di
-caratteri dallo standard input e la scrive sul server, il server leggerà una
+caratteri dallo standard input e la scrive sul server, il server leggerà la
 linea dalla connessione e la riscriverà all'indietro; sarà compito del client
 leggere la risposta del server e stamparla sullo standard output.
 
 linea dalla connessione e la riscriverà all'indietro; sarà compito del client
 leggere la risposta del server e stamparla sullo standard output.
 
@@ -33,6 +33,283 @@ Si 
 il prototipo ideale di una generica applicazione di rete in cui un server
 risponde alle richieste di un client; tutto quello che cambia nel caso si una
 applicazione più complessa è la elaborazione dell'input del client da parte
 il prototipo ideale di una generica applicazione di rete in cui un server
 risponde alle richieste di un client; tutto quello che cambia nel caso si una
 applicazione più complessa è la elaborazione dell'input del client da parte
-del server nel fornire le risposte in uscita.
+del server nel fornire le risposte in uscita. 
+
+\subsection{La struttura del server}
+\label{sec:TCPsimp_server_main}
+
+Il server si compone di un corpo principale, costituito dalla funzione
+\texttt{main} che si incarica di creare il socket, metterlo in ascolto di
+connessioni in arrivo e creare un processo figlio a cui delegare la gestione
+di ciascuna connessione. Questa parte, riportata in \nfig, è sostanzialmente
+identica a quella vista nell'esempio in \secref{sec:TCPelem_serv_code}.
+
+\begin{figure}[!htb]
+  \footnotesize
+  \begin{lstlisting}{}
+/* Subroutines declaration */
+void SockEcho(int sockfd);
+/* Program beginning */
+int main(int argc, char *argv[])
+{
+    int list_fd, conn_fd;
+    pid_t pid;
+    struct sockaddr_in serv_add;
+     ....
+    /* 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);
+    }
+    /* handle echo to client */
+    while (1) {
+        /* accept connection */
+        if ( (conn_fd = accept(list_fd, NULL, NULL)) < 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);          /* close listening socket */   
+            SockEcho(conn_fd);       /* handle echo */
+            exit(0);
+        } else {             /* parent */
+            close(conn_fd);          /* close connected socket */
+        }
+    }
+    /* normal exit, never reached */
+    exit(0);
+}
+  \end{lstlisting}
+  \caption{Codice della funzione \texttt{main} della prima versione del server
+    per il servizio \texttt{echo}.}
+  \label{fig:TCPsimpl_serv_code}
+\end{figure}
+
+La struttura di questa prima versione del server è sostanzialmente a quella
+dell'esempio precedente, ed ad esso si applicano le considerazioni fatte in
+\secref{sec:TCPel_cunc_daytime}. Le uniche differenze rispetto all'esempio in
+\figref{fig:TCPelem_serv_code} sono che in questo caso per il socket in
+ascolto viene usata la porta 7 e tutta la gestione della comunicazione è
+delegata alla funzione \texttt{SockEcho}. Per ogni connessione viene creato un
+processo figlio, il quale si incarica di lanciare la funzione
+\texttt{SockEcho}.
+
+Il codice della funzione \texttt{SockEcho} è invece mostrata in \nfig, la
+comunicazione viene gestita all'interno del ciclo (linee \texttt{\small
+  6--8}).  I dati inviati dal client vengono letti dal socket con una semplice
+\texttt{read} (che ritorna solo in presenza di dati in arrivo), la riscrittura
+viene invece gestita dalla funzione \texttt{SockWrite} (descritta a suo tempo
+in \figref{fig:sock_SockWrite_code}) che si incarica di tenere conto
+automaticamente della possibilità che non tutti i dati di cui è richiesta la
+scrittura vengano trasmessi con una singola \texttt{write}.
+
+Quando il client chiude la connessione il ricevimento del FIN fa ritornare la
+\texttt{read} con un numero di byte letti pari a zero, il che causa l'uscita
+dal ciclo e il ritorno della funzione, che a sua volta causa la terminazione
+del processo figlio.
+
+
+\begin{figure}[!htb]
+  \footnotesize
+  \begin{lstlisting}{}
+void SockEcho(int sockfd) {
+    char buffer[MAXLINE];
+    int nread, nwrite;
+    
+    /* main loop, reading 0 char means client close connection */
+    while ( (nread = read(sockfd, buffer, MAXLINE)) != 0) {
+        nwrite = SockWrite(sockfd, buffer, nread);
+    }
+    return;
+}
+  \end{lstlisting}
+  \caption{Codice della prima versione della funzione \texttt{SockEcho} per la
+    gestione del servizio \texttt{echo}.}
+  \label{fig:TCPsimpl_sockecho_code}
+\end{figure}
+
+
+\subsection{Il client}
+\label{sec:TCPsimp_server_main}
+
+Il codice del client è riportato in \nfig, anche esso ricalca la struttura del
+precedente client per il servizio \texttt{daytime} (vedi
+\secref{sec:net_cli_sample}) ma, come per il server, lo si è diviso in due
+parti, inserendo la parte relativa alle operazioni specifiche previste per il
+protocollo \texttt{echo} in una funzione a parte.
+\begin{figure}[!htb]
+  \footnotesize
+  \begin{lstlisting}{}
+int main(int argc, char *argv[])
+{
+/* 
+ * Variables definition  
+ */
+    int sock_fd, i;
+    struct sockaddr_in serv_add;
+    ...
+    /* create socket */
+    if ( (sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+        perror("Socket creation error");
+        return -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(7);                    /* echo port is 7 */
+    /* build address using inet_pton */
+    if ( (inet_pton(AF_INET, argv[optind], &serv_add.sin_addr)) <= 0) {
+        perror("Address creation error");
+        return -1;
+    }
+    /* extablish connection */
+    if (connect(sock_fd, (struct sockaddr *)&serv_add, sizeof(serv_add)) < 0) {
+        perror("Connection error");
+        return -1;
+    }
+    /* read daytime from server */
+    EchoClient(stdin, sock_fd);
+    /* normal exit */
+    return 0;
+}
+  \end{lstlisting}
+  \caption{Codice della prima versione del client \texttt{echo}.}
+  \label{fig:TCPsimpl_sockecho_code}
+\end{figure}
+
+La funzione \texttt{main} si occupa della creazione del socket e della
+connessione (linee \texttt{\small 10--27}) secondo la stessa modalità spiegata
+in \secref{sec:net_cli_sample}, il client si connette sulla porta 7
+all'indirizzo specificato dalla linea di comando (a cui si è aggiunta una
+elementare gestione delle opzioni non riportata in figura).
+
+Completata la connessione (quando la funzione \texttt{connect} ritorna) La
+funzione \texttt{EchoClient}, riportata in \nfig, si preoccupa di gestire la
+comunicazione, leggendo una riga alla volta dallo \texttt{stdin}, scrivendola
+sul socket e ristampando su \texttt{stdout} quanto ricevuto in risposta dal
+server. 
+
+\begin{figure}[!htb]
+  \footnotesize
+  \begin{lstlisting}{}
+void EchoClient(FILE * filein, int socket) 
+{
+    char sendbuff[MAXLINE], recvbuff[MAXLINE];
+    int nread; 
+    while (fgets(sendbuff, MAXLINE, filein) != NULL) {
+        SockWrite(socket, sendbuff, strlen(sendbuff)); 
+        nread = SockRead(socket, recvbuff, strlen(sendbuff));        
+        recvbuff[nread] = 0;
+        fputs(recvbuff, stdout);
+    }
+    return;
+}
+  \end{lstlisting}
+  \caption{Codice della prima versione della funzione \texttt{EchoClient} per 
+    la gestione del servizio \texttt{echo}.}
+  \label{fig:TCPsimpl_sockecho_code}
+\end{figure}
+
+La funzione utilizza due buffer per gestire i dati inviati e letti sul socket
+(\texttt{\small 3}).  La comunicazione viene gestita all'interno di un ciclo
+(linee \texttt{\small 5--10}), i dati da inviare sulla connessione vengono
+presi dallo \texttt{stdin} usando la funzione \texttt{fgets} che legge una
+linea di testo (terminata da un \texttt{CR} e fino al massimo di
+\texttt{MAXLINE} caratteri) e la salva sul buffer di invio, la funzione
+\texttt{SockWrite} (\texttt{\small 3}) scrive detti dati sul socket (gestendo
+l'invio multiplo, qualora una singola \texttt{write} non basta, come spiegato
+in \secref{sec:sock_io_behav}).
+
+I dati che vengono riletti indietro con una \texttt{SockRead} sul buffer di
+ricezione e viene inserita la terminazione della stringa (\texttt{\small
+  7--8}) e per poter usare la funzione \texttt{fputs} per scriverli su
+\texttt{stdout}. 
+
+Un end of file inviato su \texttt{stdin} causa il ritorno di \texttt{fgets}
+con un puntatore nullo e l'uscita dal ciclo, al che la subroutine ritorna ed
+il client esce.
+
+
+\section{Il funzionamento del servizio}
+\label{sec:TCPsimpl_normal_work}
+
+Benché il codice dell'esempio precedente sia molto ridotto, esso ci permetterà
+di considerare in dettaglio tutte le problematiche che si possono incontrare
+nello scrivere una applicazione di rete; infatti attraverso l'esame delle sue
+modalità di funzionamento normali, all'avvio e alla terminazione, e di quello
+che avviene nelle varie situazioni limite da una parte potremo approfondire la
+comprensione del protocollo TCP/IP e dall'altra ricavare le indicazioni
+necessarie per essere in gradi di scrivere applicazioni robuste, in grado di
+gestire anche i casi limite.
+
+
+\subsection{L'avvio e il funzionamento}
+\label{sec:TCPsimpl_startup}
+
+Il primo passo è compilare e lanciare il server (da root, per poter usare la
+porta 7 che è riservata), alla partenza esso eseguirà l'apertura passiva con
+la sequenza delle chiamate a \texttt{socket}, \texttt{bind}, \texttt{listen} e
+poi si bloccherà nella \texttt{accept}. A questo punto si potrà controllarne
+lo stato con \texttt{netstat}:
+
+\begin{verbatim}
+[piccardi@roke piccardi]$ netstat -ant
+Active Internet connections (servers and established)
+Proto Recv-Q Send-Q Local Address           Foreign Address         State 
+...
+tcp        0      0 *:echo                  *:*                     LISTEN
+...
+\end{verbatim} %$
+
+che ci mostra come il socket sia in ascolto sulla porta richiesta, accendo
+connessioni da qualunque indirizzo e da qualunque porta e su qualunque
+interfaccia locale.
+
+A questo punto si può lanciare il client, esso chiamerà \texttt{socket} e
+\texttt{connect}, una volta completato il three way handshake la funzione
+\texttt{connect} ritornerà nel client e la \texttt{accept} nel server e la
+connessione è stabilita, usando di nuovo \texttt{netstat} otterremmo:
+\begin{verbatim}
+Active Internet connections (servers and established)
+Proto Recv-Q Send-Q Local Address           Foreign Address         State
+tcp        0      0 *:echo                  *:*                     LISTEN
+tcp        0      0 roke:echo               gont:32981              ESTABLISHED
+\end{verbatim}
+
+A questo punto  lo stato è il seguente: 
+
+
+\begin{itemize}
+\item il client chiama la funzione \texttt{EchoClient} che si blocca sulla
+  \texttt{fgets} dato che non si è ancora scritto nulla sul terminale.
+\item il server eseguirà una \texttt{fork} facendo chiamare al processo figlo
+  la funzione \texttt{SockEcho}, quest'ultima si bloccherà sulla \texttt{read}
+  dal socket sul quale ancora non sono presenti dati.
+\item il 
+\end{itemize}
+il server eseguira una \texttt{fork} facendo chiamare al
+processo figlo la funzione \texttt{SockEcho}, la quale eseguirà una read s
+
+