几种并发服务器模型的实现:多线程,多进程,select,poll,epoll

时间:2021-09-04 16:43:47

http://www.cnblogs.com/wj9012/p/3879605.html


客户端使用select模型:

几种并发服务器模型的实现:多线程,多进程,select,poll,epoll
  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <errno.h>
  5 #include <sys/types.h>
  6 #include <sys/socket.h>
  7 #include <netinet/in.h>
  8 #include <arpa/inet.h>
  9 #include <sys/select.h>
 10 #include <signal.h>
 11 
 12 #define ERR_EXIT(m) \
 13     do { \
 14         perror(m);\
 15         exit(EXIT_FAILURE);\
 16     }while(0)
 17 
 18 static void do_client(int fd)
 19 {
 20     char recvbuf[MAXLINE + 1] = {0};
 21     char sendbuf[MAXLINE + 1] = {0};
 22 
 23     fd_set reade, ready;
 24     FD_ZERO(&reade);
 25     int fd_stdin = fileno(stdin);
 26     FD_SET(fd_stdin, &reade);
 27     FD_SET(fd, &reade);
 28     int fd_max = (fd_stdin > fd) ? fd_stdin : fd;
 29 
 30     int ret;
 31     while(1)
 32     {
 33         ready = reade;
 34         ret = select( fd_max+1, &ready, NULL, NULL, NULL);//轮询
 35         if(ret == -1)
 36         {
 37             if(errno == EINTR)
 38                 continue;
 39             ERR_EXIT("select");
 40         }else if(ret ==  0)
 41         {
 42             continue;
 43         }
 44 
 45         if(FD_ISSET(fd_stdin, &ready))
 46         {
 47             if(fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)
 48             {
 49                 close(fd);
 50                break;
 51             }else
 52             {
 53                 if( -1 == write(fd, sendbuf, strlen(sendbuf)))
 54                     printf("write\n");
 55             }
 56         }
 57 
 58 
 59         if(FD_ISSET(fd, &ready))
 60         {
 61             int nread = read(fd, recvbuf, MAXLINE);
 62             if(nread < 0)
 63                 ERR_EXIT("read");
 64             if(nread == 0)//如果没接收到消息,打印关闭描述符,退出循环
 65             {
 66                 fprintf(stdout, "fd close\n");
 67                 break;
 68             }
 69             fprintf(stdout, "receive:%s", recvbuf);
 70         }
 71         memset(recvbuf, 0, sizeof recvbuf);
 72         memset(sendbuf, 0, sizeof sendbuf);
 73     }
 74 }
 75 void handle(int signum)
 76 {
 77 printf("sigpipe\n");
 78 }
 79 
 80 int main(int argc, const char *argv[])
 81 {
 82     signal(SIGPIPE, SIG_IGN);
 83     int fd = socket(AF_INET, SOCK_STREAM, 0);
 84     if(fd < 0)
 85         ERR_EXIT("socket");
 86 
 87     struct sockaddr_in cliaddr;
 88     cliaddr.sin_family = AF_INET;
 89     cliaddr.sin_port = htons(8888);
 90     cliaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
 91     socklen_t len = sizeof cliaddr;
 92 
 93     int ret ;
 94     if((ret = connect(fd, (struct sockaddr*)&cliaddr, len)) == -1)
 95     {
 96         close(fd);
 97         ERR_EXIT("connect");
 98     }
 99     do_client(fd);
100     close(fd);
101     return 0;
102 }
几种并发服务器模型的实现:多线程,多进程,select,poll,epoll

 

 

1.并发多进程服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "rio.h"//封装了网络编程中的readn  writen   readline 3个函数
#include <signal.h>
 
#define ERR_EXIT(m) \
     do  { \
         perror (m);\
         exit (EXIT_FAILURE);\
     } while (0)
 
static  void  do_service( int  fd)
{
     rio_t rt;
     rio_init(&rt, fd);
     char  recvbuf[1024] = {0};
     int  ret;
     while (1)
     {
         memset (recvbuf, 0,  sizeof  recvbuf);
         ret = rio_readline(&rt, recvbuf, 1024);
         if (ret == 0)  
         {
             close(fd);
             exit (EXIT_SUCCESS);
         }
         rio_writen(fd, recvbuf,  strlen (recvbuf)) ;
     }
}
 
void  handle( int  signum) //对SIGCHLD信号的处理函数
{
     while (waitpid(-1, NULL, WNOHANG) > 0);
     return  ;
}
 
