Finita introduzione ai socket, con wrapper per read e write aggiunti.
authorSimone Piccardi <piccardi@gnulinux.it>
Mon, 23 Apr 2001 22:29:00 +0000 (22:29 +0000)
committerSimone Piccardi <piccardi@gnulinux.it>
Mon, 23 Apr 2001 22:29:00 +0000 (22:29 +0000)
Iniziato socket TCp elementari (fatta struttura del capitolo).

network.tex
socket.tex
sources/SockRead.c [new file with mode: 0644]
sources/SockWrite.c [new file with mode: 0644]

index 473d0eb4611c6154bffaf49896fa974791c38cd1..7d1d053d6c8c1e66f039f7b8e2e0f41cbaa8ecd9 100644 (file)
@@ -65,7 +65,7 @@ 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}[!htb]
   \footnotesize
   \begin{lstlisting}{}
 #include <sys/types.h>   /* predefined types */
@@ -210,9 +210,7 @@ int main(int argc, char *argv[])
     struct sockaddr_in serv_add;
     char buffer[MAXLINE];
     time_t timeval;
-
     ...
-
     /* create socket */
     if ( (list_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
         perror("Socket creation error");
index fafe08ebd1610b00eb7e5120b7aea0ad2fbf2d03..46179b0d079715a0db63c5e43851fb693915f078 100644 (file)
@@ -572,13 +572,13 @@ cosiddetta notazione \textit{dotted-decimal}, (cio
   order}) e viceversa; in questo caso si usa la lettera $a$ come mnemonico per
 indicare la stringa. Dette funzioni sono:
 \begin{itemize}
-\item \texttt{int inet\_aton(const char *strptr, struct in\_addr *addrptr)}
+\item \texttt{int inet\_aton(const char *src, struct in\_addr *dest)}
   
-  Converte la stringa puntata da \texttt{strptr} nell'indirizzo binario da
-  memorizzare all'indirizzo puntato da \texttt{addrptr}, restituendo 0 in caso
+  Converte la stringa puntata da \texttt{src} nell'indirizzo binario da
+  memorizzare all'indirizzo puntato da \texttt{dest}, restituendo 0 in caso
   di successo e 1 in caso di fallimento (è espressa in questa forma in modo da
   poterla usare direttamente con il puntatore usato per passare la struttura
-  degli indirizzi). Se usata con \texttt{addrptr} inizializzato a
+  degli indirizzi). Se usata con \texttt{dest} inizializzato a
   \texttt{NULL} effettua la validazione dell'indirizzo.
   
 \item \texttt{in\_addr\_t inet\_addr(const char *strptr)}
@@ -636,7 +636,7 @@ negativo e settano la variabile \texttt{errno} al valore
   \texttt{INET6\_ADDRSTRLEN} per indirizzi IPv6; la lunghezza del buffer deve
   comunque venire specificata attraverso il parametro \texttt{len}.
   
-  La funzione restitisce un puntatore non nullo a \texttt{dest} in caso di
+  La funzione restituisce un puntatore non nullo a \texttt{dest} in caso di
   successo e un puntatore nullo in caso di fallimento, in quest'ultimo caso
   viene settata la variabile \texttt{errno} con il valore \texttt{ENOSPC} in
   caso le dimensioni dell'indirizzo eccedano la lunghezza specificata da
@@ -649,35 +649,161 @@ negativo e settano la variabile \texttt{errno} al valore
 \label{sec:sock_io_behav}
 
 Una cosa di cui non sempre si è consapevoli quando si ha a che fare con i
