前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >linux下多路复用模型之Select模型

linux下多路复用模型之Select模型

作者头像
Gxjun
发布2018-03-26 16:35:25
1.9K0
发布2018-03-26 16:35:25
举报
文章被收录于专栏:mlml

Linux关于并发网络分为Apache模型(Process per Connection (进程连接) ) 和TPC , 还有select模型,以及poll模型(一般是Epoll模型)

 Select模型极其作用:这文章讲述的很好,没必要重述已有的东西,就直接给链接

          http://blog.csdn.net/turkeyzhou/article/details/8609360

        我的理解:

代码语言:javascript
复制
 1 /* According to POSIX.1-2001 */
 2 #include <sys/select.h>
 3 
 4 /* According to earlier standards */
 5 #include <sys/time.h>
 6 #include <sys/types.h>
 7 #include <unistd.h>
 8 
 9 int select(int nfds, fd_set *readfds, fd_set *writefds,
10 fd_set *exceptfds, struct timeval *timeout);
11 
12 void FD_CLR(int fd, fd_set *set);
13 int FD_ISSET(int fd, fd_set *set);
14 void FD_SET(int fd, fd_set *set);
15 void FD_ZERO(fd_set *set);

    对于

代码语言:javascript
复制
   int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
        第一个参数 nfds:  第n个文件id的编号   (linux下,一切皆文件)  需要注意的是: nfds = fd+1 (fd 为 FD_SET中的fd)
        第二个参数:  fd_set *readfds   读取文件编号,如果不需要读取的话 可以设置为NULL
        第三 ,四个参数:  同上
        第五个参数:为一个定义超时的结构体
代码语言:javascript
复制
1        struct timeval {
2                time_t         tv_sec;     /* seconds */
3                suseconds_t    tv_usec;    /* microseconds */
4            };
代码语言:javascript
复制

         该结构用来设定多少时间为超时 ,比如

代码语言:javascript
复制
struct timeval ss ;
   ss.tv_sec  =3;
   ss.tv_usec =0; 
//表示设定为3秒后为超时,select将会返回0
代码语言:javascript
复制
对于下面这几个函数:

1 void FD_CLR(int fd, fd_set *set);

      用来清除fd的fd_set  ,比如fd为5  ,则表示set集中所有设定等于5的fd_set 都将被清除

1 int FD_ISSET(int fd, fd_set *set);

     判断是否set 与fd是否绑定,如果没有绑定,则返回false,如果绑定了则返回True

代码语言:javascript
复制
void FD_SET(int fd, fd_set *set);

        将fd值 和set绑定 

代码语言:javascript
复制
void FD_ZERO(fd_set *set);

   将set集全部清除

简单的例子:

判断是否有数据输入,有则直接打印出来

代码语言:javascript
复制
 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<sys/select.h>
 4 #include<unistd.h>
 5 #include<sys/time.h>
 6 #include<sys/types.h>
 7 #define maxn 6
 8 #define EXIT_FAILURE  -1
 9 #define EXIT_SUCCESS   0
10 
11 int main(int argc , char * argv []){
12 
13    fd_set mtfd ;
14    int ffd =0;
15    struct timeval  outtime ;
16    int retval  ;
17    char redbuf[maxn];
18 
19    FD_ZERO(&mtfd);
20    FD_SET(ffd , &mtfd);
21 
22 // wait up to 5.5 s
23    outtime.tv_sec = 5 ;
24    outtime.tv_usec = 500 ;
25    retval = select(ffd+1 , &mtfd ,NULL , NULL , &outtime);
26    if(-1 == retval )
27    {
28      printf("error happened ! %d \n" ,__LINE__ );
29      return EXIT_FAILURE ;
30    }
31    else if(0 == retval ){
32       printf(" timeout !!  %d \n" ,__LINE__ );
33      return  EXIT_FAILURE ;
34     }
35    //means that is good !
36 
37      printf("retval  =  %d \n", retval);
38 
39     if( FD_ISSET(ffd , &mtfd) ){
40 
41         memset(redbuf ,0 , sizeof(redbuf));
42         printf("reading ... !! ");
43        // read(1 , redbuf ,sizeof(redbuf) ); //use the sys func
44        fread(redbuf , sizeof(redbuf) ,ffd+1 , stdin );
45 }
46     //  fwrite(redbuf ,strlen(redbuf) , 1 , stdout );  
47    //  write(1 , redbuf , strlen(redbuf)); 
48      printf("buf = %s    buf_len = %d \n" , redbuf , strlen(redbuf));
49 
50     return EXIT_SUCCESS ;
51 }