int  listenf(); //封装了socket,bind,listen3个函数,返回需要监听的连接socket描述符
 
int  main( int  argc,  const  char  *argv[])
{
     if ( signal (SIGPIPE, SIG_IGN) == SIG_ERR) //对客户端关闭导致的信号的处理
         ERR_EXIT( "signal_pipe" );
     if ( signal (SIGCHLD, handle) == SIG_ERR) //对子进程结束后资源的回收
         ERR_EXIT( "signal_chld" );
 
 
     int  listenfd = listenf();
 
     struct  sockaddr_in cliaddr;
     bzero(&cliaddr,  sizeof (cliaddr));
     socklen_t cli_len =  sizeof  cliaddr;
     int  clientfd;
     while (1)
     {
         clientfd = accept(listenfd, ( struct  sockaddr*)&cliaddr, &cli_len);
         if (clientfd == -1) 
         {
             close(listenfd);
             ERR_EXIT( "accept" );
         }
         
         int  pid;
         if ((pid = fork()) == -1)
         {
             ERR_EXIT( "fork" );
         } else  if (pid == 0)
         {
             close(listenfd);
             do_service(clientfd);
             exit (EXIT_SUCCESS);
         }
         close(clientfd);
     }
     close(listenfd);
     return  0;
}
 
int  listenf()
{
     int  listenfd = socket(AF_INET, SOCK_STREAM, 0); //准备一个socketfd
     if (listenfd == -1 )
         ERR_EXIT( "socket" );
 
     int  on = 1;
     if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on,  sizeof (on)) == -1) //setsockopt设置端口复用
     {
         close(listenfd);
         ERR_EXIT( "setsockopt" );
     }
 
     struct  sockaddr_in seraddr;
     seraddr.sin_family = AF_INET;
     seraddr.sin_port = htons(8888);
     seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
     socklen_t len =  sizeof (seraddr);
     if (bind(listenfd, ( struct  sockaddr*)&seraddr, len) == -1) //监听socket端口,
     {
         close(listenfd);
         ERR_EXIT( "bind" );
     }
 
     if (listen(listenfd, 6) == -1)
     {
         close(listenfd);
         ERR_EXIT( "listen" );
     }
     return  listenfd;
}

 2.多线程服务器模型:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "rio.h"
#include <signal.h>
#include <pthread.h>
 
#define ERR_EXIT(m) \
     do  { \
         perror (m);\
         exit (EXIT_FAILURE);\
     } while (0)
 
static  void  do_service( int  fd)
{
     rio_t rt;
     rio_init(&rt, fd);
     char  recvbuf[1024] = {0};
     int  ret;
     int  rcnt = 0;
     while (1)
     {
         memset (recvbuf, 0,  sizeof  recvbuf);
         ret = rio_readline(&rt, recvbuf, 1024);
          if (ret == 0)
          {
          close(fd);
//         break;
           pthread_exit(NULL);
          }
          write(fd, recvbuf,  strlen (recvbuf)) ;
     }
}
 
void  handle( int  signum)
{
     printf ( "hello\n" );
}
 
void  *pthread_func( void  *arg)
{
     pthread_detach(pthread_self());
     int  *q = ( int *)arg;
    int  p = *( int *)arg;
    free (q);
      do_service(p);
      close(p);
}
int  listenf();
 
int  main( int  argc,  const  char  *argv[])
{
     if ( signal (SIGPIPE, handle) == SIG_ERR)
         ERR_EXIT( "signal" );
     int  listenfd = listenf();
 
     struct  sockaddr_in cliaddr;
     bzero(&cliaddr,  sizeof (cliaddr));
     socklen_t cli_len =  sizeof  cliaddr;
     int  clientfd;
     while (1)
     {
     clientfd = accept(listenfd, ( struct  sockaddr*)&cliaddr, &cli_len);
     if (clientfd == -1) 
     {
         close(listenfd);
         ERR_EXIT( "accept" );
     }
 
     pthread_t tid;
     int  *p = ( int *) malloc ( sizeof ( int ));
    *p = clientfd;
     pthread_create(&tid, NULL, pthread_func, p);
     }
     close(listenfd);
     return  0;
}
 