-socket è che le funzioni di I/O non sempre hanno lo stesso comportamento che
-avrebbero con i normali files (in particolare questo è vero nel caso si stream
-socket). Infatti con i socket funzioni come \texttt{read} o \texttt{write}
-possono restituire in input o scrivere in output un numero di bytes minore di
-quello richiesto, e questo è un comportamento normale e non un errore. Ciò
-avviene perché il kernel può 
+socket è che le funzioni di input/output non sempre hanno lo stesso
+comportamento che avrebbero con i normali files (in particolare questo accade
+per i socket di tipo stream). 
+
+Infatti con i socket può accadere che funzioni come \texttt{read} o
+\texttt{write} possano restituire in input o scrivere in output un numero di
+bytes minore di quello richiesto. Questo è un comportamento normale e non un
+errore, e succede perché si eccede il limite di buffer del kernel. In questo
+caso tutto quello che il programma chiamante deve fare è di ripetere la
+lettura (o scrittura) per la quantità di bytes rimanenti (lo stesso può
+avvenire scrivendo più di 4096 bytes in una pipe, dato che quello è il limite
+di solito adottato per il buffer di trasmissione del kernel).
+
+\begin{figure}[htb]
+  \centering
+  \footnotesize
+  \begin{lstlisting}{}
+#include <unistd.h>
+
+ssize_t SockRead(int fd, void *buf, size_t count) 
+{
+    size_t nleft;
+    ssize_t nread;
+    nleft = count;
+    while (nleft > 0) {             /* repeat until no left */
+        if ( (nread = read(fd, buf, nleft)) < 0) {
+            if (errno == EINTR) {   /* if interrupted by system call */
+                continue;           /* repeat the loop */
+            } else {
+                return(nread);      /* otherwise exit */
+            }
+        } else if (nread == 0) {    /* EOF */
+            break;                  /* break loop here */ 
+        }
+        nleft -= nread;             /* set left to read */
+        buf +=nread;                /* set pointer */
+    }
+    return (count - nleft);
+}  
+  \end{lstlisting}
+  \caption{Funzione \texttt{SockRead}, legge $n$ bytes da un socket }
+  \label{fig:sock_SockRead_code}
+\end{figure}
 
+Per questo motivo seguendo l'esempio di W. R. Stevens si sono definite due
+funzioni \texttt{SockRead} e \texttt{SockWrite} che eseguono la lettura da un
+socket tenendo conto di questa caratteristica, ed in grado di ritornare dopo
+avere letto o scritto esattamente il numero di bytes specificato; il sorgente
+è riportato in \curfig\ e \nfig\ ed è disponibile fra i sorgenti allegati alla
+guida nei files \texttt{SockRead.c} e \texttt{SockWrite.c}.
 
+\begin{figure}[htb]
+  \centering
+  \footnotesize
+  \begin{lstlisting}{}
+#include <unistd.h>
+
+ssize_t SockWrite(int fd, const void *buf, size_t count) 
+{
+    size_t nleft;
+    ssize_t nwritten;
+
+    nleft = count;
+    while (nleft > 0) {             /* repeat until no left */
+        if ( (nwritten = write(fd, buf, nleft)) < 0) {
+            if (errno == EINTR) {   /* if interrupted by system call */
+                continue;           /* repeat the loop */
+            } else {
+                return(nwritten);   /* otherwise exit with error */
+            }
+        }
+        nleft -= nwritten;          /* set left to write */
+        buf +=nwritten;             /* set pointer */
+    }
+    return (count);
+}  
+  \end{lstlisting}
+  \caption{Funzione \texttt{SockWrite}, scrive $n$ bytes su un socket }
+  \label{fig:sock_SockWrite_code}
+\end{figure}
 
+Come si può notare le funzioni ripetono la lettura/scrittura in un loop fino
+all'esaurimento del numero di bytes richiesti, in caso di errore viene
+controllato se questo è \texttt{EINTR} (cioè un'interruzione della system call
+dovuta ad un segnale), nel qual caso l'accesso viene ripetuto, altrimenti
+l'errore viene ritornato interrompendo il loop.  
 
+Nel caso della lettura se il numero di bytes letti è zero significa che è
+arrivati alla fine del file e pertanto si ritorna senza aver concluso la
+lettura di tutti i bytes richiesti. 
 
 
 
 \chapter{Socket TCP elementari}
 \label{cha:elem_TCP_sock}
 
