并发服务器有三种设计模式:

多进程:每个进程服务一个客户端。优势是有各自独立的地址空间,可靠性高,但进程调度开销大,无法资源共享,进程间通信机制复杂。

多线程:每个线程服务一个客户端。优势是开销小,通信机制简单,可共享内存。但共享地址空间,可靠性低,一个服务器出现问题时可能导致系统崩溃,同时全局共享可能带来竞争,共享资源需要互斥,对编程要求高。

单进程:占有的进程及线程资源少,通信机制简单。但监听服务器及各个子服务器揉和在一起,程序结构复杂不清晰,编程麻烦。

 

 


// File: mult-pr-tcp-server.c
/*
多进程并发服务器。该程序等候客户连接,一旦连接则显示客户的地址,
接着接收该客户的名字并显示。然后接收来自该客户的信息(字符串)。
每当收到一个字符串,则显示该字符串,并将字符串反转,再将反转的字
符发回客户。之后,继续等待接收该客户的信息直至该客户关闭连接。服
务器具有同时处理多客户的能力。
*/
#include <stdio.h>          /* These are the usual header files */ 
#include <strings.h>          /* for bzero() */
#include <unistd.h>         /* for close() */
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h>
 
#define PORT 1234   /* Port that will be opened */ 
#define BACKLOG 2   /* Number of allowed connections */ 
#define MAXDATASIZE 1000  
void process_cli(int connectfd, struct sockaddr_in client);
 
main() 
{ 
        int listenfd, connectfd; /* socket descriptors */ 
        pid_t pid;
        struct sockaddr_in server; /* server's address information */ 
        struct sockaddr_in client; /* client's address information */ 
        int sin_size; 
 
        /* Create TCP socket  */
        if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
           /* handle exception */
           perror("Creating socket failed.");
           exit(1);
           }
 
        int opt = SO_REUSEADDR;
        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
 
        bzero(&server,sizeof(server));
        server.sin_family=AF_INET; 
        server.sin_port=htons(PORT); 
        server.sin_addr.s_addr = htonl (INADDR_ANY); 
        if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) { 
           /* handle exception */
           perror("Bind error.");
           exit(1); 
           }    
 
        if(listen(listenfd,BACKLOG) == -1){  /* calls listen() */ 
           perror("listen() error/n"); 
           exit(1); 
           } 
 
        sin_size=sizeof(struct sockaddr_in); 
 
        while(1)
        {
          /*accept connection.what causes the acceptance? */
         if ((connectfd = accept(listenfd,(struct sockaddr *)&client,&sin_size))==-1) {
           perror("accept() error/n"); 
           exit(1); 
           } 
        /*  Create child process to service client */
        if ((pid=fork())>0) {
           /* parent process */
           close(connectfd);
           continue;
           }
        else if (pid==0) {
           /*child process*/
           close(listenfd);
           process_cli(connectfd, client);
           exit(0);     
           }
        else {
           printf("fork error/n");
           exit(0);
           }
        }
        close(listenfd);   /* close listenfd */         
} 
 
void process_cli(int connectfd, struct sockaddr_in client)
{
        int num;
        char recvbuf[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE];
 
        printf("You got a connection from %s.  ",inet_ntoa(client.sin_addr) ); /* prints client's IP */ 
        /* Get client's name from client */
        num = recv(connectfd, cli_name, MAXDATASIZE,0);
        if (num == 0) {
           close(connectfd);
           printf("Client disconnected./n");
           return;
           }
        cli_name[num - 1] = '/0';
        printf("Client's name is %s./n",cli_name);
 
        while (num = recv(connectfd, recvbuf, MAXDATASIZE,0)) 
        {
                int i = 0;
                recvbuf[num] = '/0';
                printf("Received client( %s ) message: %s",cli_name, recvbuf);
                for (i = 0; i < num - 1; i++) {
                sendbuf[i] = recvbuf[num - i -2];
                }
                sendbuf[num - 1] = '/0';
 
                send(connectfd,sendbuf,strlen(sendbuf),0); /* send to the client welcome message */ 
        }
        close(connectfd); /*  close connectfd */ 
}

 


// File: mult-thread-tcp-server.c
/*
多线程并发服务器。该程序实现多线程并发服务器
*/
 #include <stdio.h>          /* These are the usual header files */ 
//#include <strings.h>          /* for bzero() only <strings.h>  can not be compiled for memcpy in c++*/
#include <string.h>          /* for bzero() */
#include <unistd.h>         /* for close() */
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h>
#include <pthread.h>
#include <stdlib.h>   /* for exit in c++(.C/.cc) ; no need for c ??*/
 