int  listenf()
{
     int  listenfd = socket(AF_INET, SOCK_STREAM, 0); //准备一个socketfd
     if (listenfd == -1 )
         ERR_EXIT( "listen" );
 
     int  on = 1;
     if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on,  sizeof (on)) == -1) //setsockopt设置端口复用
     {
         close(listenfd);
         ERR_EXIT( "setsockopt" );
     }
 
     struct  sockaddr_in seraddr;
     seraddr.sin_family = AF_INET;
     seraddr.sin_port = htons(8888);
     seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
     socklen_t len =  sizeof (seraddr);
     if (bind(listenfd, ( struct  sockaddr*)&seraddr, len) == -1) //监听socket端口,
     {
         close(listenfd);
         ERR_EXIT( "bind" );
     }
 
     if (listen(listenfd, 6) == -1)
     {
         close(listenfd);
         ERR_EXIT( "listen" );
     }
return  listenfd;
}

 3.select服务器模型:

几种并发服务器模型的实现:多线程,多进程,select,poll,epoll
  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <unistd.h>
  5 #include <errno.h>
  6 #include <sys/types.h>
  7 #include <sys/stat.h>
  8 #include <sys/socket.h>
  9 #include <arpa/inet.h>
 10 #include <signal.h>
 11 #include <sys/select.h>
 12 #define MAXLEN 1024
 13 
 14 #define ERR_EXIT(m) \
 15     do { \
 16         perror(m);\
 17         exit(EXIT_FAILURE);\
 18     }while(0)
 19 
 20 int listenf();//封装了网络socket的socekt,bind,listen函数,返回监听的socket描述符
 21 void handle(int signum)//SIGPIPE(子进程结束)的信号的处理
 22 {
 23     printf("hello\n");
 24 }
 25 static void do_select(int);
 26 
 27 int main(int argc, const char *argv[])
 28 {
 29     if(signal(SIGPIPE, handle) == SIG_ERR)
 30         ERR_EXIT("signal");
 31     int listenfd = listenf();
 32     do_select(listenfd);
 33     close(listenfd);
 34     return 0;
 35 }
 36 
 37 int listenf()
 38 {
 39     int listenfd = socket(AF_INET, SOCK_STREAM, 0);//准备一个socketfd
 40     if(listenfd == -1 )
 41         ERR_EXIT("listen");
 42 
 43     int on = 1;
 44     if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt设置端口复用
 45     {
 46         close(listenfd);
 47         ERR_EXIT("setsockopt");
 48     }
 49 
 50     struct sockaddr_in seraddr;
 51     seraddr.sin_family = AF_INET;
 52     seraddr.sin_port = htons(8888);
 53     seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
 54     socklen_t len = sizeof(seraddr);
 55     if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//监听socket端口,
 56     {
 57         close(listenfd);
 58         ERR_EXIT("bind");
 59     }
 60 
 61     if(listen(listenfd, 6) == -1)
 62     {
 63         close(listenfd);
 64         ERR_EXIT("listen");
 65     }
 66     return listenfd;
 67 }
 68 
 69 void do_select(int fd)
 70 {
 71     //struct sockaddr_in cliaddr;
 72     //memset(&cliaddr, 0, sizeof(cliaddr));//此处不需要客户端的地址信息
 73     fd_set set;
 74     fd_set rset;
 75     FD_ZERO(&rset);
 76     FD_SET(fd, &rset);
 77     int nready;
 78     int fd_set[MAXLEN];
 79 
 80     int i;
 81     for(i = 0;i < MAXLEN; ++i)
 82         fd_set[i] = -1;
 83     fd_set[0] = fd;
 84 
 85     int maxi = fd;//初始最大轮询fd是监听的fd
 86     int  arrlen = 1;//表示数组长度
 87    
 88    char recvbuf[1024] = {0};
 89     while(1)
 90     {
 91         set = rset;
 92         nready = select(maxi+1, &set, NULL, NULL, NULL);
 93         if(nready == -1)
 94         {
 95             ERR_EXIT("select");
 96         }
 97         if(FD_ISSET(fd, &set))//查看书否有新的客户端请求
 98         {
 99             int clifd = accept(fd, NULL, NULL);
100             if(clifd == -1)
101                 ERR_EXIT("accept");
102             for(i = 1; i < MAXLEN; ++i)
103             { if(fd_set[i] == -1)
104                 {
105                     fd_set[i] = clifd;
106                     break;
107                 }
108             }
109             FD_SET(clifd, &rset);
110             if(clifd > maxi)
111                 maxi = clifd;
112             arrlen++;
113             --nready;
114         }
115 
116         for(i = 1; i < arrlen; ++i)//轮询数据连接
117         {
118             int set_fd = fd_set[i];
119          if(FD_ISSET(set_fd, &set))
120          {
121             int n = read(set_fd, recvbuf, 1024);
122             if(n == -1)
123                 ERR_EXIT("read");
124             else if(n == 0)//客户端退出,
125             {
126               FD_CLR(set_fd, &rset);
127               close(set_fd);
128               fd_set[i] = -1;
129               arrlen--;
130             }
131             write(set_fd, recvbuf, strlen(recvbuf));
132                memset(recvbuf, 0, 1024);
133          if(--nready < 0)
134              break;
135          }
136         }
137     }
138 }
几种并发服务器模型的实现:多线程,多进程,select,poll,epoll


