X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=simpltcp.tex;h=7d0291f9b66a353f25359857a0bdfe6d46e5a401;hp=d723e308d32ff3c36296e66c91a7ec4ddd5aa4ba;hb=3c1cadac6a684ce18f4e1a6e23d752ee5ba94c8f;hpb=25de957ddf731370bec1eb74b13cf35aa7886d1b diff --git a/simpltcp.tex b/simpltcp.tex index d723e30..7d0291f 100644 --- a/simpltcp.tex +++ b/simpltcp.tex @@ -58,64 +58,14 @@ corpo principale, costituito dalla funzione \code{main}. Questa 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 \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} @@ -123,8 +73,8 @@ int main(int argc, char *argv[]) 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 @@ -136,25 +86,17 @@ Il codice della funzione \code{ServEcho} 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 +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} @@ -171,78 +113,38 @@ del processo figlio. 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} @@ -254,11 +156,11 @@ La funzione utilizza due buffer per gestire i dati inviati e letti sul socket 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 \const{MAXLINE} caratteri) e la salva sul buffer di invio, la funzione -\func{SockWrite} (\texttt{\small 3}) scrive detti dati sul socket (gestendo +\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}. @@ -387,7 +289,7 @@ quando affronteremo il comportamento in caso di conclusioni anomale: 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 @@ -408,7 +310,7 @@ Tutto questo riguarda la connessione, c' 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 \const{SIGCHLD} al padre, ma dato che non si è installato un -manipolatore e che l'azione predefinita per questo segnale è quella di essere +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\index{zombie} (si riveda quanto illustrato in \secref{sec:sig_sigchld}), come risulterà @@ -425,19 +327,12 @@ 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