makefile文件:

代码语言:javascript
复制
 1 .SUFFIXES: .o.c
 2 CC =gcc
 3 SRC = Se_keyboard.c
 4 OBJ = $(SRC: .c =.o)
 5 BIN = Se_keyboard
 6 
 7 
 8 .PHONY: start
 9 start:  $(OBJ)
10         $(CC) -o $(BIN) $(OBJ)
11 .o.c: $(SRC)
12         $(CC) -g -Wall $@ -c  $<
13 .PHONY: clean
14 clean:
15         rm -f $(OBJ)                    

  虽然知道这么多,但是还是觉得Select并没有什么作用。

   Select一般是和Socket搭配使用,相当于线程池的效果,但是线程池有缺点,详情看这儿:

   http://blog.csdn.net/tianmohust/article/details/6677985

      http://blog.csdn.net/xifeijian/article/details/17385831

  关于Select的原理图:

      首先来看下一对一的socket的c/s模式

 关于socket的一对一的详解

http://www.cnblogs.com/gongxijun/p/4592864.html  

 看完这个之后,我们现在可以来看看这个图:

下面为举例:  

          服务器创建一个socket并bind绑定一个本机地址和设定一个端口,然后进入listen监听状态。采用select模型而非传统apache模型(ppc)或者tpc模型 。 不过Select模型就是有这样一个特点

  一般我们default默认的SOMAXCONN为128 当然我们可以另外取一个设定(下面我们设定的是2048)作为最大连接数,虽然可以设置更大,但是缺点是,select模型是一个轮询模式,就是每一个都需要遍历一边所有的链接的fd

  查看是否在fd_set集合中,这样,当SOMAXCONN取值非常大时,对于每一个客户端,访问时间都会延迟一点点,这样就是效率不是特别高!

 下面是一个简单的多路复用的网络并发Select模型