4

几种并发服务器模型的实现:多线程,多进程,select,poll,epoll
  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <unistd.h>
  5 #include <errno.h>
  6 #include <sys/types.h>
  7 #include <sys/stat.h>
  8 #include <sys/socket.h>
  9 #include <arpa/inet.h>
 10 #include <signal.h>
 11 #include <poll.h>
 12 #define MAXLEN 1024
 13 
 14 #define ERR_EXIT(m) \
 15     do { \
 16         perror(m);\
 17         exit(EXIT_FAILURE);\
 18     }while(0)
 19 
 20 static void do_service(int);
 21 int listenf();
 22 void handle(int signum)
 23 {
 24     printf("hello\n");
 25 }
 26 static void do_poll(int);
 27 
 28 int main(int argc, const char *argv[])
 29 {
 30     if(signal(SIGPIPE, handle) == SIG_ERR)
 31         ERR_EXIT("signal");
 32     int listenfd = listenf();
 33     do_poll(listenfd);
 34     close(listenfd);
 35     return 0;
 36 }
 37 
 38 int listenf()
 39 {
 40     int listenfd = socket(AF_INET, SOCK_STREAM, 0);//准备一个socketfd
 41     if(listenfd == -1 )
 42         ERR_EXIT("listen");
 43 
 44     int on = 1;
 45     if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt设置端口复用
 46     {
 47         close(listenfd);
 48         ERR_EXIT("setsockopt");
 49     }
 50 
 51     struct sockaddr_in seraddr;
 52     seraddr.sin_family = AF_INET;
 53     seraddr.sin_port = htons(8888);
 54     seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
 55     socklen_t len = sizeof(seraddr);
 56     if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//监听socket端口,
 57     {
 58         close(listenfd);
 59         ERR_EXIT("bind");
 60     }
 61 
 62     if(listen(listenfd, 6) == -1)
 63     {
 64         close(listenfd);
 65         ERR_EXIT("listen");
 66     }
 67     return listenfd;
 68 }
 69 
 70 void do_poll(int fd)
 71 {
 72     struct pollfd pfd[MAXLEN];//设置的最大连接数,存放结构体信息
 73     char recvbuf[1024] = {0};
 74     int i;
 75     for(i = 0; i < MAXLEN; ++i)
 76         pfd[i].fd = -1;
 77 
 78     pfd[0].fd = fd;
 79     pfd[0].events = POLLIN;//将监听连接的描述符防止数组开头
 80 
 81     int nready;
 82     int maxi = 0;
 83     while(1)
 84     {
 85         nready = poll(pfd, maxi+1, -1);//轮询的是有效数组长度,不能少1
 86         if(nready == -1)
 87         {
 88             ERR_EXIT("select");
 89         }
 90 
 91         if(pfd[0].revents & POLLIN)//有客户端请求连接
 92         {
 93             int clifd = accept(fd, NULL, NULL);
 94             if(clifd == -1)
 95                 ERR_EXIT("accept");
 96             for(i = 1; i < MAXLEN; ++i)
 97             { 
 98                 if(pfd[i].fd == -1)
 99                 {
100                     pfd[i].fd = clifd;
101                     pfd[i].events = POLLIN;
102                     break;
103                 }
104             }
105             if(i > maxi)
106                 maxi = i;  
107             --nready;
108         }
109 
110         for(i = 1; i <= maxi; ++i)
111         {
112             if(pfd[i].fd == -1)
113                 continue;
114 
115             if( pfd[i].revents & POLLIN)
116             {
117                 int n = read(pfd[i].fd, recvbuf, 1024);
118                 if(n == -1)
119                     ERR_EXIT("read");
120                 else if(n == 0)//客户端退出,则从集合中清除
121                 {
122                     printf("%d close\n", pfd[i].fd);
123                     close(pfd[i].fd); 
124                     pfd[i].fd = -1;
125                     continue;
126                 }
127                 write(pfd[i].fd, recvbuf, strlen(recvbuf));
128                 memset(recvbuf, 0, 1024);
129 
130                 if(--nready < 0)
131                     break;
132             }
133         }
134     }
135 }
几种并发服务器模型的实现:多线程,多进程,select,poll,epoll