#define PORT 1234   /* Port that will be opened */ 
#define BACKLOG 5   /* Number of allowed connections */ 
#define MAXDATASIZE 1000  
 
//void process_cli(int connectfd, sockaddr_in client); // c only supports struct sockaddr_in, but c++ support sockaddr_in
void process_cli(int connectfd, struct sockaddr_in client);
/* function to be executed by the new thread */
void* start_routine(void* arg);
typedef struct  _ARG  {
   int connfd;
   struct sockaddr_in client;  
}ARG;
// it's better to use typedef struct
main() 
{ 
        int listenfd, connectfd; /* socket descriptors */ 
        pthread_t  thread;
        //struct ARG *arg;           // when no typedef,there should be struct for c code; no need for c++
         ARG *arg;
        struct sockaddr_in server; /* server's address information */ 
        struct sockaddr_in client; /* client's address information */ 
        int sin_size; 
 
        /* Create TCP socket */
        if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
           /* handle exception */
           perror("Creating socket failed.");
           exit(1);
        }
 
        int opt = SO_REUSEADDR;
        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
 
        bzero(&server,sizeof(server));
        server.sin_family=AF_INET; 
        server.sin_port=htons(PORT); 
        server.sin_addr.s_addr = htonl (INADDR_ANY); 
        if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) { 
           /* handle exception */
           perror("Bind error.");
           exit(1); 
           }    
 
        if(listen(listenfd,BACKLOG) == -1){  /* calls listen() */ 
           perror("listen() error/n"); 
           exit(1); 
           } 
 
        sin_size=sizeof(struct sockaddr_in); 
        while(1)
        {
           /* Accept connection */
          // if ((connectfd = accept(listenfd,(struct sockaddr *)&client,&sin_size))==-1) {// no problem for c
          if ((connectfd = accept(listenfd,(struct sockaddr *)&client,(socklen_t *)&sin_size))==-1) {
              perror("accept() error/n"); 
              exit(1); 
              } 
           /*  Create thread*/
 
           arg = new  ARG;
           arg->connfd = connectfd;
           //memcpy((void *)&arg->client, &client, sizeof(client)); // both ok!
           memcpy(&arg->client, &client, sizeof(client));
 
           if (pthread_create(&thread, NULL, start_routine, (void*)arg)) {
              /* handle exception */
              perror("Pthread_create() error");
              exit(1);
              }
        }
        close(listenfd);   /* close listenfd */         
} 
 
void process_cli(int connectfd, sockaddr_in client)
{
        int num;
        char recvbuf[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE];
 
        printf("You got a connection from %s.  ",inet_ntoa(client.sin_addr) ); 
        /* Get client's name from client */
        num = recv(connectfd, cli_name, MAXDATASIZE,0);
        if (num == 0) {
           close(connectfd);
           printf("Client disconnected./n");
           return;
           }
        cli_name[num - 1] = '/0';
        printf("Client's name is %s./n",cli_name);
 
        while (num = recv(connectfd, recvbuf, MAXDATASIZE,0)) {
           recvbuf[num] = '/0';
           printf("Received client( %s ) message: %s",cli_name, recvbuf);
           for (int i = 0; i < num - 1; i++) {
              sendbuf[i] = recvbuf[num - i -2];
              }
           sendbuf[num - 1] = '/0';
           send(connectfd,sendbuf,strlen(sendbuf),0);
           }
        close(connectfd); /*  close connectfd */ 
}
 
void* start_routine(void* arg)
{
        ARG *info;
        info = (ARG *)arg;
 
        /* handle client's requirement */
        process_cli(info->connfd, info->client);
 
        delete arg will cause warning!the type for deleting should be the same as new allocated
        delete info;
        pthread_exit(NULL);
}

 


// File:singlethread-mult-tcp-server.C
/*单线程并发服务器实例。该程序采用单线程并发服务器算法实现的。*/
#include <stdio.h>          /* These are the usual header files */ 
#include <string.h>          /* for bzero() */
#include <unistd.h>         /* for close() */
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h>
#include <sys/time.h>
#include <stdlib.h>
 
#define PORT 1234   /* Port that will be opened */ 
#define BACKLOG 5   /* Number of allowed connections simutaniously*/ 
#define MAXDATASIZE 1000 
typedef struct _CLIENT{
   int       fd;
   char*  name;
   struct sockaddr_in addr; /* client's address information */
   char* data;                                        
} CLIENT;  
void process_cli(CLIENT *client, char* recvbuf, int len);
void savedata(char* recvbuf, int len, char* data);
 
