Tips :
When the client closed initially , how can we guarantee that the information that the client send can be totally received by the server.This passage mainly discuss this problem with close() and shutdown(). With the comparison , we learned that close() function is to close full-duplex communication from both sides. This is not what we want. But shutdown () can solve this problem wonderfully. When we use shutdown function , we can decide we want to close the write or read of the client or both of them which is more flexible than close() and that the reason why we use shutdown finally. We only made some changes for the client.
Server:
/************************************************************************
> filename: echoserv2.c
> Author: ma6174
> Mail: ma6174@163.com
> Created Time: Thu 29 Oct 16:38:57 2015
************************************************************************/
#include <sys/select.h>
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#define ERR_EXIT(m) \
do \
{\
perror(m) ;\
exit(EXIT_FAILURE) ;\
}while(0)
//ssize_t the signed integer , size_t the unsigned integer
ssize_t readn ( int fd , void *buf , size_t count ) //encapsulate like read function
{
size_t nleft = count ;
ssize_t nread ;
char *bufp ;
bufp = ( char *) buf ;
while ( nleft > 0 )
{
//nread = read ( fd , bufp , nleft ) ;
//printf ("nread = %d\n" , nread ) ;
if ( (nread = read ( fd , bufp , nleft )) < 0 )
{
if ( EINTR == errno ) // interrupted by the signal , we don't think this occasion is a mistake
{
continue ;
}
return -1 ; //failure
}
else if ( 0 == nread ) // peer close
{
return count - nleft ;
}
bufp += nread ;
nleft -= nread ;
}
return count ;
}
ssize_t writen ( int fd ,const void *buf , size_t count )
{
size_t nleft = count ;
ssize_t nwritten ;
char * bufp ;
bufp = (char*) buf ;
while ( nleft > 0 )
{
if ( ( nwritten = write ( fd , bufp , nleft ) ) < 0 )
{
if ( EINTR == errno )
{
continue ;
}
return -1 ;
}
else if ( 0 == nwritten ) // like nothing happened to write function
{
continue ;
}
bufp += nwritten ;
nleft -= nwritten ;
}
return count ;
}
ssize_t recv_peek ( int sockfd , void *buf , size_t len )
{
while ( 1 )
{
int ret = recv ( sockfd , buf , len , MSG_PEEK ) ;
//only let buf read the data from sockfd but not remove the data from that sockfd(socket)
//in comparison with read function it receive the data and remove the data in the socket
if ( -1 == ret && EINTR == errno )
{
continue ; //interrupted by signal
}
return ret ;
}
}
ssize_t readline ( int sockfd , void *buf , size_t maxline )
{
int ret ;
int nread ;
char *bufp ;
bufp = (char*) buf ;
int nleft ;
nleft = maxline ;
while (1 )
{
ret = recv_peek ( sockfd, bufp , nleft ) ;
if ( ret < 0 ) //failure
{
return ret ;
}
else if ( 0 == ret ) // peer closed
{
return ret ;
}
nread = ret ;
int i ;
for ( i = 0 ; i < nread ; i ++ )
{
if ( '\n' == bufp[i])
{
ret = readn (sockfd,bufp , i+1) ;
if ( ret != i + 1 )
{
exit(EXIT_FAILURE) ;
}
return ret ;
}
}
if ( nread > nleft ) // the data that we read(nread) can't be more than the real data in the socket
{
exit ( EXIT_FAILURE ) ;
}
nleft -= nread ;
ret = readn ( sockfd , bufp , nread ) ; // read n characters from the socket ( don't contain '\n')
if ( ret != nread )
{
exit(EXIT_FAILURE ) ;
}
bufp += nread ;
}
return -1 ;
}
void echo_serv ( int conn )
{
char recvbuf[1024] ;
while(1)
{
memset ( recvbuf , 0 , sizeof(recvbuf) ) ;
int ret = readline ( conn , recvbuf , sizeof(recvbuf) ) ;
if ( -1 == ret )
{
ERR_EXIT("readline") ;
}
if ( 0 == ret )
{
// ERR_EXIT ("client close\n") ;
printf("client close\n") ;
break ;
}
fputs ( recvbuf , stdout ) ;
writen ( conn , recvbuf , strlen(recvbuf) ) ;
}
}
void handle_sigchld ( int sig )
{
// wait ( NULL ) ; //call wait function to catch the process of the son
/*ther will be a problem to call wait if there many sons' processes , wait function can
not wait all the sons' processes to quit. Another saying is that wait funciton can not
guarantee that there is clear of zombie processes when many sons's processes exist.
wait function only wait for the first son process to quit ,then wait will quit immediately.
In order to improve this performance , we use waitpid() to change.
*/
// static i = 0 ;
// printf ("i = %d\n",++i ) ;
while ( waitpid(-1,NULL,WNOHANG) > 0 )
;
/*-1 means all of the son process will quit,WNOHANG means wait no hang
no hang means when there is no son process , waitpid will return -1 :*/
}
void handle_sigpipe( int sig )
{
printf ("recv a sig = %d\n" , sig ) ;
}
int
main ()
{
signal ( SIGPIPE , handle_sigpipe ) ; // catch the SIGPIPE signal when the peer client close
signal ( SIGCHLD , handle_sigchld ) ;
// signal ( SIGCHLD , SIG_IGN ) ;
/* neglect the signal of SIGCHLD to avoid zombie process which happened to
the server when the client quit , but the server still keeps listening.*/
int listenfd ;
if (( listenfd = socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP )) < 0 )
{
ERR_EXIT("socket") ;
}
struct sockaddr_in servaddr ;
memset ( & servaddr , 0 , sizeof(servaddr ) ) ;
servaddr.sin_family = AF_INET ;
servaddr.sin_port = htons (5188) ;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY) ; //INTADDR_ANY == 0
//other two means to change the host address to network address
//servaddr.sin_addr.s_addr = inet_addr("127.0.0.1") ;
//inet_aton("127.0.0.1" , &servaddr.sin_addr) ;
//set out the bind has already used by SO_REUSEADDR
int on = 1 ;
int opt ;
if (( opt = setsockopt (listenfd ,SOL_SOCKET , SO_REUSEADDR, &on , sizeof(on) )) < 0 )
{
ERR_EXIT("setsocketopt") ;
}
if ( bind ( listenfd , (struct sockaddr * ) &servaddr , sizeof(servaddr )) < 0 )
{
ERR_EXIT("bind") ;
}
if ( listen ( listenfd , SOMAXCONN) < 0 )
{
ERR_EXIT("listen") ;
}
//struct sockaddr_in peeraddr ;
//socklen_t peerlen = sizeof(peeraddr) ;
//int conn ;
/*
int conn ;
while ( 1 )
{
if ( (conn = accept ( listenfd , ( struct sockaddr * )&peeraddr , &peerlen )) < 0 )
{
ERR_EXIT("accept") ;
}
// print the client's address and ip '
printf ("ip = %s port = %d\n", inet_ntoa(peeraddr.sin_addr) ,ntohs(peeraddr.sin_port) ) ;
int pid ;
pid = fork() ; // creat the child process to set out the multi-connection from clients
if ( -1 == pid )
{
ERR_EXIT ("fork") ;
}
if ( 0 == pid )
{
close (listenfd) ; // the son's pid didn't need to process the listen , so we close listenfd ;
echo_serv (conn) ;
exit(EXIT_SUCCESS) ;
}
else
{
close (conn) ; // the father's pid didn't need to process the acceept ,so we close conn '
}
}
*/
int client[FD_SETSIZE] ; // fd_setsize is the max number that set can manage
int i ;
int maxIndex ;
maxIndex = 0 ;
for ( i = 0 ; i < FD_SETSIZE ; i ++ )
{
client[i] = -1 ; //-1 means free
}
int nready ;
int maxfd ;
maxfd = listenfd + 1 ;
fd_set rset ;
fd_set allset ;
FD_ZERO(&rset) ;
FD_ZERO(&allset) ;
FD_SET ( listenfd , &allset ) ;
struct sockaddr_in peeraddr ;
socklen_t peerlen = sizeof(peeraddr) ;
int conn ;
while ( 1 )
{
rset = allset ;
nready = select ( maxfd + 1 , &rset , NULL , NULL , NULL ) ;
if ( -1 == nready )
{
if ( EINTR == errno ) // interrupted by signal .
{
continue ;
}
ERR_EXIT("select") ;
}
if ( 0 == nready ) // if timeout ,continue which is impossible , because the last parameter is NULL
{
continue ;
}
if ( FD_ISSET ( listenfd , &rset ) ) // if listening socket creates an event .
{
socklen_t peerlen = sizeof(peeraddr) ;
conn = accept ( listenfd , (struct sockaddr*) &peeraddr , &peerlen ) ;
if ( -1 == conn )
{
ERR_EXIT("accept") ;
}
for ( i = 0 ; i < FD_SETSIZE ; i ++ )
{
if ( client[i] < 0 )
{
client[i] = conn ;
if ( i > maxIndex )
{
maxIndex = i ;
}
break ;
}
}
if ( FD_SETSIZE == i )
{
fprintf ( stderr, "too many clients\n") ;
exit (EXIT_FAILURE) ;
}
printf ("ip = %s port = %d\n" , inet_ntoa(peeraddr.sin_addr) , ntohs(peeraddr.sin_port) ) ;
FD_SET ( conn , &allset ) ;
{
if ( conn > maxfd ) // pay attention to that the new descriptor may larger than the maxfd
// at that occasion we need update maxfd immediately.
{
maxfd = conn ;
}
}
if ( --nready <= 0 ) // all the monitoring event were cleared out.
{
continue ;
}
}
for ( i = 0 ; i <= maxIndex ; i ++ )
{
conn = client[i] ;
if ( -1 == conn )
{
continue ;
}
if ( FD_ISSET ( conn , &rset ) ) // connected socket creats a readable event.
{
char recvbuf[1024] = {0} ;
int ret = readline ( conn , recvbuf , sizeof(recvbuf) ) ;
if ( -1 == ret )
{
ERR_EXIT("readlien") ;
}
if ( 0 == ret )
{
printf ("client close\n") ;
FD_CLR (conn , &allset ) ;
client[i] = -1 ;
close (conn ) ; // this is a bug which we missed before
}
fputs ( recvbuf , stdout ) ;
sleep(4) ;
writen ( conn , recvbuf , strlen(recvbuf) ) ;
if ( -- nready <= 0 )
{
break ;
}
}
}
}
return 0 ;
}
Client(modified) :
/************************************************************************
> filename : echocli.c
> Author: ma6174
> Mail: ma6174@163.com
> Created Time: Thu 29 Oct 16:38:57 2015
************************************************************************/
#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#define ERR_EXIT(m) \
do \
{\
perror(m) ;\
exit(EXIT_FAILURE) ;\
}while(0)
//ssize_t signed integer size_t unsigned integer
ssize_t readn ( int fd , void *buf , size_t count ) // encapsulate readn as the function of read
{
size_t nleft = count ;
ssize_t nread ;
char *bufp ;
bufp = ( char *) buf ;
// printf ("nleft = %d\n" , nleft ) ;
while ( nleft > 0 )
{
// printf ("nleft = %d\n" , nleft ) ;
nread = read ( fd , bufp , nleft ) ;
// printf ("nread = %d\n" , nread ) ;
if( nread < 0 )
{
if ( errno == EINTR ) // interrupted by signal , we don't think this occasion is not right
{
continue ;
}
return -1 ;
}
else if ( 0 == nread ) //peer closed
{
return count - nleft ;
//break ;
}
// printf ("nreaad = %d\n" , nread ) ;
bufp += nread ;
nleft -= nread ;
}
return count ;
}
ssize_t writen ( int fd , const void *buf ,size_t count )
{
size_t nleft = count ;
ssize_t nwritten ;
char *bufp ;
bufp = ( char * ) buf ;
while ( nleft > 0 )
{
if ( ( nwritten = write ( fd , bufp , nleft ) ) < 0 )
{
if ( errno == EINTR ) // just like the occasion in writen
{
continue ;
}
return -1 ;
}
else if ( 0 == nwritten ) // if 0 == nread , like nothing happened to write ...
{
continue ;
}
bufp += nwritten ;
nleft -= nwritten ;
}
return count ;
}
ssize_t recv_peek ( int sockfd , void *buf , size_t len )
{
while ( 1 )
{
int ret = recv ( sockfd , buf , len , MSG_PEEK ) ;
//only let buf read the data from sockfd but not remove the data from that sockfd(socket)
//in comparison with read function it receive the data and remove the data in the socket
if ( -1 == ret && EINTR == errno )
{
continue ; //interrupted by signal
}
return ret ;
}
}
ssize_t readline ( int sockfd , void *buf , size_t maxline )
{
int ret ;
int nread ;
char *bufp ;
bufp = (char*) buf ;
int nleft ;
nleft = maxline ;
while (1 )
{
ret = recv_peek ( sockfd, bufp , nleft ) ;
if ( ret < 0 ) //failure
{
return ret ;
}
else if ( 0 == ret ) // peer closed
{
return ret ;
}
nread = ret ;
int i ;
for ( i = 0 ; i < nread ; i ++ )
{
if ( '\n' == bufp[i])
{
ret = readn (sockfd,bufp , i+1) ;
if ( ret != i + 1 )
{
exit(EXIT_FAILURE) ;
}
return ret ;
}
}
if ( nread > nleft ) // the data that we read(nread) can't be more than the real data in the socket
{
exit ( EXIT_FAILURE ) ;
}
nleft -= nread ;
ret = readn ( sockfd , bufp , nread ) ; // read n characters from the socket ( don't contain '\n')
if ( ret != nread )
{
exit(EXIT_FAILURE ) ;
}
bufp += nread ;
}
return -1 ;
}
void echo_cli ( int sock )
{
/*
char sendbuf[1024]={0} ;
char recvbuf[1024]={0} ;
memset ( sendbuf , 0 , sizeof(sendbuf) ) ;
memset ( recvbuf , 0 , sizeof(recvbuf) ) ;
int n ;
while( fgets ( sendbuf , sizeof(sendbuf) , stdin ) != NULL )
{
//fgets get the data with '\n' in default
// writen ( sock , sendbuf ,strlen(sendbuf) ) ; // the former 4 bytes and the real data(n)
// write ( sock , sendbuf , 1 ) ;
// write ( sock , sendbuf + 1 , strlen(sendbuf) -1 ) ; i
writen ( sock , sendbuf, strlen(sendbuf) ) ;
int ret = readline ( sock , recvbuf , sizeof(recvbuf) ) ;
if ( -1 == ret )
{
ERR_EXIT ("readline") ;
}
else if ( 0 == ret ) //may interrupted by signal
{
printf ("server________ close\n") ;
break ;
}
fputs ( recvbuf , stdout ) ;
memset ( sendbuf , 0 , sizeof(sendbuf) ) ;
memset ( recvbuf , 0 , sizeof(recvbuf) ) ;
}
close(sock)
*/
fd_set rset ;
FD_ZERO ( &rset ) ;
int nready ;
int maxfd ;
int fd_stdin = fileno(stdin) ; // STD_FILENO == 0 but stdin may be redirected. so we don't use STD_FILENO
if ( fd_stdin > sock )
{
maxfd = fd_stdin ;
}
else
{
maxfd = sock ;
}
char sendbuf[1024] = {0} ;
char recvbuf[1024] = {0} ;
memset ( recvbuf , 0 , sizeof(recvbuf) ) ;
int stdineof = 0 ;
while(1)
{
if ( 0 == stdineof ) //when the STDIN is not end , add rset to the set
{
FD_SET ( fd_stdin , &rset ) ;
}
FD_SET ( sock , &rset ) ;
nready = select ( maxfd + 1 , &rset , NULL , NULL , NULL ) ;
if ( -1 == nready )
{
ERR_EXIT("select") ;
}
if ( 0 == nready )
{
continue ;
}
if ( FD_ISSET ( sock , &rset ) )
{
int ret = readline ( sock , recvbuf , sizeof ( recvbuf ) ) ;
if ( -1 == ret )
{
ERR_EXIT("redline") ;
}
else if ( 0 == ret )
{
printf ("server_____close\n") ;
break ;
}
fputs ( recvbuf , stdout ) ;
memset ( recvbuf , 0 , sizeof(recvbuf) ) ;
}
if ( FD_ISSET ( fd_stdin , &rset ) )
{
if (fgets (sendbuf, sizeof(sendbuf) , stdin ) == NULL )
{
/*
close(sock) ;
sleep ( 5 ) ;
exit ( EXIT_FAILURE ) ;
//break ;
*/
stdineof = 1 ;
shutdown ( sock , SHUT_WR ) ;
// close with shutdonw to avoid that the client can read the information even client closed initially
}
else
{
writen ( sock , sendbuf , strlen(sendbuf) ) ;
memset ( sendbuf , 0 , sizeof(sendbuf) ) ;
}
}
}
close(sock) ;
}
void handle_sigpipe ( int sig )
{
printf ("recv a sig = %d\n" , sig ) ;
}
int
main ()
{
//signal ( SIGPIPE , handle_sigpipe ) ;i
signal ( SIGPIPE , SIG_IGN ) ;
int sock ;
if (( sock= socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP )) < 0 )
{
ERR_EXIT("socket") ;
}
struct sockaddr_in servaddr ;
memset ( & servaddr , 0 , sizeof(servaddr) ) ;
servaddr.sin_family = AF_INET ;
servaddr.sin_port = htons (5188) ;
//servaddr.sin_addr.s_addr = htonl(INADDR_ANY) ;
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1") ;
//inet_aton("127.0.0.1" , &servaddr.sin_addr) ;
if ( connect ( sock , ( struct sockaddr * )& servaddr, sizeof(servaddr )) < 0 )
{
ERR_EXIT("connet") ;
}
echo_cli(sock) ;
return 0 ;
}