多路IO複用模型select函數使用實例

linux中IO多路複用技術主要有三種:select 、poll 、epoll。

1. 什麼是多路IO複用:

其實就是通過一種機制,可以監視多個描述符(Linux中一切皆文件,打開一個文件就需要一個文件描述符,同樣,在套接字中建立一個連接也需要一個描述符,這個描述符在計算機中是有個數限制的),一旦某個描述符可讀或可寫或某些我們需要的狀態發生變化,則這種機制就能通知程序,從而進行讀寫或其它操作。

本節只做select函數說明,poll、epoll將在之後說明

2. 網絡多路IO複用模型之select

使用select可以完成非阻塞方式工作的程序,它能夠監測需要被監視的文件描述符的變化情況---讀寫或者異常。但是select監視文件描述符的個數是固定的(1024/2048)。

select函數原型:

 int  select(int nfd, fd_set *readfd, fd_set *writefd,fd_set *errfd, struct timeval *timeout);

maxfd :  指明需要監視所有文件描述符的最大值,並且要+1

readfd:   是一個指向fd_set結構的指針,fd_set可以理解爲一個集合,該集合中存放一些文件描述符,這個變量裏存放了一些要監視讀變化的描述符。

writefd:  同readfd,只不過,這裏的文件描述符監視的是寫變化的(如果有一個文件可寫,則select返回大於0的數)

errfd:  同理,監視發生錯誤的文件描述符號

timeout:   設置超時時間,主要有三種狀態,A.傳入NULL,一直阻塞,直到狀態變化  B.傳入0,則一直返回,無變化爲0,有變化爲正值,C.大於0,則是超時等待的時間,狀態無變化,則超時返回,返回值爲0,否則爲正值

3. 示例,使用select提高服務器處理能力(服務端代碼)

  int func_select(int serverfd)
 {//serverfd爲服務端套接字描述符,該函數實現服務端監聽客戶端連接,並監聽已連接客戶端的發送數據
     fd_set client_fdset;        //監聽文件描述符集合
     int maxsock = serverfd;     //文件描述符中最大文件描述符號
     struct timeval timeout;     //超時時間
     int client_fd[5] = {0};     //已連接客戶端套接字
     int connectcount = 0;       //描述符數量
     char buff[1024];
     int ret = 0;
     const int connecte = 10;    //可連接最大數

     while(1)
     {
         FD_ZERO(&client_fdset);             //初始化集合
         FD_SET(serverfd,&client_fdset);     //將該描述符加入到描述符集合中
         timeout.tv_sec = 30;                //設置select超時時間
         timeout.tv_usec = 0;

         for(int i = 0;i<connecte;i++)       //將可能存在的描述符加入監聽
         {
             if(client_fd[i] != 0)
            {
                 FD_SET(client_fd[i],&client_fdset);
             }
         }

         /*select只監聽可讀狀態*/
         ret = select(maxsock+1,&client_fdset,NULL,NULL,&timeout);
         if(ret == -1)
             break;
         else if(ret == 0)
             continue;

         //遍歷已經連接描述符,檢查是否有可都狀態
         for(int i = 0;i<connectcount;i++)
         {
             if(FD_ISSET(client_fd[i],&client_fdset))  //判斷當前描述符是否加入監視集合
             {
                 ret = recv(client_fd[i],buff,1024,0);
                 if(ret <= 0)
                 {
                     close(client_fd[i]);
                     FD_CLR(client_fd[i],&client_fdset);
                     client_fd[i] = 0;
                 }
                 else
                 {
                     printf("%d -->recv data %s\n",i,buff);
                 }
            }
         }

         if(FD_ISSET(serverfd,&client_fdset)) //檢查是否有新的連接
         {
             struct sockaddr_in  addr_cl;
             size_t  socksize = sizeof(struct sockaddr_in);
             int sockclient = accept(serverfd,(struct sockaddr*)&addr_cl,&socksize);
             if(sockclient <= 0)
             {
                 printf("accept err!");
                 continue;
             }

             if(connectcount < connecte)
             {
                 client_fd[connectcount++] = sockclient; //保存新連接,方便加入描述符集合
                 bzero(buff,1024);
                 strcpy(buff,"this is server! welcome!\n");
                 send(sockclient,buff,1024,0);

                 bzero(buff,sizeof(buff));
                 ret = recv(sockclient,buff,1024,0);
                 if(ret < 0)
                 {
                     printf("recv error!");
                     close(sockclient);
                     return 0;
                 }
                 printf("recv data:%s\n",buff);

                 if(sockclient > maxsock)
                 {
                     maxsock = sockclient;   //記錄最大套接字描述符
                 }
                 else
                 {
                     printf("max connect ::quit:::\n");
                 }
             }
         }
     }
     
     return 0;
 }

上面代碼主要介紹瞭如何使用select函數來實現服務端的併發能力,當用戶調用了select,則當前進程被阻塞,內核會檢測select集合中的套接字描述符,如果有任何一個要監聽的狀態發生變化,則select會返回。

select的優勢並不是對單個連接處理的更快,所以,連接數太少的化,性能可能比多線程阻塞IO更低,但是它的優點是處理很多連接。

select的優點:
1.可以監視多個文件描述符,減少了平均等待時間
2.處理大批量連接時,有效減輕進程調度的壓力
select缺點:
1.監聽文件描述符有上限,這個上限是由fd_set決定的。
2.它返回的只是就緒事件的個數,要判斷是那個事件滿足,需要遍歷文件描述符。
3.select監聽的集合是輸入輸出參數,每次監聽都需要重新初始化。
4.每次調用select,都需要把fd集合從用戶態拷貝到內核態,這個開銷在fd很多時會很大
5.內核採用遍歷fd集合的方式來檢測就緒事件,這個開銷在fd很多時也很大
6.select和poll都只能工作在低效的LT(水平觸發)模式

 

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