Linux下的网络socket编程-------------多路复用(select )

select多路复用

        • 1.I/O多路复用(IO Multiplexing)
        • 2.select()参数解析
        • 3.示例流程图
        • 4.代码示例

1.I/O多路复用(IO Multiplexing)

IO多路复用模型是建立在内核提供的多路分离函数select基础之上的,使用select函数 可以避免同步非阻塞IO模型中轮询等待的问题,此外poll、epoll都是这种模型。在该种模式下,用户首先将需要进行IO操作的 socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起 read请求,读取数据并继续执行。从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加 监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理 多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处 理多个IO请求的目的。

2.select()参数解析

select()函数允许进程指示内核等待多个事件(文件描述符)中的任何一个发生,并只在有一个或多个事件发生或经历一段指定时 间后才唤醒它,然后接下来判断究竟是哪个文件描述符发生了事件并进行相应的处理。

给出select()的参数

#include  
#include 
struct timeval 
{    
     long tv_sec;   //seconds    
     long tv_usec;  //microseconds 
};
FD_ZERO(fd_set* fds)           //清空集合
FD_SET(int fd, fd_set* fds)    //将给定的描述符加入集合 
FD_ISSET(int fd, fd_set* fds)  //判断指定描述符是否在集合中 
FD_CLR(int fd, fd_set* fds)    //将给定的描述符从文件中删除  

int select(int max_fd, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout);
  1. select函数的返回值是就绪描述符的数目,超时时返回0,出错返回-1;
  2. 第一个参数max_fd指待测试的fd的总个数,它的值是待测试的最大文件描述符加1。Linux内核从0开始到max_fd-1扫描文件描述 符,如果有数据出现事件(读、写、异常)将会返回;假设需要监测的文件描述符是8,9,10,那么Linux内核实际也要监测0~7,此时真 正带测试的文件描述符是0~10总共11个,即max(8,9,10)+1,所以第一个参数是所有要监听的文件描述符中最大的+1。
  3. 中间三个参数readset、writeset和exceptset指定要让内核测试读、写和异常条件的fd集合,如果不需要测试的可以设置为 NULL;
  4. 最后一个参数是设置select的超时时间,如果设置为NULL则永不超时;

说明: select监视并等待多个文件描述符的属性发生变化,它监视的属性分3类,分别是readfds(文件描述符有数据到来可读)、 writefds(文件描述符可写)、和exceptfds(文件描述符异常)。调用后select函数会阻塞,直到有描述符就绪(有数据可读、可写、 或者有错误异常),或者超时( timeout 指定等待时间)发生函数才返回。当select()函数返回后,可以通过遍历 fdset,来找到 究竟是哪些文件描述符就绪。

3.示例流程图

下面是使用select()多路复用实现网络socket服务器多路并发的流程图:
Linux下的网络socket编程-------------多路复用(select )_第1张图片

