Linux中select函數的使用 select() Linux linux函數 select

來源:互聯網
上載者:User

阻塞式I/O編程有兩個特點:

一、如果一個發現I\O有輸入,讀取的過程中,另外一個也有了輸入,這時候不會產生任何反應.這就需要你的程式語句去用到select函數的時候才知道有資料輸入。

二、程式去select的時候,如果沒有資料輸入,程式會一直等待,直到有資料位元置,也就是程式中無需迴圈和sleep。

 

Select在Socket編程中還是比較重要的,可是對於初學Socket的人來說都不太愛用Select寫程式,他們只是習慣寫諸如connect、accept、recv或recvfrom這樣的阻塞程式

(所謂阻塞方式block,顧名思義,就是進程或是線程執行到這些函數時必須等待某個事件的發生,如果事件沒有發生,進程或線程就被阻塞,函數不能立即返回)。

可是使用Select就可以完成非阻塞(所謂非阻塞方式non-block,就是進程或線程執行此函數時不必非要等待事件的發生,一旦執行肯定返回,以傳回值的不同來反映函數的執行情況,如果事件發生則與阻塞方式相同,若事件沒有發生則返回一個代碼來告知事件未發生,而進程或線程繼續執行,所以效率較高)方式工作的程式,它能夠監視我們需要監視的檔案描述符的變化情況——讀寫或是異常。

下面man下select函數的:

/* According to POSIX.1-2001 */

      #include <sys/select.h>

      /* According to earlier standards */

      #include <sys/time.h>

      #include <sys/types.h>

      #include <unistd.h>

      int select(int nfds, fd_set *readfds, fd_set *writefds,

                  fd_set *exceptfds, structtimeval *timeout);

第一,   struct fd_set可以理解為一個集合,這個集合中存放的是檔案描述符(filedescriptor),即檔案控制代碼,這可以是我們所說的普通意義的檔案,當然Linux下任何裝置、管道、FIFO等都是檔案形式,全部包括在內,所以毫無疑問一個socket就是一個檔案,socket控制代碼就是一個檔案描述符。

fd_set集合可以通過一些宏由人為來操作,

比如

清空集合FD_ZERO(fd_set*);

將一個給定的檔案描述符加入集合之中FD_SET(int,fd_set *);

將一個給定的檔案描述符從集合中刪除FD_CLR(int,fd_set*);

檢查集合中指定的檔案描述符是否可以讀寫FD_ISSET(int,fd_set* )。
第二,structtimeval是一個大家常用的結構,用來代表時間值,有兩個成員,一個是秒數,另一個是毫秒數。

具體解釋select的參數:

int maxfdp是一個整數值,是指集合中所有檔案描述符的範圍,即所有檔案描述符的最大值加1,不能錯!

 
fd_set *readfds是指向fd_set結構的指標,這個集合中應該包括檔案描述符,我們是要監視這些檔案描述符的讀變化的,即我們關心是否可以從這些檔案中讀取資料了,如果這個集合中有一個檔案可讀,select就會返回一個大於0的值,表示有檔案可讀,如果沒有可讀的檔案,則根據timeout參數再判斷是否逾時,若超出timeout的時間,select返回0,若發生錯誤返回負值。可以傳入NULL值,表示不關心任何檔案的讀變化。

fd_set *writefds是指向fd_set結構的指標,這個集合中應該包括檔案描述符,我們是要監視這些檔案描述符的寫變化的,即我們關心是否可以向這些檔案中寫入資料了,如果這個集合中有一個檔案可寫,select就會返回一個大於0的值,表示有檔案可寫,如果沒有可寫的檔案,則根據timeout參數再判斷是否逾時,若超出timeout的時間,select返回0,若發生錯誤返回負值。可以傳入NULL值,表示不關心任何檔案的寫變化。

fd_set *errorfds同上面兩個參數的意圖,用來監視檔案錯誤異常。

struct timeval* timeout是select的逾時時間,這個參數至關重要,它可以使select處於三種狀態,

第一,   若將NULL以形參傳入,即不傳入時間結構,就是將select置於阻塞狀態,一定等到監視檔案描述符集合中某個檔案描述符發生變化為止;

第二,   第二,若將時間值設為0秒0毫秒,就變成一個純粹的非阻塞函數,不管檔案描述符是否有變化,都立刻返回繼續執行,檔案無變化返回0,有變化返回一個正值;

第三,   timeout的值大於0,這就是等待的逾時時間,即select在timeout時間內阻塞,逾時時間之內有事件到來就返回了,檔案無變化返回0,有變化返回一個正值;

傳回值:

負值:select錯誤

正值:某些檔案可讀寫或出錯

 0:等待逾時,沒有可讀寫或錯誤的檔案

舉個簡單的例子:

#include <sys/time.h>

#include <sys/types.h>

#include <unistd.h>

#include <sys/select.h>

#include<stdio.h>

#include<stdlib.h>

int main()

