Monday, December 27, 2010

FD_SET、FD_ISSET 使用型態

FD_SET、FD_ISSET 型態

    select()函數主要是建立在fd_set型態的基礎上的fd_set是一組檔案描述元(fd)的集合,它用一位來表示一個fd(下面會仔細介紹),對於fd_set型態通過下面四個Define來操作:

        fd_set set;

   
    FD_ZERO(&set); /* 將set清為零,使集合中不含任何fd*/

        FD_SET(fd, &set); /* 將fd 加入set 集合*/

        FD_CLR(fd, &set); /* 將fd從set集合中清除*/

        FD_ISSET(fd, &set); /* 測試fd是否在set集合中*/



過 去,一個 fd_set 通常只能包含<32的fd(檔案描述元),因為 fd_set 其實只用了一個32位向量來表示 fd;現在,UNIX 系統通常會在inlcude<sys/select.h>中定義常數 FD_SETSIZE,它是資料型態 fd_set 的描述字數量,其值通常是 1024,這樣就能表示 <1024 的 fd。

    根據 fd_set的 位向量實現,我們可以重新理解操作 fd_set 的四個 Define:


        fd_set set;



        FD_ZERO(&set);  /*將set的所有位置0,如set在記憶體中佔8位則將set置為00000000*/



        FD_SET(0, &set);  /* 將set的第0位置1,如set原來是00000000,則現在變為10000000,這樣fd==1的檔案描述元就被加進set中了*/



        FD_CLR(4, &set);  /*將set的第4位置0,如set原來是10001000,則現在變為10000000,這樣fd==4的檔案描述元就被從set中清除了*/



        FD_ISSET(5, &set);  /* 測試set的第5位是否為1,如果set原來是10000100,則返回非零,表明fd==5的檔案描述元在set中;否則返回0*/



―――――――――――――――――――――――――――――――――――――――
注意fd的最大值必須<FD_SETSIZE
―――――――――――――――――――――――――――――――――――――――
select函數的接口比較簡單:

    int select(int nfds, fd_set *readset, fd_set *writeset,fd_set* exceptset, struct timeval *timeout);


功能:
        測試指定的fd可讀,可寫,有異常條件待處裡?



參數: 
  • nfds


    需 要檢查的檔案描述元個數(即檢查到 fd_set 的第幾位),數值應該比三組  fd_set  中所含的最大  fd  值更大,一般設為三組  fd_set  中所含的最大 fd 值加1(如在readset,writeset,exceptset中所含最大的fd為5,則nfds=6,因為fd是從0開始的)。設這個值是為提高效 率,使函數不必檢查 fd_set 的所有1024位。


    (1)readset - 用來檢查可讀性的一組檔案描述元。

    (2)writeset - 用來檢查可寫性的一組檔案描述元。

    (3)exceptset - 用來檢查是否有異常條件出現的檔案描述元。 (注:錯誤不包括在異常條件之內)



  • timeout
有三種可能:

  1. timeout=NULL(阻塞:直到有一個fd位被置為1函數才返回) 
  2. timeout所指向的結構設為非零時間(等待固定時間:有一個fd位被置為1或者時間耗盡,函數均返回) 
  3. timeout所指向的結構,時間設為0(非阻塞:函數檢查完每個fd後立即返回)

返回值:返回對應位仍然為1的fd的總數。

Remarks:     

三組 fd_set 均將某些 fd 位置0,只有那些可讀,可寫以及有異常條件待處裡的 fd 位仍然為1。

    使用 select 函數的過程一般是:
        先 調用 DefineFD_ZERO 將指定的 fd_set 清零,然後調用 DefineFD_SET 將需要測試的 fd 加入 fd_set,接著調用函數 select 測試 fd_set 中的所有 fd,最後用 DefineFD_ISSET 檢查某個 fd 在函數 select 調用後,相應位是否仍然為1。

以下是一個測試單個檔案描述元可讀性的例子:

     int isready(int fd)
     {
         int rc;
         fd_set fds;
         struct timeval tv;
         FD_ZERO(&fds);
         FD_SET(fd,&fds);
         tv.tv_sec = tv.tv_usec = 0;
      rc = select(fd+1, &fds, NULL, NULL, &tv);
         if (rc < 0) //error
           return -1;
         return FD_ISSET(fd,&fds) ? 1 : 0;
     }
 
下面還有一個複雜一些的應用:
    //這段代碼將指定測試Socket的描述字的可讀可寫性,因為Socket使用的也是fd。
uint32 SocketWait(TSocket *s,bool rd,bool wr,uint32 timems)
{
     fd_set rfds,wfds;
#ifdef _WIN32
     TIMEVAL tv;
#else
     struct timeval tv;
#endif /* _WIN32 */
     FD_ZERO(&rfds);
     FD_ZERO(&wfds);
     if (rd) //TRUE
          FD_SET(*s,&rfds); //添加要測試的描述字
     if (wr) //FALSE
          FD_SET(*s,&wfds);
     tv.tv_sec=timems/1000; //second
     tv.tv_usec=timems%1000; //ms
     for (;;) //如果errno==EINTR,反复測試緩衝區的可讀性
          switch(select((*s)+1,&rfds,&wfds,NULL,
(timems==TIME_INFINITE?NULL:&tv))) //測試在規定的時間內套接口接收緩衝區中是否有資料可讀
         { //0--超時,-1--出錯
         case 0: /* time out */
              return 0;
         case (-1): /* socket error */
              if (SocketError()==EINTR)
                   break;
              return 0; //有錯但不是EINTR
          default:
              if (FD_ISSET(*s,&rfds)) //如果s是fds中的一員返回非0,否則返回0
                   return 1;
              if (FD_ISSET(*s,&wfds))
                   return 2;
              return 0;
         };

 

轉載於:http://jiaxingkui123.blog.163.com/blog/static/8756468420081111102917528

No comments:

Post a Comment