main() 
{ 
        int    i, maxi, maxfd,sockfd;
        int    nready;
        ssize_t      n;
        fd_set      rset, allset;
        int listenfd, connectfd; /* socket descriptors */     
        struct sockaddr_in server; /* server's address information */ 
        /* client's information */ 
        CLIENT client[FD_SETSIZE];
        char recvbuf[MAXDATASIZE];
        int sin_size; 
 
        /* Create TCP socket  */
        if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
           /* handle exception */
           perror("Creating socket failed.");
           exit(1);
           }
 
        int opt = SO_REUSEADDR;
        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
 
        bzero(&server,sizeof(server));
        server.sin_family=AF_INET; 
        server.sin_port=htons(PORT); 
        server.sin_addr.s_addr = htonl (INADDR_ANY); 
        if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) { 
           /* handle exception */
           perror("Bind error.");
           exit(1); 
           }    
 
        if(listen(listenfd,BACKLOG) == -1){  /* calls listen() */ 
           perror("listen() error/n"); 
           exit(1); 
           } 
 
        sin_size=sizeof(struct sockaddr_in); 
        /*initialize for select */
        maxfd = listenfd;      
        maxi = -1;                
        for (i = 0; i < FD_SETSIZE; i++) {
           client[i].fd = -1;   
           }
        FD_ZERO(&allset);
        FD_SET(listenfd, &allset);
 
        while(1)
        {
        struct sockaddr_in addr;
        rset = allset;             
        nready = select(maxfd+1, &rset, NULL, NULL, NULL);
        printf("select saw rset actions and the readfset num is %d. /n",nready );
 
        if (FD_ISSET(listenfd, &rset)) 
        {      /* new client connection */
                /* Accept connection */
                printf("accept a connection./n");
                if ((connectfd = accept(listenfd,(struct sockaddr *)&addr,(socklen_t *)&sin_size))==-1) {
                perror("accept() error/n"); 
                continue; 
                }
 
                /* Put new fd to client */
                for (i = 0; i < FD_SETSIZE; i++)
                if (client[i].fd < 0) {
                client[i].fd = connectfd;    /* save descriptor */
                client[i].name = new char[MAXDATASIZE];
                client[i].addr = addr;
                client[i].data = new char[MAXDATASIZE];
                client[i].name[0] = '/0';
                client[i].data[0] = '/0';
                printf("You got a connection from %s.  ",inet_ntoa(client[i].addr.sin_addr) ); 
                break;
                }
 
                printf("add new connect fd./n");
                if (i == FD_SETSIZE)          printf("too many clients/n");
                FD_SET(connectfd, &allset);   /* add new descriptor to set */
                if (connectfd > maxfd)  maxfd = connectfd;    
                if (i > maxi)      maxi = i;           
                if (--nready <= 0) continue;       /* no more readable descriptors */
        }
 
        for (i = 0; i <= maxi; i++) 
        {      
                /* check all clients for data */
                if ( (sockfd = client[i].fd) < 0)   continue; /* no  more connected clients*/
                
                if (FD_ISSET(sockfd, &rset)) {
                printf("recv occured for connect fd[%d]./n",i);
                if ( (n = recv(sockfd, recvbuf, MAXDATASIZE,0)) == 0) {
                /*connection closed by client */
                close(sockfd);
                printf("Client( %s ) closed connection. User's data: %s/n",client[i].name,client[i].data);
                FD_CLR(sockfd, &allset);
                client[i].fd = -1;
                delete client[i].name;
                delete client[i].data;
                } else
                process_cli(&client[i], recvbuf, n);
                if (--nready <= 0)     break;      /* no more readable descriptors */
                }
        }
        }
        close(listenfd);   /* close listenfd */         
} 
 
void process_cli(CLIENT *client, char* recvbuf, int len)
{
        char sendbuf[MAXDATASIZE];
 
        recvbuf[len-1] = '/0';
        if (strlen(client->name) == 0) {
           /* Got client's name from client */
           memcpy(client->name,recvbuf, len);
           printf("Client's name is %s./n",client->name);
           return;
           }
 
        /* save client's data */
        printf("Received client( %s ) message: %s/n",client->name, recvbuf);
        /* save user's data */
        savedata(recvbuf,len, client->data);
        /* reverse usr's data */
        for (int i1 = 0; i1 < len - 1; i1++) {
           sendbuf[i1] = recvbuf[len - i1 -2];
        }
        sendbuf[len - 1] = '/0';
 
        send(client->fd,sendbuf,strlen(sendbuf),0); 
}
 
void savedata(char* recvbuf, int len, char* data)
{
        int start = strlen(data);
        for (int i = 0; i < len; i++) {
           data[start + i] = recvbuf[i];
        }         
}