When we use select function to realise concurrent server , the times of concurrent connection is limited by two sides.
(1) One is that the max count of a process can open is limited , but we can fix that by adjusting kernel parameter which is simple.
(2) The other is that when we use select method we were limited by the volume set of fd_set (FD_SETSIZE) , if we want to break this limitation , we need to recompile kernel which make our work really complicated.
In order to solve this problem , we poll function to realise multi-concurrent connection instead of select.
Client :
/************************************************************************
>file name : echocli.c
> Author: ma6174
> Mail: ma6174@163.com
> Created Time: Thu 29 Oct 16:38:57 2015
************************************************************************/
/*
When we test the max count of conncurrent connection , we need to adjust the kernal
parameter with the client's and server's side.
root#: ulimit -n 2048 ( client )
root#: ulimit -n 2048 ( server )
*/
#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define ERR_EXIT(m) \
do \
{\
perror(m) ;\
exit(EXIT_FAILURE) ;\
}while(0)
int
main ()
{
int n ;
n = 0 ;
while(1)
{
int sock ;
if (( sock= socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP )) < 0 )
{
sleep(4) ;
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") ;
}
struct sockaddr_in localaddr ;
socklen_t addrlen = sizeof(localaddr) ;
if ( getsockname ( sock , ( struct sockaddr *) &localaddr , &addrlen ) < 0 )
{
ERR_EXIT ("getsockname") ;
}
printf("n = %d\n" , ++n ) ;
printf ("local_ip = %s local_port = %d\n" , inet_ntoa (localaddr.sin_addr ) , ntohs(localaddr.sin_port) ) ;
// close ( sock ) ;
}
return 0 ;
}
Server :
/************************************************************************
> filename: echoserv2.c
> Author: ma6174
> Mail: ma6174@163.com
> Created Time: Thu 29 Oct 16:38:57 2015
************************************************************************/
#include <poll.h>
#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 ;
//printf("sockfd = %d buf =%s \n" , sockfd , bufp ) ;
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 = PF_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 pollfd client[2048] ; // poll
int i ;
for ( i = 0 ; i < 2048 ; i ++ )
{
client[i].fd = -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 count ; //calculate the times of connection.
count = 0 ;
int conn ;
client[0].events = POLLIN ; // POLL_IN means writeable event ;
client[0].fd = listenfd ;
int maxIndex ; // the max position which is not free
maxIndex = 0 ;
while(1)
{
//rset = allset ;
nready = poll ( client , maxIndex + 1 , 1 ) ; //-1 means wait for all the time.
//printf ("nread = %d\n" , nready ) ;
//nready = select ( maxfd + 1 , &rset , NULL , NULL , NULL ) ;
if ( -1 == nready )
{
if ( EINTR == errno ) // interrupted by signal .
{
continue ;
}
ERR_EXIT("select") ;
}
/*
if (nready)
{
printf("nread = %d\n", nready) ;
}
*/
if ( 0 == nready ) // timeout
{
continue ;
}
if ( client[0].revents & POLLIN ) //listening socket creats an writeable event
{
//printf ("output \n\n") ;
socklen_t peerlen = sizeof(peeraddr) ;
conn = accept ( listenfd , (struct sockaddr*) &peeraddr , &peerlen ) ;
if ( -1 == conn )
{
ERR_EXIT("accept") ;
}
for ( i = 0 ; i < 2048 ; i ++ )
{
if ( client[i].fd < 0 )
{
client[i].fd = conn ;
client[i].events = POLLIN ;
if ( i > maxIndex )
{
maxIndex = i ;
}
break ;
}
}
if ( 2048 == i )
{
fprintf ( stderr, "too many clients\n") ;
exit (EXIT_FAILURE) ;
}
//int count ;
//count = 0 ;
printf("count = %d\n" , ++count ) ;
printf ("ip = %s port = %d\n" , inet_ntoa(peeraddr.sin_addr) , ntohs(peeraddr.sin_port) ) ;
//client[i].events = POLLIN ;
if ( --nready <= 0 ) // all the monitoring event were cleared out.
{
continue ;
}
}
for ( i = 1 ; i <= maxIndex ; i ++ ) //connected socket creat a writeable event
{
conn = client[i].fd ;
if ( -1 == conn )
{
continue ;
}
if ( client[i].revents & (POLLIN | POLLERR) )
{
//printf("output\n") ;
char recvbuf[1024] = {0} ;
int ret = readline ( conn , recvbuf , 1024 ) ;
if ( -1 == ret )
{
ERR_EXIT("readline") ;
}
if ( 0 == ret )
{
printf ("client close\n") ;
client[i].fd = -1 ;
close (conn ) ; // this is a bug which we missed before
}
//printf("ret = %d\n", ret ) ;
fputs ( recvbuf , stdout ) ;
// sleep(4) ;
writen ( conn , recvbuf , strlen(recvbuf) ) ;
if ( -- nready <= 0 )
{
break ;
}
}
}
}
return 0 ;
}
When we change the parameter of both sides ( ulimit -n 2048) , we found that the max concurrent connection of server is 2044 ( 1 - 2044 ) , because we have stdin input , stdin output , stdin error and listenfd( the listening socket), in comparison with server , the client's number is 2045 , because the client don't need to listen.