{

         fd_setreadfds;

         FD_ZERO(&readfds);

         FD_SET(STDIN_FILENO,&readfds);

         intret;

         charbuf[256]={0};

         structtimeval  tv={5,1000};

         //       tv.tv_sec=5;

         //       tv.tv_usec=1000;

 

         ret=select(STDIN_FILENO+1,&readfds,NULL,NULL,&tv);

         //ret=select(STDIN_FILENO+1,&readfds,NULL,NULL,NULL);

         //ret=select(STDIN_FILENO+1,&readfds,NULL,NULL,0);

         printf("ret=%d\n",ret);

         if(ret==-1)

         {

                   perror("selsecterror ");

                   exit(EXIT_FAILURE);

         }

         elseif(ret)

         {

                   if(FD_ISSET(STDIN_FILENO,&readfds))

                            read(STDIN_FILENO,buf,256);

                   printf("readfrom stdin msg : %s\n",buf);

         }

         else

                   printf("timeout\n");

 

         return0;

 

}

運行結果:3中狀態:

1.    錯誤。

2.    逾時,在指定的時間內沒有檢測到使用者的輸入。Selsect()返回0

                             

3.使用者輸入了,即檢測到了標準輸入已經準備好了

 

下面我們看一下socket編程中select()函數的使用。

在此之貼出select函數部分。

用戶端:

while(1)
    {
         FD_ZERO(&readfds);
        FD_SET(sockfd,&readfds);
        FD_SET(STDIN_FILENO,&readfds);
        maxfd=sockfd;
        printf("before select %d\n",readfds);
      if((ret=select(maxfd+1,&readfds,NULL,NULL,NULL))==-1)
      { 
          perror("select:");
          exit(EXIT_FAILURE);
      }
      //哪個檔案描述符準備好了就將那個所對應的位設定為1,其他的設定為0
      //eg:socket=3準備好了則readfds為00000100(8)
      //select 函數的傳回值為:準備好的檔案描述符的個數。
         DEBUG("ret=%d\n",ret);
        printf("after select %d\n",readfds);
  
     if(FD_ISSET(sockfd,&readfds))
        { printf("sockfd select %d\n",readfds);
           if(read(sockfd,read_buf,sizeof(read_buf))<=0)
           {
             perror("read <=0 ");
             break;
           }
          printf("%s\n",read_buf);
          memset(read_buf,0,sizeof(read_buf));
        }    
        if(FD_ISSET(STDIN_FILENO,&readfds)) //有標準輸入描述符準備好了
        {  printf("STDIN_FILENO select %d\n",readfds);
         
           read(STDIN_FILENO,buf,sizeof(buf));   
          //或 scanf("%s",buf);//從標準輸入裝置的緩衝區中取出內容放到buf中
          write(sockfd, buf, strlen(buf)+1);
        }
         
    }

服務端:

void server_write_read(int sockfd)
{

      fd_set  rfds;
    FD_ZERO(&rfds); 
    FD_SET(sockfd,&rfds);
    int i, maxfd = sockfd,ret=-1;
    int newfd;
    struct sockaddr_in client;
   socklen_t len = sizeof(client);
   char buf[MAXBUF] = {'\0'};
    while(1)
    {
             FD_ZERO(&rfds);  //clear set
             for(i = 0; i < MAXCLIENT; i++)
           {
              // add fd to set
              if(connfd[i].fd != 0)
              {
                FD_SET(connfd[i].fd ,&rfds);
              }
              //get tje max fd
              if(connfd[i].fd != 0  &&connfd[i].fd> maxfd)
              {
                maxfd=connfd[i].fd;
               
              }
            }
         FD_SET(sockfd,&rfds);
         printf("server_write_read  before select %d\n",rfds);
         if((ret=select(maxfd+1,&rfds,NULL,NULL,NULL))==-1)
       { 
           perror("select:");
           continue;
       }
       printf("server_write_read  after select %d\n",rfds);
       if(FD_ISSET(sockfd, &rfds))
       {       printf("server_write_read  sockfd %d\n",rfds);
           newfd = accept(sockfd, (struct sockaddr *)&client, &len);
           if(-1 == newfd)
            system_error("accept");
            else
             printf("welcome  %s was connected.\n", inet_ntoa(client.sin_addr));
          DEBUG("newfd=%d\n",newfd);
           //save socket description
           for(i = 0; i < MAXCLIENT; i++)
           {
              if(connfd[i].fd == 0)
              {
                 connfd[i].fd = newfd;
                 break;
              }
            }
        
     }
  
   for(i = 0; i < MAXCLIENT; i++)
   { 
      if(FD_ISSET(connfd[i].fd, &rfds))
      {
            if(connfd[i].fd == 0)continue;
         if ((read(connfd[i].fd, buf, MAXBUF)) <= 0)
           {DEBUG("server read <=0\n");
             for(i = 0; i < MAXCLIENT; i++)
             {
               if(connfd[i].fd==newfd)
                {
                  connfd[i].fd=0;
                  break;
                }
             }
             close(newfd);
             FD_CLR(newfd, &rfds);
           }
           else
           {
               int j=0;
               printf("////////// buf=%s\n",buf);
              for(; chat_func[j].func; j++)
              {
                if (buf[0] == chat_func[j].protocol)
                {
                 chat_func[j].func(buf, connfd[i].fd);
                 break;
                }
             }
             memset(buf,0,MAXBUF); 
           } 
      }
    
    }
       
   }
  
}

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.