一.概念

      select系统调用是用来让程序监视多个文件句柄的状态变化。程序会停在select这里等待,直到被监视的文件句柄有一个或多个的状态发生了改变。关于文件句柄,其实就是一个整数,我们最熟悉的句柄是0、1、2三个,0是标准输入,1是标准输出,3是标准错误。0、1、2是整数表示的,对应的FILE*结构的表示是stdin、stdout、stderr。

二、select的参数说明IO多路复用之select_其他

    参数说明:

    (1)nfds:需要监视的最大文件描述符+1;

    (2)rdset、wrset、exset:分别对应需要检测的可读文件描述符的集合,可写文件描述符的集合,异常文件描述符的集合。

    (3)struct timeval:这个结构用于描述一段时间长度。如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。

    (4)处理三组描述词组的方式

    void FD_CLR(int fd,fd_set* set);  用来清除描述词组set中相关fd的位

    int FD_ISSET(int fd,fd_set* set);  用来测试描述词组set中相关fd的位是否为真

    void FD_SET(int fd,fd_set* set);  用来设置描述词组set中相关fd的位

    void FD_ZERO(fd_set* set);   用来清除描述词组set中所有的位

    (5)timeout:为结构timeval,用来设置select的等待时间。其结构如下:

       IO多路复用之select_其他_02

    a、当timeout参数设置为NULL:表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了事件。

    b、当timeout参数设置为0:只检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。

    c、当timeout参数设置为特定的时间值:如果在指定的时间段内没有事件发生,则select将超时返回。

三、select函数的返回值分析

    (1)执行成功:返回文件描述词状态已改变的个数。

    (2)返回0:在描述词状态改变前已超过timeout时间,没有返回。

    (3) 返回-1:表示错误。错误原因存于errno中,此时的参数readfds,writefds,exceptfds,timeout的值都讲变成不可预测的。错误值可能为:

            EBADF:文件描述词为无效的或者文件已关闭。

            EINTR:此调用该信号被中断。

            EINVAL:参数n为负值。

            ENOMEM:核心内存不足。

四、对select模型的理解

    取fd_set的长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。

    (1)执行fd_set set;FD_ZERO(&set):则set用位表示是0000 0000。

    (2)若fd=5,执行FD_SET(fd,&set):set变为0001 0000。

    (3)若再加入fd=2,fd=1,则set变为0001 0011。

    (4)执行select(6,&set,0,0,0)阻塞等待。

    (5)若fd=2,fd=1都发生可读事件,则select返回,此时select变为0000 0011。注意:没有发生事件的fd=5被清空。

   基于上面的说法,可以得出selec模型的特点。

        (1)可监控的文件描述符个数取决于sizeof(fd_set)的值。

        (2)强fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd。一是用于在select返回后,array作为源数据和fd_set进行FD_ISSET判断。而是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select前 都要重新从array取得fd逐一加入。扫描array的同时取得最大值maxfd,用于select的第一个参数。

        (3)select模型必须在select前循环array(加fd,取maxfd),select返回后循环array(FD_ISSET判断是否有事件发生)。

五、代码描述