-Esamineremo in questo capitolo quanto necessario per capire come scrivere un
-client e un server TCP, riprendendo quanto visto in \ref{sec:net_cli_sample} e
-\ref{sec:net_cli_server}. 
+In questo capitolo esamineremo i vari dettagli necessari per capire il
+funzionamento dei socket TCP, partendo dai due esempi elementari visti in
+precedenza (vedi \ref{sec:net_cli_sample} e \ref{sec:net_cli_server}), per
+arrivare a scrivere una semplice applicazione client/server completa
+(l'implementazione del servizio \texttt{time} su TCP).
 
+Tratteremo qui dunque il funzionamento delle varie funzioni che si sono usate
+nell'esempio precedente e daremo una descrizione delle principali
+caratteristiche del funzionamento di una connessione TCP.
 
-\subsection{Creazione e terminazione della connessione TCP}
+\section{Il funzionamento di una connessione TCP}
+\label{sec:TCPel_connession}
+
+Prima di entrare nei dettagli del funzionamento delle funzioni della
+interfaccia dei socket che operano con TCP (\texttt{connect}, \texttt{accept},
+\texttt{close}) è fondamentale capire alcune basi del funzionamento di una
+connessione TCP, in particolare su come la si stabilisce e come la si
+conclude e sul diagramma degli stati del TCP.
+
+\subsection{Creazione: il \textit{three way handshake}}
+\label{sec:TCPel_conn_cre}
+
+\subsection{Il significato delle opzioni del TCP}
+\label{sec:TCPel_TCP_opt}
+
+\subsection{La terminazione della connessione}
+\label{sec:TCPel_conn_term}
+
+\subsection{Il diagramma delle transizioni di stato}
+\label{sec:TCPel_trans_dia}
+
+\subsection{Lo stato \texttt{TIME\_WAIT}}
+\label{sec:TCPel_time_wait}
+
+
+\section{I numeri di porta}
+\label{sec:TCPel_ports}
+
+\section{Le funzioni dei socket TCP}
+\label{sec:TCPel_functions}
+
+\subsection{La funzione \texttt{connect}}
+\label{sec:TCPel_func_connect}
+
+\subsection{La funzione \texttt{bind}}
+\label{sec:TCPel_func_bind}
+
+\subsection{La funzione \texttt{listen}}
+\label{sec:TCPel_func_listen}
+
+\subsection{La funzione \texttt{connect}}
+\label{sec:TCPel_func_connect}
+
+\subsection{La funzione \texttt{accept}}
+\label{sec:TCPel_func_accept}
 
-Per capire il funzionamento delle funzioni della interfaccia dei socket che
-operano con TCP (le varie \texttt{connect}, \texttt{accept}, \texttt{close}
-che abbiamo visto negli esempi iniziali e su cui torneremo più avanti) è
-fodamentale capire come funziona la creazione e la conclusione di una
-connessione TCP.
 
 \subsection{Le porte}
 
 
+
diff --git a/sources/SockRead.c b/sources/SockRead.c
new file mode 100644 (file)
index 0000000..749d767
--- /dev/null
@@ -0,0 +1,24 @@
+#include <unistd.h>
+
+ssize_t SockRead(int fd, void *buf, size_t count) 
+{
+    size_t nleft;
+    ssize_t nread;
+    nleft = count;
+    while (nleft > 0) {             /* repeat until no left */
+       if ( (nread = read(fd, buf, nleft)) < 0) {
+           if (errno == EINTR) {   /* if interrupted by system call */
+               continue;           /* repeat the loop */
+           } else {
+               return(nread);      /* otherwise exit */
+           }
+       } else if (nread == 0) {    /* EOF */
+           break;                  /* break loop here */ 
+       }
+       nleft -= nread;             /* set left to read */
+       buf +=nread;                /* set pointer */
+    }
+    return (count - nleft);
+}
+
diff --git a/sources/SockWrite.c b/sources/SockWrite.c
new file mode 100644 (file)
index 0000000..ddeb4c7
--- /dev/null
@@ -0,0 +1,22 @@
+#include <unistd.h>
+
+ssize_t SockWrite(int fd, const void *buf, size_t count) 
+{
+    size_t nleft;
+    ssize_t nwritten;
+
+    nleft = count;
+    while (nleft > 0) {             /* repeat until no left */
+       if ( (nwritten = write(fd, buf, nleft)) < 0) {
+           if (errno == EINTR) {   /* if interrupted by system call */
+               continue;           /* repeat the loop */
+           } else {
+               return(nwritten);   /* otherwise exit with error */
+           }
+       }
+       nleft -= nwritten;          /* set left to write */
+       buf +=nwritten;             /* set pointer */
+    }
+    return (count);
+}
+