代码语言:javascript
复制
  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<stdlib.h>
  4 #include<unistd.h>
  5 #include<netinet/in.h>
  6 #include<netinet/ip.h>
  7 #include<sys/socket.h>
  8 #include<sys/types.h>
  9 #include<sys/time.h>
 10 #include<arpa/inet.h>
 11 #include<sys/select.h>
 12 #include<assert.h>
 13 
 14 #ifndef  EXIT_SUCCESS
 15 #define EXIT_SUCCESS 0
 16 #endif
 17 
 18 #ifndef  EXIT_FAILURE
 19 #define EXIT_FAILURE -1
 20 #endif
 21 
 22 #define Max_connect 2048    //usually is SOMAXCONN =128 ,can be change
 23 
 24 #define maxn 1024
 25 #define Port 5567
 26 #define Ser_addr "192.168.132.128"
 27 
 28 #define ERROR_EXIT( inf ) \
 29   do{    \
 30      perror( inf );  \
 31      printf("it happened in %d \n", __LINE__); \
 32      exit(-1); \
 33   }while(0);
 34 
 35 #define  Waring( inf ) \
 36 do{    \
 37      perror( inf );  \
 38      printf("it happened in %d \n", __LINE__); \
 39   }while(0);
 40 
 41 
 42 int fds[Max_connect];
 43 int cnt = 1;
 44 
 45 void
 46 print (int fd, const char *str)
 47 {
 48   assert (str != NULL);
 49   printf ("the fd is %d \n", fd);
 50   puts (str);
 51 }
 52 
 53 int
 54 main (int argv, char *argc[])
 55 {
 56 
 57   int ser_sfd = -1, i;
 58   struct sockaddr_in ser_addr;
 59   struct sockaddr_in client_addr;
 60   int setfd = 0, optval, maxsockfd;
 61 
 62   char rebuf[maxn], wbuf[maxn];
 63 
 64   // build a socket with ipv4 ans tcp 
 65 
 66   if ((ser_sfd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
 67     ERROR_EXIT ("socket...!");
 68 
 69   // set the type socket
 70   /*
 71      level ={ SOL_SOCKET ,IPPROTO_TCP}
 72      setsockopt is to cancle the jiangsi process
 73    */
 74 
 75   printf ("ser_sfd = %d  \n", ser_sfd);
 76   if (setsockopt (ser_sfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof (optval))
 77       < 0)
 78     ERROR_EXIT ("setsockopt...!");
 79 
 80   memset (&client_addr, 0, sizeof (client_addr));
 81   memset (&ser_addr, 0, sizeof (ser_addr));
 82   ser_addr.sin_family = AF_INET;
 83   ser_addr.sin_port = htons (Port);
 84   ser_addr.sin_addr.s_addr = htonl (INADDR_ANY);
 85 
 86   if (bind (ser_sfd, (struct sockaddr *) &ser_addr, sizeof (ser_addr)) < 0)
 87     ERROR_EXIT ("bind..!");
 88 
 89   if (listen (ser_sfd, Max_connect) < 0)
 90     ERROR_EXIT ("listen...!");
 91 
 92   //user the select moduel
 93   memset (fds, 0, sizeof (fds));
 94   fd_set fdset, wfd;
 95   maxsockfd = ser_sfd;        //max socket fd
 96 
 97   struct timeval tout;
 98 
 99   tout.tv_sec = 15;
100   tout.tv_usec = 0;
101 
102 
103   while (1)
104     {
105 
106       FD_ZERO (&fdset);        //clear  
107       //FD_ZERO (&wfd);
108 
109       FD_SET (ser_sfd, &fdset);    //bind
110       //FD_SET (ser_sfd, &wfd);
111 
112       struct timeval tout;
113 
114       tout.tv_sec = 15;
115       tout.tv_usec = 0;
116 
117 
118       for (i = 0; i < cnt; i++)
119     {
120       if (fds[i] != 0)
121         FD_SET (fds[i], &fdset);
122     }
123 
124       int tag = select (maxsockfd + 1, &fdset, NULL, NULL, &tout);
125 
126       if (tag == 0)
127     {
128       Waring ("select wait timeout !");
129       continue;
130     }
131       else if (tag == -1)
132     ERROR_EXIT ("Error select ...!");
133 
134       //lunxun select
135       for (i = 0; i < cnt; i++)
136     {
137 
138       if (FD_ISSET (fds[i], &fdset))
139         {
140 
141           int len = recv (fds[i], rebuf, sizeof (rebuf), 0);
142           if (len <= 0)
143         {
144 
145           printf ("%d: \n", fds[i]);
146           close (fds[i]);
147           FD_CLR (fds[i], &fdset);
148           Waring ("client is closed !");
149           continue;
150         }
151 
152           printf ("the client_ip : %s\n",
153               inet_ntoa (client_addr.sin_addr));
154 
155           print (fds[i], rebuf);
156 
157           send (fds[i], rebuf, sizeof (rebuf), 0);    //hui she
158           memset (rebuf, 0, sizeof (rebuf));
159         }
160     }
161       //if have a new connect happened
162       //  memset(&client_addr , 0 ,sizeof(client_addr)); 
163       if (FD_ISSET (ser_sfd, &fdset))
164     {
165       // memset(&client_addr , 0 ,sizeof(client_addr));
166       int acplen = sizeof (client_addr);
167       int acp = accept (ser_sfd, (struct sockaddr *) &client_addr,
168                 &acplen);
169 
170       printf ("accept return acp=%d \n", acp);
171 
172       if (acp < 0)
173         {
174 
175           Waring ("waring accept acp<=0!");
176           continue;
177         }
178       //add to arr
179       if (cnt < maxn)
180         fds[cnt++] = acp;
181       else
182         {
183           ERROR_EXIT ("cnt>maxn");
184         }
185       if (acp > maxsockfd)
186         maxsockfd = acp;
187     }
188     }
189 
190   for (i = 0; i < cnt; i++)
191     {
192       close (fds[i]);
193     }
194 
195   return EXIT_SUCCESS;
196 }

makefile文件:

代码语言:javascript
复制
 1 .SUFFIXES: .o.c 
 2 CC =gcc
 3 SRC =  server.c
 4 OBJ = $(SRC: .c =.o) 
 5 BIN = Sez_Server
 6 
 7 
 8 .PHONY: start
 9 start:  $(OBJ)
10     $(CC) -o $(BIN) $(OBJ)
11 .o.c: $(SRC)
12     $(CC) -g -Wall $@ -c  $<
13 .PHONY: clean
14 clean:
15     rm -f $(OBJ)

客户端:

代码语言:javascript
复制
 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stdlib.h>
 4 #include<netinet/in.h>
 5 #include<arpa/inet.h>
 6 #include<sys/socket.h>
 7 #include<sys/types.h>
 8 #include<assert.h>
 9 #include<unistd.h>
10 #ifndef EXIT_FAILURE
11 #define EXIT_FAILURE  -1    //exit failure
12 #endif
13 #ifndef EXIT_SUCCESS
14 #define EXIT_SUCCESS   0    // exit sucessful
15 #endif
16 
17 #define Port 5567
18 #define IPADDR  "192.168.132.128"
19 #define maxn 1024
20 
21 #define ERROR_EXIT( inf ) \
22  do{  \
23    perror( inf ); \
24    printf("it's happened in %d \n",__LINE__); \
25    }while(0) ;
26 
27 
28  //use the function to connect the server !!
29 
30 
31 int
32 main (int argv, char *argc[])
33 {
34 
35   int sfd = -1;
36   char rbuf[maxn], wbuf[maxn];
37   // int sfd=-1 ; //socket_fd
38   int confd = -1;        //connect_fd
39   struct sockaddr_in soaddr;
40   if ((sfd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
41     {
42       ERROR_EXIT ("socket");
43       return EXIT_FAILURE;
44     }
45 
46   //memset  a struct    
47   memset (&soaddr, 0, sizeof (soaddr));
48 
49   //int the struct sockaddr_in
50 
51   soaddr.sin_family = AF_INET;
52   soaddr.sin_port = htons (Port);
53   soaddr.sin_addr.s_addr = inet_addr (IPADDR);
54 
55   if ((confd =
56        connect (sfd, (struct sockadrr *) &soaddr, sizeof (soaddr))) < 0)
57     {
58       ERROR_EXIT ("connect");
59       return EXIT_FAILURE;
60     }
61 
62   printf ("connect is sucessful !\n");
63 
64   while (fgets (wbuf, sizeof (rbuf), stdin) != NULL)
65     {
66       write (sfd, wbuf, strlen (wbuf));
67       read (sfd, rbuf, sizeof (rbuf));
68       fputs (rbuf, stdout);
69     }
70 
71   close (sfd);
72   close (confd);
73   return EXIT_SUCCESS;
74 }

makefile文件:

  1. 效果图:
代码语言:javascript
复制
 1 .SUFFIXES: .o.c 
 2 CC =gcc
 3 SRC = client.c
 4 OBJ = $(SRC: .c =.o) 
 5 BIN = Se_client
 6 
 7 
 8 .PHONY: start
 9 start:  $(OBJ)
10     $(CC) -o $(BIN) $(OBJ)
11 .o.c: $(SRC)
12     $(CC) -g -Wall $@ -c  $<
13 .PHONY: clean
14 clean:
15     rm -f $(OBJ)
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2015-08-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档