4.代码示例

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<sys/types.h>
  4 #include<string.h>
  5 #include<errno.h>
  6 #include<unistd.h>
  7 #include<ctype.h>
  8 #include<getopt.h>
  9 #include<time.h>
 10 #include<pthread.h>
 11 #include<libgen.h>
 12 #include<arpa/inet.h>
 13 #include<sys/socket.h>
 14 #include<netinet/in.h>
 15 
 16 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
 17 
 18 static inline void msleep(unsigned long ms);
 19 static inline void print_usage(char *progname);
 20 int socket_server_init(char *listen_ip,int listen_port);
 21 
 22 int main(int argc, char **argv)
 23 {
 24         int          listenfd;
 25         int          connfd;
 26         int          daemon_run = 0;
 27         char         *progname = NULL;
 28         int          opt;
 29         fd_set       rdset;
 30         int          rv;
 31         int          i,j;
 32         int          found;
 33         int          maxfd = 0;
 34         char         buf[1024];
 35         int          fds_array[1024];
 36         int          serv_port = 0;
 37 
 38         struct option     long_options[]=
 39         {
 40                 {"daemon", no_argument, NULL, 'b'},
 41                 {"port", required_argument, NULL, 'p'},
 42                 {"help", no_argument, NULL, 'h'},
 43                 {NULL, 0, NULL, 0}
 44         };
 45 
 46         progname = basename(argv[0]);
 47 
 48         /*Parser the command line parameters */
 49         while((opt = getopt_long(argc,argv, "bh:p", long_options, NULL)) != -1)
 50         {
 51                 switch(opt)
 52                 {
 53                         case 'b':
 54                                 daemon_run = 1;
 55                                 break;
 56 
 57                         case 'p':
 58                                 serv_port = atoi(optarg);
 59                                 break;
 60 
 61                         case 'h':   /* Get help information */
 62                                 print_usage(progname);
 63                                 return EXIT_SUCCESS;
 64 
 65                         default:
 66                                 break;
 67                 }
 68         }
 69 
 70         if(!serv_port)
 71         {
 72                 print_usage(progname);
 73                 return -1;
 74         }
 75 
 76         if((listenfd = socket_server_init(NULL, serv_port)) < 0)
 77         {
 78                 printf("ERROR:%s server listen on port %dfailture\n",argv[0],serv_port);
 79                 return -2;
 80         }
 81         printf("%s server start to listen on port %d\n", argv[0], serv_port);
 82 
 83         /*set program running on background */
 84         if(daemon_run)
 85         {
 86                 daemon(0,0);
 87         }
 88 
 89         for(i = 0; i < ARRAY_SIZE(fds_array); i++)
 90         {
 91                 fds_array[i] = -1;
 92         }
 93         fds_array[0] = listenfd;
 94 
 95         for (; ;)
 96         {
 97                 FD_ZERO(&rdset);
 98                 for(i = 0; i < ARRAY_SIZE(fds_array); i++)
 99                 {
100                         if(fds_array[i] < 0)
101                                 continue;
102                         maxfd = fds_array[i]>maxfd ? fds_array[i] : maxfd;
103                         FD_SET(fds_array[i], &rdset);
104                 }
105 
106                 /*program will blocked here*/
107                rv = select(maxfd+1, &rdset, NULL, NULL, NULL);
108                if(rv < 0)
109                {
110                        printf("select failture: %s\n", strerror(errno));
111                        break;
112                }
113                else if(rv==0)
114                {
115                        printf("select get timeout\n");
116                        continue;
117                }
118 
119                /*listen socket get event means new client start connect now */
120                if( FD_ISSET(listenfd, &rdset))
121                {
122                        if((connfd=accept(listenfd, (struct sockaddr *)NULL, NULL))<0)
123                        {
124                                printf("accept new clientfailture:%s\n",strerror(errno));
125                                continue;
126                        }
127 
128                        found = 0;
129                        for(i = 0; i < ARRAY_SIZE(fds_array); i++)
130                        {
131                                if( fds_array[i] < 0)
132                                {
133                                        printf("accept new client[%d]andadditintoar\n",
134                                                        connfd);
135                                        fds_array[i] = connfd;
136                                        found = 1;
137                                        break;
138                                }
139                        }
140                        if(!found)
141                        {
142                                printf("accept new client[%d] but full, so refuse it\n",
143                                                connfd);
144                                close(connfd);
145                        }
146                }
147 
148                else /*data arrive from already connect client */
149                {
150                        for(i=0; i < ARRAY_SIZE(fds_array); i++)
151                        {
152                                if(fds_array[i]<0 || !FD_ISSET(fds_array[i], &rdset))
153                                        continue;
154                                if((rv=read(fds_array[i],buf,sizeof(buf))) <=0)
155                                {
156                                        printf("socket[%d] read failtur disconnect.\n",
157                                                        fds_array[i]);
158                                        close(fds_array[i]);
159                                        fds_array[1024] = -1;
160                                }
161                                else
162                                {
163                                        printf("socket[%d] read get %d bytes data\n",
164                                                        fds_array[i], rv);
165                                        for(j = 0; j < rv; j++)
166                                                buf[j]=toupper(buf[j]);
167 
168                                        if(write(fds_array[i],buf,rv) < 0)
169                                        {
170                                                printf("socket[%d] write failture:%s\n",
171                                                         fds_array[i],strerror(errno));
172                                                close(fds_array[i]);
173                                                fds_array[i] = -1;
174                                        }
175                                }
176                        }
177                }
178         }
179 Cleanup:
180         close(listenfd);
181         return 0;
182 }
183 
184 static inline void msleep(unsigned long ms)
185 {
186         struct timeval      tv;
187 
188         tv.tv_sec = ms/1000;
189         tv.tv_usec = (ms%1000)*1000;
190 
191         select(0, NULL, NULL, NULL, &tv);
192 }
193 
194 static inline void print_usage(char *progname)
195 {
196         printf("Usage: %s [OPTION]...\n", progname);
197         printf("%s is a socket server progname, which used to verify client and echo back string fr    om it\n",progname);
198         return;
199 }
200 
201 int socket_server_init(char *listen_ip, int listen_port)
202 {
203         struct sockaddr_in     servaddr;
204         int                    rv = 0;
205         int                    on = 1;
206         int                    listenfd;
207 
208         if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
209         {
210                 printf("Use socket() to create a TCP socket failture:%s\n",strerror(errno));
211                 return -1;
212         }
213 
214         setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
215 
216         memset(&servaddr, 0, sizeof(servaddr));
217         servaddr.sin_family = AF_INET;
218         servaddr.sin_port = htons(listen_port);
219 
220         if(!listen_ip)
221         {
222                 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
223         }
224         else
225         {
226                 if(inet_pton(AF_INET, listen_ip, &servaddr.sin_addr) <= 0)
227                 {
228                         printf("inet_pton() set listen IP address failture\n");
229                         rv = -2;
230                         goto Cleanup;
231                 }
232         }
233 
234         if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
235         {
236                 printf("Use bind() to bind the TCP socket failture:%s\n",strerror(errno));
237                 rv = -3;
238                 goto Cleanup;
239         }
240 
241         if(listen(listenfd, 13) < 0)
242         {
243                 printf("Use bind() to bind the TCP socket failture:%s\n",strerror(errno));
244                 rv = -4;
245                 goto Cleanup;
246         }
247 
248 Cleanup:
249         if(rv<0)
250                 close(listenfd);
251         else
252                 rv = listenfd;
253         return rv;
254 }                                  

你可能感兴趣的:(socket,网络,epoll)