IO多路複用之select

一.概念

      select系統調用是用來讓程序監視多個文件句柄的狀態變化。程序會停在select這裏等待,直到被監視的文件句柄有一個或多個的狀態發生了改變。關於文件句柄,其實就是一個整數,我們最熟悉的句柄是0、1、2三個,0是標準輸入,1是標準輸出,3是標準錯誤。0、1、2是整數表示的,對應的FILE*結構的表示是stdin、stdout、stderr。

二、select的參數說明wKiom1eMvUrw7FIZAADA5TjXruI314.png

    參數說明:

    (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的等待時間。其結構如下:

       wKioL1eMwavBVQe2AAAfVj7YBoU559.png

    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 }


執行結果:

wKioL1eMz1aj_vOlAABoBoPKIcY779.png


wKiom1eMz3-S2LBRAABAmUaThpA308.png

    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章