+%% simpltcp.tex
+%%
+%% Copyright (C) 2000-2002 Simone Piccardi. Permission is granted to
+%% copy, distribute and/or modify this document under the terms of the GNU Free
+%% Documentation License, Version 1.1 or any later version published by the
+%% Free Software Foundation; with the Invariant Sections being "Prefazione",
+%% with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the
+%% license is included in the section entitled "GNU Free Documentation
+%% License".
+%%
\chapter{Un esempio completo di client/server TCP}
\label{cha:simple_TCP_sock}
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 \figref{fig:TCPsimpl_serv_code}, è analoga a quella vista
-nel precedente esempio esaminato in \secref{sec:TCPel_cunc_serv}.
+nel precedente esempio esaminato in \secref{sec:TCP_cunc_daytime}.
\begin{figure}[!htb]
- \footnotesize
- \begin{lstlisting}{}
-/* Subroutines declaration */
-void ServEcho(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}
+ \footnotesize \centering
+ \begin{minipage}[c]{15.6cm}
+ \includecodesample{listati/ElemEchoTCPServer.c}
+ \end{minipage}
+ \normalsize
\caption{Codice della funzione \code{main} della prima versione del server
per il servizio \texttt{echo}.}
\label{fig:TCPsimpl_serv_code}
La struttura di questa prima versione del server è sostanzialmente identica a
quella dell'esempio citato, ed ad esso si applicano le considerazioni fatte in
-\secref{sec:TCPel_cunc_daytime}. Le uniche differenze rispetto all'esempio in
-\figref{fig:TCPel_serv_code} sono che in questo caso per il socket in ascolto
+\secref{sec:TCP_cunc_daytime}. Le uniche differenze rispetto all'esempio in
+\figref{fig:TCP_serv_code} sono che in questo caso per il socket in ascolto
viene usata la porta 7 e che tutta la gestione della comunicazione è delegata
alla funzione \code{ServEcho}.
% Per ogni connessione viene creato un
% processo figlio, il quale si incarica di lanciare la funzione
% \texttt{SockEcho}.
-Il codice della funzione \code{ServEcho} è 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
-\func{read} (che ritorna solo in presenza di dati in arrivo), la riscrittura
-viene invece gestita dalla funzione \func{SockWrite} (descritta 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 \func{write}.
+Il codice della funzione \code{ServEcho} è invece mostrata in
+\figref{fig:TCPsimpl_server_elem_sub}, 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 \func{read} (che ritorna solo in
+presenza di dati in arrivo), la riscrittura viene invece gestita dalla
+funzione \func{FullWrite} (descritta in \figref{fig:sock_FullWrite_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
+\func{write}.
\begin{figure}[!htb]
- \footnotesize
- \begin{lstlisting}{}
-void ServEcho(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}
+ \footnotesize \centering
+ \begin{minipage}[c]{15.6cm}
+ \includecodesample{listati/ServEcho.c}
+ \end{minipage}
+ \normalsize
\caption{Codice della prima versione della funzione \code{ServEcho} per la
gestione del servizio \texttt{echo}.}
\label{fig:TCPsimpl_server_elem_sub}
Il codice del client è riportato in \figref{fig:TCPsimpl_client_elem}, 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
+\texttt{daytime} (vedi \secref{sec:TCP_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 */
- ClientEcho(stdin, sock_fd);
- /* normal exit */
- return 0;
-}
- \end{lstlisting}
+ \footnotesize \centering
+ \begin{minipage}[c]{15.6 cm}
+ \includecodesample{listati/EchoServerWrong.c}
+ \end{minipage}
+ \normalsize
\caption{Codice della prima versione del client \texttt{echo}.}
\label{fig:TCPsimpl_client_elem}
\end{figure}
La funzione \code{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
+in \secref{sec:TCP_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, al ritrno fiììdi \func{connect} è ritornata, la
-funzione \code{ClientEcho}, riportata in
-\figref{fig:TCPsimpl_client_echo_sub}, si preoccupa di gestire la
-comunicazione, leggendo una riga alla volta dallo \file{stdin}, scrivendola
-sul socket e ristampando su \file{stdout} quanto ricevuto in risposta dal
-server.
+Completata la connessione, al ritorno di \func{connect}, la funzione
+\code{ClientEcho}, riportata in \figref{fig:TCPsimpl_client_echo_sub}, si
+preoccupa di gestire la comunicazione, leggendo una riga alla volta dallo
+\file{stdin}, scrivendola sul socket e ristampando su \file{stdout} quanto
+ricevuto in risposta dal server.
\begin{figure}[!htb]
- \footnotesize
- \begin{lstlisting}{}
-void ClientEcho(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}
+ \footnotesize \centering
+ \begin{minipage}[c]{15.6cm}
+ \includecodesample{listati/ClientEcho.c}
+ \end{minipage}
+ \normalsize
\caption{Codice della prima versione della funzione \texttt{ClientEcho} per
la gestione del servizio \texttt{echo}.}
\label{fig:TCPsimpl_client_echo_sub}
(linee \texttt{\small 5--10}), i dati da inviare sulla connessione vengono
presi dallo \file{stdin} usando la funzione \func{fgets} che legge una
linea di testo (terminata da un \texttt{CR} e fino al massimo di
-\macro{MAXLINE} caratteri) e la salva sul buffer di invio, la funzione
-\func{SockWrite} (\texttt{\small 3}) scrive detti dati sul socket (gestendo
+\const{MAXLINE} caratteri) e la salva sul buffer di invio, la funzione
+\func{FullWrite} (\texttt{\small 3}) scrive detti dati sul socket (gestendo
l'invio multiplo qualora una singola \func{write} non basti, come spiegato
in \secref{sec:sock_io_behav}).
-I dati che vengono riletti indietro con una \func{SockRead} sul buffer di
+I dati che vengono riletti indietro con una \func{FullRead} sul buffer di
ricezione e viene inserita la terminazione della stringa (\texttt{\small
7--8}) e per poter usare la funzione \func{fputs} per scriverli su
\file{stdout}.
server a cui questo risponderà con un ACK. A questo punto il client verrà a
trovarsi nello stato \texttt{FIN\_WAIT\_2} ed il server nello stato
\texttt{CLOSE\_WAIT} (si riveda quanto spiegato in
- \secref{sec:TCPel_conn_term}).
+ \secref{sec:TCP_conn_term}).
\item quando il server riceve il FIN la \func{read} del processo figlio che
gestisce la connessione ritorna restituendo 0 causando così l'uscita dal
ciclo e il ritorno di \code{ServEcho}, a questo punto il processo figlio
Tutto questo riguarda la connessione, c'è però da tenere conto dell'effetto
del procedimento di chiusura del processo figlio nel server (si veda quanto
esaminato in \secref{sec:proc_termination}). In questo caso avremo l'invio del
-segnale \macro{SIGCHILD} al padre, ma dato che non si è installato un
-manipolatore e che l'azione di default per questo segnale è quella di essere
+segnale \const{SIGCHLD} al padre, ma dato che non si è installato un
+gestore e che l'azione predefinita per questo segnale è quella di essere
ignorato, non avendo predisposto la ricezione dello stato di terminazione,
-otterremo che il processo figlio entrerà nello stato di zombie, come risulterà
+otterremo che il processo figlio entrerà nello stato di zombie\index{zombie}
+(si riveda quanto illustrato in \secref{sec:sig_sigchld}), come risulterà
ripetendo il comando \cmd{ps}:
\begin{verbatim}
2356 pts/0 S 0:00 ./echod
2359 pts/0 Z 0:00 [echod <defunct>]
\end{verbatim}
-Poiché non è possibile lasciare processi zombie che pur inattivi occupano
-spazio nella tabella dei processi e a lungo andare saturerebbero le risorse
-del kernel, occorrerà ricevere opportunamente lo stato di terminazione del
-processo (si veda \secref{sec:proc_wait}), cosa che faremo utilizzando
-\macro{SIGCHLD} secondo quanto illustrato in \secref{sec:sig_sigchld}.
+Poiché non è possibile lasciare processi zombie\index{zombie} che pur inattivi
+occupano spazio nella tabella dei processi e a lungo andare saturerebbero le
+risorse del kernel, occorrerà ricevere opportunamente lo stato di terminazione
+del processo (si veda \secref{sec:proc_wait}), cosa che faremo utilizzando
+\const{SIGCHLD} secondo quanto illustrato in \secref{sec:sig_sigchld}.
La prima modifica al nostro server è pertanto quella di inserire la gestione
-della terminazione dei processi figli attraverso l'uso di un manipolatore.
+della terminazione dei processi figli attraverso l'uso di un gestore.
Per questo useremo la funzione \code{Signal}, illustrata in
-\figref{fig:sig_Signal_code}, per installare il semplice manipolatore che
+\figref{fig:sig_Signal_code}, per installare il semplice gestore che
riceve i segnali dei processi figli terminati già visto in
\figref{fig:sig_sigchld_handl}; aggiungendo il seguente codice:
-\begin{lstlisting}{}
- ...
- /* install SIGCHLD handler */
- Signal(SIGCHLD, sigchld_hand); /* establish handler */
- /* create socket */
- ...
-\end{lstlisting}
-
+\includecodesnip{listati/sigchildhand.c}
\noindent
all'esempio illustrato in \figref{fig:TCPsimpl_serv_code}, e linkando il tutto
alla funzione \code{sigchld\_hand}, si risolverà completamente il problema
-degli zombie.
+degli zombie\index{zombie}.