From 59ee032c64ce1caa37bfc311f63219a52efe21d4 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Mon, 23 Apr 2001 22:29:00 +0000 Subject: [PATCH] Finita introduzione ai socket, con wrapper per read e write aggiunti. Iniziato socket TCp elementari (fatta struttura del capitolo). --- network.tex | 4 +- socket.tex | 166 ++++++++++++++++++++++++++++++++++++++------ sources/SockRead.c | 24 +++++++ sources/SockWrite.c | 22 ++++++ 4 files changed, 193 insertions(+), 23 deletions(-) create mode 100644 sources/SockRead.c create mode 100644 sources/SockWrite.c diff --git a/network.tex b/network.tex index 473d0eb..7d1d053 100644 --- a/network.tex +++ b/network.tex @@ -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 /* 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"); diff --git a/socket.tex b/socket.tex index fafe08e..46179b0 100644 --- a/socket.tex +++ b/socket.tex @@ -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 + +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 + +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 index 0000000..749d767 --- /dev/null +++ b/sources/SockRead.c @@ -0,0 +1,24 @@ +#include + +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 index 0000000..ddeb4c7 --- /dev/null +++ b/sources/SockWrite.c @@ -0,0 +1,22 @@ +#include + +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); +} + -- 2.30.2