server.c

  1 #include<stdio.h>                                                           
  2 #include<string.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 #include<time.h>
  6 #include<sys/types.h>
  7 #include<sys/socket.h>
  8 #include<netinet/in.h>
  9 #include<arpa/inet.h>
 10 
 11 #define _BACKLOG_ 5
 12 
 13 void Usage(const char *proc)
 14 {
 15     printf("%s[ip][port]\n",proc);
 16 }
 17 
 18 int start(char *_ip,int _port)
 19 {
 20     int sock=socket(AF_INET,SOCK_STREAM,0);
 21     if(sock<0)
 22     {
 23         perror("sock");
 24         return 1;
 25     }
 26     struct sockaddr_in local;
 27     local.sin_family=AF_INET;
 28     local.sin_port=htons(_port);
 29     local.sin_addr.s_addr=inet_addr(_ip);
 30     if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
 31     {
 32         perror("bind");
 33         return 2;
 34     }
 35     if(listen(sock,_BACKLOG_)<0)
 36     {
 37         perror("listen");
 38         return 3;
 39     }
 40     return sock;
 41 }
 42 
 43 int main(int argc,char *argv[])
 44 {
 45     if(argc!=3)
 46     {       
 47         Usage(argv[0]);
 48         //return 1;
 49     }
 50     char *ip=argv[1];
 51     int port=atoi(argv[2]);
 52     int listen_sock=start(ip,port);
 53 
 54     struct sockaddr_in cli;
 55     socklen_t len=sizeof(cli);
 56     fd_set _reads;
 57     fd_set _writes;
 58     int fds[64]={0};
 59     int fd_max=0;
 60     int fds_num=sizeof(fds)/sizeof(fds[0]);
 61     int i=0;
 62     for(i=0;i<fds_num;++i)
 63     {
 64         fds[i]=-1;
 65     }
 66     fds[0]=listen_sock;
 67     fd_max=fds[0];
 68     struct timeval timeout={5,0};  
 69     int done=0;
 70     int new_sock=-1;
 71     while(!done)
 72     {
 73         FD_ZERO(&_reads);
 74         FD_ZERO(&_writes);
 75         FD_SET(listen_sock,&_reads);
 76 
 77         timeout.tv_sec=5;
 78         timeout.tv_usec=0;
 79 
 80         for(i=1;i<fds_num;++i)
 81         {
 82             if(fds[i]>0&&i<32)
 83             {
 84                 FD_SET(fds[i],&_reads);
 85                 if(fd_max<fds[i])
 86                 {
 87                     fd_max=fds[i];
 88                 }
 89             }
 90         }
 91         switch(select(fd_max+1,&_reads,&_writes,NULL,NULL))                           92         {
 93             case 0: //timeout
 94             {
 95                 printf("timeout...");
 96                 break;
 97             }
 98             case -1: //error
 99             {
100                 perror("select");
101                 break;
102             }
103             default:
104             {
105                 for(i=0;i<fds_num;++i)
106                 {
107                     if(fds[i]==listen_sock&&FD_ISSET(fds[i],&_reads))
108                     {//listen sock is ready
109                         new_sock=accept(listen_sock,(struct sockaddr*)&cli,&    len);
110                         if(new_sock<0)
111                         {
112                             perror("accept");                                        113                             continue;
114                         }
115                         printf("get a new connect:%d\n",new_sock);
116                         for(i=0;i<fds_num;++i)
117                         {
118                             if(fds[i]==-1)
119                             {
120                                 fds[i]=new_sock;
121                                 break;
122                             }
123                         }
124                         if(i==fds_num)
125                         {
126                             close(new_sock);
127                         }
128                     }
129                     else if(fds[i]>0&&FD_ISSET(fds[i],&_reads))
130                     {//ready is success
131                         char buf[1024];
132                         memset(buf,'\0',sizeof(buf));
133                         ssize_t _size=read(fds[i],buf,sizeof(buf)-1); 
134                         if(_size<0)
135                         {
136                             perror("read");
137                             close(fds[i]);
138                             fds[i]=-1;
139                         }
140                         else if(_size==0)
141                         {
142                             printf("client close...");
143                             close(fds[i]);
144                         }
145                         else
146                         {
147                             FD_CLR(fds[i],&_reads);
148                             buf[_size]='\0';
149                             printf("client say:%s",buf);
150                             write(fds[i],buf,sizeof(buf)-1);
151                             fflush(stdout);
152                         }    
153                     }
154                     else
155                     {}
156                 }
157             }
158             break;
159 
160         }
161     }
162     return 0;
163 }


client.c

  1 #include<stdio.h>                                                           
  2 #include<string.h>
  3 #include<stdlib.h>
  4 #include<unistd.h>
  5 #include<sys/types.h>
  6 #include<sys/socket.h>
  7 #include<netinet/in.h>
  8 #include<arpa/inet.h>
  9 
 10 void Usage(const char* proc)
 11 {
 12     printf("%s [ip] [port]\n",proc);
 13 }
 14 
 15 int main(int argc,char* argv[])
 16 {
 17     if(argc!=3)
 18     {
 19         Usage(argv[0]);
 20         exit(1);
 21     }
 22     int port=atoi(argv[2]);
 23     char* ip=argv[1];
 24     int client_sock=socket(AF_INET,SOCK_STREAM,0);
 25     if(client_sock<0)
 26     {
 27         perror("socket");
 28         exit(2);
 29     }
 30 
 31     struct sockaddr_in remote;
 32     remote.sin_family=AF_INET;
 33     remote.sin_port=htons(port);
 34     remote.sin_addr.s_addr=inet_addr(ip);
 35     if(connect(client_sock,(struct sockaddr*)&remote,sizeof(remote))<0)
 36     {
 37         perror("connect");
 38         exit(3);
 39     }
 40     char buf[1024];
 41     ssize_t _s;
 42     while(1)
 43     {
 44         memset(buf,'\0',sizeof(buf)-1);
 45         printf("please input:");
 46         fflush(stdout);    
 47         fgets(buf,sizeof(buf)-1,stdin);
 48         _s=write(client_sock,buf,sizeof(buf)-1);
 49         if(_s<0)
 50         {
 51             perror("write");
 52             exit(4);
 53         }
 54         _s=read(client_sock,buf,sizeof(buf)-1);
 55         if(_s>0)
 56         {
 57             buf[_s]='\0';
 58             printf("server->client:%s",buf);
 59             fflush(stdout);
 60         }
 61         else if(_s==0)
 62         {
 63             close(client_sock);
 64         }
 65         else
 66         {
 67             perror("read");  
 68             exit(5);
 69         }
 70     }
 71     return 0;
 72 }


执行结果:

IO多路复用之select_select_03


IO多路复用之select_select_04