5.epoll服务器模型:

几种并发服务器模型的实现:多线程,多进程,select,poll,epoll
  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <unistd.h>
  5 #include <errno.h>
  6 #include <sys/types.h>
  7 #include <sys/stat.h>
  8 #include <sys/socket.h>
  9 #include <arpa/inet.h>
 10 #include <signal.h>
 11 #include <sys/epoll.h>
 12 #define MAXLEN 1024
 13 
 14 #define ERR_EXIT(m) \
 15     do { \
 16         perror(m);\
 17         exit(EXIT_FAILURE);\
 18     }while(0)
 19 
 20 int listenf();
 21 static void do_epoll(int);
 22 
 23 int main(int argc, const char *argv[])
 24 {
 25     int listenfd = listenf();
 26     do_epoll(listenfd);
 27     close(listenfd);
 28     return 0;
 29 }
 30 
 31 int listenf()
 32 {
 33     int listenfd = socket(AF_INET, SOCK_STREAM, 0);//准备一个socketfd
 34     if(listenfd == -1 )
 35         ERR_EXIT("listen");
 36 
 37     int on = 1;
 38     if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt设置端口复用
 39     {
 40         close(listenfd);
 41         ERR_EXIT("setsockopt");
 42     }
 43 
 44     struct sockaddr_in seraddr;
 45     seraddr.sin_family = AF_INET;
 46     seraddr.sin_port = htons(8888);
 47     seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
 48     socklen_t len = sizeof(seraddr);
 49     if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//监听socket端口,
 50     {
 51         close(listenfd);
 52         ERR_EXIT("bind");
 53     }
 54 
 55     if(listen(listenfd, 6) == -1)
 56     {
 57         close(listenfd);
 58         ERR_EXIT("listen");
 59     }
 60     return listenfd;
 61 }
 62 
 63 void do_epoll(int fd)
 64 {
 65     char recvbuf[MAXLEN] = {0};
 66     int epollfd = epoll_create(2048);//设置的最大连接数
 67     if(epollfd == -1)
 68         ERR_EXIT("epoll_create");
 69 
 70     struct epoll_event ev;
 71     ev.data.fd = fd;
 72     ev.events = EPOLLIN;
 73     if(epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1)//加入轮询
 74                  ERR_EXIT("epoll_ctl_add");
 75 
 76     struct epoll_event events[2048];//数组在epoll_wait返回结果的时候使用
 77     int ret;
 78     int i;//在下面while的for循环中遍历使用
 79     int rfd;
 80     int clientfd;
 81     int nread;
 82     while(1)
 83     {
 84         ret = epoll_wait(epollfd, events, 2048, -1);
 85         if(ret == -1)
 86             ERR_EXIT("epoll_wait");
 87 
 88         for(i = 0; i < ret; ++i )
 89         {
 90             rfd = events[i].data.fd;
 91             if(rfd == fd)
 92             {
 93                 if((clientfd = accept(fd, NULL, NULL)) == -1)
 94                 ERR_EXIT("accept");
 95                 ev.data.fd = clientfd;
 96                 ev.events = EPOLLIN;
 97                 if(epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &ev) == -1)
 98                     ERR_EXIT("epoll_ctl");
 99             }else
100             {
101                 int nread = read(rfd, recvbuf, MAXLEN);
102                 if(nread == -1)
103                 {
104                     if(errno == EINTR)
105                         continue;
106                     ERR_EXIT("read");
107                 }else if( nread == 0)//客户端退出,从epoll轮询中删除
108                 {
109                     printf("%d fd close\n", rfd);
110                     ev.data.fd = rfd;
111                     ev.events = EPOLLIN;
112                     if(epoll_ctl(epollfd, EPOLL_CTL_DEL, rfd, &ev) == -1)
113                         ERR_EXIT("epoll_ctl");
114                 }else
115                 {
116                     if(write(rfd, recvbuf, strlen(recvbuf)) == -1)
117                         ERR_EXIT("write");
118                     memset(recvbuf, 0, MAXLEN);
119                 }
120             }
121         }
122     }
123     close(epollfd);
124 }
几种并发服务器模型的实现:多线程,多进程,select,poll,epoll