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

 

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

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

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

        我的理解:

 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);

    对于

   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
第三 ,四个参数: 同上
第五个参数:为一个定义超时的结构体
1        struct timeval {
2                time_t         tv_sec;     /* seconds */
3                suseconds_t    tv_usec;    /* microseconds */
4            };

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

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

    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

void FD_SET(int fd, fd_set *set);

        将fd值 和set绑定 

void FD_ZERO(fd_set *set);

   将set集全部清除

 

简单的例子:

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

 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 }
View Code

makefile文件:

    

 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模型

   

  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文件:
  

 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)

客户端:

    

 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.     
     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)

    效果图:

  2.   

     

posted @ 2015-08-06 11:12  龚细军  阅读(5291)  评论(5编辑  收藏  举报