From 730b0bb045c1794c4f4675605dc106b756e82d69 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sun, 2 Feb 2003 20:35:34 +0000 Subject: [PATCH] Iniziato a lavorare sulla Shared Memory POSIX. Risistemate un po' di cose e scritte routine e programma di prova. --- ipc.tex | 46 ++++++-- simpltcp.tex | 12 +- socket.tex | 20 ++-- sources/ElemEchoTCPClient.c | 6 +- sources/ElemEchoTCPServer.c | 4 +- sources/{SockRead.c => FullRead.c} | 8 +- sources/{SockWrite.c => FullWrite.c} | 8 +- sources/Gapil.h | 27 ++++- sources/Makefile | 12 +- sources/SharedMem.c | 163 ++++++++++++++++++++++----- sources/SimpleEchoTCPClient.c | 6 +- sources/SimpleEchoTCPServer.c | 4 +- 12 files changed, 237 insertions(+), 79 deletions(-) rename sources/{SockRead.c => FullRead.c} (91%) rename sources/{SockWrite.c => FullWrite.c} (90%) diff --git a/ipc.tex b/ipc.tex index 8428b47..78d612f 100644 --- a/ipc.tex +++ b/ipc.tex @@ -3779,15 +3779,39 @@ meccanismi di comunicazione, che vanno sotto il nome di POSIX IPC, definendo una interfaccia completamente nuova, che tratteremo in questa sezione. - \subsection{Considerazioni generali} \label{sec:ipc_posix_generic} -Il Linux non tutti gli oggetti del POSIX IPC sono supportati nel kernel -ufficiale; solo la memoria condivisa è presente, ma solo a partire dal kernel -2.4.x, per gli altri oggetti esistono patch e librerie non ufficiali. -Nonostante questo è importante esaminare questa interfaccia per la sua netta -superiorità nei confronti di quella del \textit{SysV IPC}. +In Linux non tutti gli oggetti del POSIX IPC sono pienamente supportati nel +kernel ufficiale; solo la memoria condivisa è presente con l'interfaccia +completa, ma solo a partire dal kernel 2.4.x, i semafori sono forniti dalle +\acr{glibc} nella sezione che implementa i thread POSIX, e sono utilizzabili +solo all'interno dei thread generati dallo stesso processo,\footnote{sono cioè + oggetti che non possono, al contrario dei semafori del SysV IPC, essere + utilizzati per sincronizzare processi diversi.} le code di messaggi non +hanno alcun tipo di supporto ufficiale. Esistono tuttavia dei patch e delle +librerie aggiuntive che supportano alcune di queste interfacce, anche se +sperimentali e di uso limitato. + +La caratteristica fondamentale dell'interfaccia POSIX è l'abbandono dell'uso +degli identificatori e delle chiavi visti nel SysV IPC, per passare ai +\textit{Posix IPC names}\index{Posix IPC names}, che sono sostanzialmente +equivalenti ai nomi dei file. Tutte le funzioni che creano un oggetto di IPC +Posix prendono come primo argomento una stringa che indica uno di questi nomi; +lo standard è molto generico riguardo l'implementazione, ed i nomi stessi +possono avere o meno una corrispondenza sul filesystem. + + + le caratteristiche di questi nomi, +per i quali richiede che: +\begin{itemize} +\item debbano essere conformi alle regole che caratterizzano i + \textit{pathname}, in particolare non essere più lunghi di \const{PATH\_MAX} + byte e terminati da un carattere nullo. +\item se il nome inizia per una \texttt{/} +\end{itemize} + + \subsection{Code di messaggi} @@ -3797,11 +3821,11 @@ Le code di messaggi non sono ancora supportate nel kernel ufficiale;\footnote{esiste però una proposta di implementazione di Krzysztof Benedyczak, a partire dal kernel 2.5.50.} inoltre esse possono essere implementate, usando la memoria condivisa ed i mutex, con funzioni di -libreria. In generale, come le corrispettive del SysV IPC, sono poco usate, +libreria. In generale, come le corrispettive del SysV IPC, sono poco usate, dato che i socket\index{socket}, nei casi in cui sono sufficienti, sono più comodi, e negli altri casi la comunicazione può essere gestita direttamente -con mutex e memoria condivisa. Per questo ci limiteremo ad una descrizione -essenziale. +con mutex e memoria condivisa. Per questo, in assenza di una implementazione +uffiale, ne tralasciamo la descrizione. @@ -3812,7 +3836,9 @@ Dei semafori POSIX esistono sostanzialmente due implementazioni; una livello di libreria ed è fornita dalla libreria dei thread; questa però li implementa solo a livello di thread e non di processi. Esiste un'altra versione, realizzata da Konstantin Knizhnik, che reimplementa l'interfaccia -POSIX usando i semafori di SysV IPC. +POSIX usando i semafori di SysV IPC. + + \subsection{Memoria condivisa} diff --git a/simpltcp.tex b/simpltcp.tex index 4d46b1a..b4b9931 100644 --- a/simpltcp.tex +++ b/simpltcp.tex @@ -136,7 +136,7 @@ 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}. @@ -150,7 +150,7 @@ void ServEcho(int sockfd) { /* main loop, reading 0 char means client close connection */ while ( (nread = read(sockfd, buffer, MAXLINE)) != 0) { - nwrite = SockWrite(sockfd, buffer, nread); + nwrite = FullWrite(sockfd, buffer, nread); } return; } @@ -234,8 +234,8 @@ 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)); + FullWrite(socket, sendbuff, strlen(sendbuff)); + nread = FullRead(socket, recvbuff, strlen(sendbuff)); recvbuff[nread] = 0; fputs(recvbuff, stdout); } @@ -253,11 +253,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}. diff --git a/socket.tex b/socket.tex index fa70b6d..2e34e78 100644 --- a/socket.tex +++ b/socket.tex @@ -817,7 +817,7 @@ riveda quanto detto in \secref{sec:ipc_pipes}). \begin{lstlisting}{} #include -ssize_t SockRead(int fd, void *buf, size_t count) +ssize_t FullRead(int fd, void *buf, size_t count) { size_t nleft; ssize_t nread; @@ -839,17 +839,17 @@ ssize_t SockRead(int fd, void *buf, size_t count) return (count - nleft); } \end{lstlisting} - \caption{Funzione \func{SockRead}, legge \var{count} byte da un socket } - \label{fig:sock_SockRead_code} + \caption{Funzione \func{FullRead}, legge \var{count} byte da un socket } + \label{fig:sock_FullRead_code} \end{figure} Per questo motivo, seguendo l'esempio di W. R. Stevens in \cite{UNP1}, si sono -definite due funzioni \func{SockRead} e \func{SockWrite} che eseguono la +definite due funzioni \func{FullRead} e \func{FullWrite} 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 byte -specificato; il sorgente è riportato in \figref{fig:sock_SockRead_code} e -\figref{fig:sock_SockWrite_code} ed è disponibile fra i sorgenti allegati alla -guida nei files \file{SockRead.c} e \file{SockWrite.c}. +specificato; il sorgente è riportato in \figref{fig:sock_FullRead_code} e +\figref{fig:sock_FullWrite_code} ed è disponibile fra i sorgenti allegati alla +guida nei files \file{FullRead.c} e \file{FullWrite.c}. \begin{figure}[htb] \centering @@ -857,7 +857,7 @@ guida nei files \file{SockRead.c} e \file{SockWrite.c}. \begin{lstlisting}{} #include -ssize_t SockWrite(int fd, const void *buf, size_t count) +ssize_t FullWrite(int fd, const void *buf, size_t count) { size_t nleft; ssize_t nwritten; @@ -877,8 +877,8 @@ ssize_t SockWrite(int fd, const void *buf, size_t count) return (count); } \end{lstlisting} - \caption{Funzione \func{SockWrite}, scrive \var{count} byte su un socket.} - \label{fig:sock_SockWrite_code} + \caption{Funzione \func{FullWrite}, scrive \var{count} byte su un socket.} + \label{fig:sock_FullWrite_code} \end{figure} Come si può notare le funzioni ripetono la lettura/scrittura in un ciclo fino diff --git a/sources/ElemEchoTCPClient.c b/sources/ElemEchoTCPClient.c index ac1d991..3529cd7 100644 --- a/sources/ElemEchoTCPClient.c +++ b/sources/ElemEchoTCPClient.c @@ -26,7 +26,7 @@ * * Usage: echo -h give all info's * - * $Id: ElemEchoTCPClient.c,v 1.4 2002/12/03 11:06:05 piccardi Exp $ + * $Id: ElemEchoTCPClient.c,v 1.5 2003/02/02 20:35:33 piccardi Exp $ * ****************************************************************/ /* @@ -120,8 +120,8 @@ 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)); + FullWrite(socket, sendbuff, strlen(sendbuff)); + nread = FullRead(socket, recvbuff, strlen(sendbuff)); recvbuff[nread] = 0; fputs(recvbuff, stdout); } diff --git a/sources/ElemEchoTCPServer.c b/sources/ElemEchoTCPServer.c index f02ac5c..1793d3f 100644 --- a/sources/ElemEchoTCPServer.c +++ b/sources/ElemEchoTCPServer.c @@ -26,7 +26,7 @@ * * Usage: echod -h give all info * - * $Id: ElemEchoTCPServer.c,v 1.4 2002/12/03 11:06:05 piccardi Exp $ + * $Id: ElemEchoTCPServer.c,v 1.5 2003/02/02 20:35:33 piccardi Exp $ * ****************************************************************/ /* @@ -148,7 +148,7 @@ void ServEcho(int sockfd) { /* main loop, reading 0 char means client close connection */ while ( (nread = read(sockfd, buffer, MAXLINE)) != 0) { printf("Letti %d bytes, %s ", nread, buffer); - nwrite = SockWrite(sockfd, buffer, nread); + nwrite = FullWrite(sockfd, buffer, nread); } return; } diff --git a/sources/SockRead.c b/sources/FullRead.c similarity index 91% rename from sources/SockRead.c rename to sources/FullRead.c index a018f33..b7be61f 100644 --- a/sources/SockRead.c +++ b/sources/FullRead.c @@ -1,4 +1,4 @@ -/* SockRead.c +/* FullRead.c * * Copyright (C) 2001 Simone Piccardi * @@ -18,19 +18,19 @@ */ /**************************************************************** * - * Routine SockRead + * Routine FullRead * Routine to read an exact number of bytes from a socket * * Author: Simone Piccardi * Jun. 2001 * - * $Id: SockRead.c,v 1.3 2001/09/09 22:45:34 piccardi Exp $ + * $Id: FullRead.c,v 1.1 2003/02/02 20:35:33 piccardi Exp $ * ****************************************************************/ #include #include -ssize_t SockRead(int fd, void *buf, size_t count) +ssize_t FullRead(int fd, void *buf, size_t count) { size_t nleft; ssize_t nread; diff --git a/sources/SockWrite.c b/sources/FullWrite.c similarity index 90% rename from sources/SockWrite.c rename to sources/FullWrite.c index c0a41d9..dbddf1a 100644 --- a/sources/SockWrite.c +++ b/sources/FullWrite.c @@ -1,4 +1,4 @@ -/* SockWrite.c +/* FullWrite.c * * Copyright (C) 2001 Simone Piccardi * @@ -18,19 +18,19 @@ */ /**************************************************************** * - * Routine SockWrite + * Routine FullWrite * Routine to write an exact number of bytes into a socket * * Author: Simone Piccardi * Jun. 2001 * - * $Id: SockWrite.c,v 1.3 2001/09/09 22:45:34 piccardi Exp $ + * $Id: FullWrite.c,v 1.1 2003/02/02 20:35:33 piccardi Exp $ * ****************************************************************/ #include #include -ssize_t SockWrite(int fd, const void *buf, size_t count) +ssize_t FullWrite(int fd, const void *buf, size_t count) { size_t nleft; ssize_t nwritten; diff --git a/sources/Gapil.h b/sources/Gapil.h index a8e786f..401871c 100644 --- a/sources/Gapil.h +++ b/sources/Gapil.h @@ -23,7 +23,7 @@ * * Author: S. Piccardi * - * $Id: Gapil.h,v 1.5 2003/01/07 23:00:34 piccardi Exp $ + * $Id: Gapil.h,v 1.6 2003/02/02 20:35:33 piccardi Exp $ * *****************************************************************************/ #include /* IPC semaphore declarations */ @@ -96,12 +96,27 @@ void HandSigCHLD(int sig); /* * Socket service functions */ -/* Function SockRead: to read from a socket. See SockRead.c */ -ssize_t SockRead(int fd, void *buf, size_t count); -/* Function SockWrite: to read from a socket. See SockWrite.c */ -ssize_t SockWrite(int fd, const void *buf, size_t count); +/* Function FullRead: to read from a socket. See FullRead.c */ +ssize_t FullRead(int fd, void *buf, size_t count); +/* Function FullWrite: to read from a socket. See FullWrite.c */ +ssize_t FullWrite(int fd, const void *buf, size_t count); /* * File miscellaneous */ -/* Function DirScan: simple scan for a directory */ +/* Function DirScan: simple scan for a directory. See DirScan.c */ int DirScan(char * dirname, int(*compute)(struct dirent *)); +/* + * Shared memory handling functions. See SharedMem.c + */ +/* Function ShmCreate: create a SysV shared memory */ +void * ShmCreate(key_t ipc_key, int shm_size, int perm, char fill); +/* Function ShmFind: find an existing SysV shared memory */ +void * ShmFind(key_t ipc_key, int shm_size); +/* Function ShmRemove: remove a SysV shared memory */ +int ShmRemove(key_t ipc_key, void * shm_ptr); +/* Function CreateShm: create a POSIX shared memory */ +void * CreateShm(char * shm_name, int shm_size, int perm, char fill); +/* Function FindShm: find an existing POSIX shared memory */ +void * FindShm(char * shm_name, int shm_size); +/* Function RemoveShm: remove a POSIX shared memory */ +int RemoveShm(char * shm_name); diff --git a/sources/Makefile b/sources/Makefile index 2b6240e..32e5d20 100644 --- a/sources/Makefile +++ b/sources/Makefile @@ -8,14 +8,14 @@ CFLAGJ= -L./ -lgapil LIB = libgapil.so -OBJ = SockRead.o SockWrite.o SigHand.o Mutex.o SharedMem.o LockFile.o DirScan.o +OBJ = FullRead.o FullWrite.o SigHand.o Mutex.o SharedMem.o LockFile.o DirScan.o FINAL = forktest errcode echo echod daytimed iterdaytimed daytime testfopen \ testren fortune fortuned mqfortune mqfortuned flock myls dirmonitor \ - readmon ipctestid + readmon ipctestid writeshm readshm $(LIB): $(OBJ) - gcc -shared $^ -o $@ + gcc -shared -lrt $^ -o $@ $(OBJ): Gapil.h @@ -84,6 +84,12 @@ daytime: ElemDaytimeTCPClient.c ipctestid: IPCTestId.c $(CC) $^ -o $@ +writeshm: WriteShm.c + $(CC) $(CFLAGJ) $^ -o $@ + +#readshm: ReadShm.c +# $(CC) $(CFLAGJ) $^ -o $@ + # Macro per la generazione della tarball dei sorgenti package: clean gapil_source.tgz diff --git a/sources/SharedMem.c b/sources/SharedMem.c index 23f9c09..eab28f4 100644 --- a/sources/SharedMem.c +++ b/sources/SharedMem.c @@ -19,11 +19,14 @@ /*************************************************************** * * File SharedMem.c - * Routine for Shared Memory use + * Routines for Shared Memory use. + * + * Define two interfaces, the first one use SysV shared memory, the + * second POSIX shared memory. * * Author: S. Piccardi * - * $Id: SharedMem.c,v 1.2 2002/12/03 22:30:11 piccardi Exp $ + * $Id: SharedMem.c,v 1.3 2003/02/02 20:35:33 piccardi Exp $ * ***************************************************************/ #include /* SysV IPC shared memory declarations */ @@ -32,6 +35,15 @@ #include /* standard I/O functions */ #include #include /* signal handling declarations */ +#include +#include +#include +#include +/* ************************************************************************* + * + * Functions for SysV shared memory + * + * ************************************************************************* */ /* * Function ShmCreate: * Create and attach a SysV shared memory segment to the current process. @@ -46,42 +58,141 @@ * * Input: an IPC key value * the shared memory segment size - * Return: the address of the segment + * the permissions + * the fill value + * Return: the address of the shared memory segment (NULL on error) */ -void * ShmCreate(key_t ipc_key, int shm_size, char fill) +void * ShmCreate(key_t ipc_key, int shm_size, int perm, char fill) { - void * shptr; - int shmid; /* ID of the IPC shared memory segment */ - shmid = shmget(ipc_key,shm_size,IPC_CREAT|0666); /* get shm ID */ - if (shmid < 0) { - return (void *) shmid; + void * shm_ptr; + int shm_id; /* ID of the IPC shared memory segment */ + shm_id = shmget(ipc_key, shm_size, IPC_CREAT|perm); /* get shm ID */ + if (shm_id < 0) { + return NULL; } - shptr = shmat(shmid,0,0); /* take the pointer to it */ - if ( shptr == 0 ){ - perror("cannot attach shared memory"); - exit(1); + shm_ptr = shmat(shm_id, NULL, 0); /* map it into memory */ + if (shm_ptr < 0) { + return NULL; } - memset((void *)shptr, fill, shm_size); /* second counter starts from "0" */ - return shptr; + memset((void *)shm_ptr, fill, shm_size); /* fill segment */ + return shm_ptr; } /* * Function ShmFind: - * Find a shared memory segment + * Find a SysV shared memory segment * Input: an IPC key value * the shared memory segment size - * Return: the address of the segment + * Return: the address of the segment (NULL on error) */ void * ShmFind(key_t ipc_key, int shm_size) { - void * shptr; - int shmid; /* ID of the IPC shared memory segment */ - if ( (shmid=shmget(ipc_key,shm_size,0))<0 ){ /* find shared memory ID */ - perror("cannot find shared memory"); - exit(1); + void * shm_ptr; + int shm_id; /* ID of the SysV shared memory segment */ + shm_id = shmget(ipc_key, shm_size, 0); /* find shared memory ID */ + if (shm_id < 0) { + return NULL; + } + shm_ptr = shmat(shm_id, NULL, 0); /* map it into memory */ + if (shm_ptr < 0) { + return NULL; + } + return shm_ptr; +} +/* + * Function ShmRemove: + * Scheudle removal for a SysV shared memory segment + * Input: an IPC key value + * the shared memory segment size + * Return: 0 on success, -1 on error + */ +int ShmRemove(key_t ipc_key, void * shm_ptr) +{ + int shm_id; /* ID of the SysV shared memory segment */ + /* first detach segment */ + if (shmdt(shm_ptr) < 0) { + return -1; + } + /* schedule segment removal */ + shm_id = shmget(ipc_key, 0, 0); /* find shared memory ID */ + if (shm_id < 0) { + if (errno == EIDRM) return 0; + return -1; } - if ( (shptr=shmat(shmid,0,0)) < 0 ){ /* take the pointer to it */ - perror("cannot attach shared memory"); - exit(1); + if (shmctl(shm_id, IPC_RMID, NULL) < 0) { /* ask for removal */ + if (errno == EIDRM) return 0; + return -1; } - return shptr; + return 0; +} +/* ************************************************************************* + * + * Functions for POSIX shared memory + * + * ************************************************************************* */ +/* + * Function CreateShm: + * Create a POSIX shared memory segment and map it to the current process. + * + * + * Input: a pathname + * the shared memory segment size + * the permissions + * the fill value + * Return: the address of the shared memory segment (NULL on error) + */ +void * CreateShm(char * shm_name, int shm_size, int perm, char fill) +{ + void * shm_ptr; + int fd; + int flag; + /* first open the object, creating it if not existent */ + flag = O_RDWR|O_TRUNC|O_CREAT|O_EXCL; + fd = shm_open(shm_name, flag, perm); /* get object file descriptor */ + if (fd < 0) { + return NULL; + } + /* set the object size */ + if (ftruncate(fd, shm_size)) { + return NULL; + } + /* map it in the process address space */ + shm_ptr = mmap(NULL, shm_size, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0); + if (shm_ptr == MAP_FAILED) { + return NULL; + } + memset((void *) shm_ptr, fill, shm_size); /* fill segment */ + return shm_ptr; +} +/* + * Function FindShm: + * Find a POSIX shared memory segment + * Input: a name + * the shared memory segment size + * Return: the address of the segment (NULL on error) + */ +void * FindShm(char * shm_name, int shm_size) +{ + void * shm_ptr; + int fd; /* ID of the IPC shared memory segment */ + /* find shared memory ID */ + if ((fd = shm_open(shm_name, O_RDWR|O_EXCL, 0)) < 0) { + return NULL; + } + /* take the pointer to it */ + shm_ptr = mmap(NULL, shm_size, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0); + if (shm_ptr == MAP_FAILED) { + return NULL; + } + return shm_ptr; +} +/* + * Function RemoveShm: + * Remove a POSIX shared memory segment + * Input: the object name + * Return: 0 on success, -1 on error + */ +int RemoveShm(char * shm_name) +{ + shm_unlink(shm_name); + return 0; } diff --git a/sources/SimpleEchoTCPClient.c b/sources/SimpleEchoTCPClient.c index f694163..70855ad 100644 --- a/sources/SimpleEchoTCPClient.c +++ b/sources/SimpleEchoTCPClient.c @@ -26,7 +26,7 @@ * * Usage: echo -h give all info's * - * $Id: SimpleEchoTCPClient.c,v 1.5 2002/12/03 11:06:05 piccardi Exp $ + * $Id: SimpleEchoTCPClient.c,v 1.6 2003/02/02 20:35:33 piccardi Exp $ * ****************************************************************/ /* @@ -120,8 +120,8 @@ 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)); + FullWrite(socket, sendbuff, strlen(sendbuff)); + nread = FullRead(socket, recvbuff, strlen(sendbuff)); recvbuff[nread] = 0; fputs(recvbuff, stdout); } diff --git a/sources/SimpleEchoTCPServer.c b/sources/SimpleEchoTCPServer.c index d4ba728..890954d 100644 --- a/sources/SimpleEchoTCPServer.c +++ b/sources/SimpleEchoTCPServer.c @@ -26,7 +26,7 @@ * * Usage: echod * - * $Id: SimpleEchoTCPServer.c,v 1.5 2002/12/03 11:06:05 piccardi Exp $ + * $Id: SimpleEchoTCPServer.c,v 1.6 2003/02/02 20:35:34 piccardi Exp $ * ****************************************************************/ /* @@ -147,7 +147,7 @@ void SockEcho(int sockfd) { /* main loop, reading 0 char means client close connection */ while ( (nread = read(sockfd, buffer, MAXLINE)) != 0) { printf("Letti %d bytes, %s ", nread, buffer); - nwrite = SockWrite(sockfd, buffer, nread); + nwrite = FullWrite(sockfd, buffer, nread); } return; } -- 2.30.2