select函數的作用!

select()的機制中提供一fd_set的數據結構,實際上是一long類型的數組,
每一個數組元素都能與一打開的文件句柄(不管是Socket句柄,還是其他
文件或命名管道或設備句柄)建立聯繫,建立聯繫的工作由程序員完成,
當調用select()時,由內核根據IO狀態修改fd_set的內容,由此來通知執
行了select()的進程哪一Socket或文件可讀。

有時,select()也被用來當作延時函數使用。sleep()延時會釋放cpu,用select的話,可以在佔用cpu的情況下,延時

int select(
int nfds,
fd_set* readfds,
fd_set* writefds,
fd_set* exceptfds,
const struct timeval* timeout
);

參數:

nfds    

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

readset   

     用來檢查可讀性的一組文件描述字。

writeset

     用來檢查可寫性的一組文件描述字。

exceptset

     用來檢查是否有異常條件出現的文件描述字。(注:錯誤不包括在異常條件之內)

timeout

有三種可能:

1.        timeout="NULL"(阻塞:直到有一個fd位被置爲1函數才返回)

2.        timeout所指向的結構設爲非零時間(等待固定時間:有一個fd位被置爲1或者時間耗盡,函數均返回)

3.        timeout所指向的結構,時間設爲0(非阻塞:函數檢查完每個fd後立即返回)


返回值:     

返回對應位仍然爲1的fd的總數。

四個宏來操作: 完全一點 從accept開始.


    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系統通常會在頭文件中定義常量 FD_SETSIZE,它是數據類型fd_set的描述字數量,其值通常是1024,這樣就能表示<1024的fd。根據fd_set的位矢量實 現,我們可以重新理解操作fd_set的四個宏:


    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*/

首先:
SOCKET sock;
sock= socket(AF_INET,SOCK_STREAM,0);

struct sockaddr_in addr;      //告訴sock 應該再什麼地方licence
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port=htons(11111);   //端口啦
addr.sin_addr.s_addr=htonl(INADDR_ANY);            //在本機的所有ip上開始監聽

bind (sock,(sockaddr *)&addr,sizeof(addr));//bind....

listen(sock,5);                               ;//最大5個隊列

SOCKET socka;                         //這個用來接受一個連接
fd_set rfd;                                   // 描述符集 這個將用來測試有沒有一個可用的連接
struct timeval timeout;

FD_ZERO(&rfd);                     //總是這樣先清空一個描述符集

timeout.tv_sec=60;                //等下select用到這個
timeout.tv_usec=0;

u_long ul="1";

ioctlsocket(sock,FIONBIO,&ul);    //用非阻塞的連接

//現在開始用select
FD_SET(sock,&rfd);    //把sock放入要測試的描述符集 就是說把sock放入了rfd裏面 這樣下一步調用select對rfd進行測試的時候就會測試sock了(因爲我們將sock放入的rdf) 一個描述符集可以包含多個被測試的描述符,
if(select(sock+1,&rfd,0,0, &timeout)==0)   // select的第一個參數是可以忽略的(這樣寫是爲了保持和linux下一致) 第二個參數放入需要測試的讀描述符集(也就是說如果這裏面有一個描述符可以讀取了,select就返回) 第三個放入需要測試的寫描述符集,第四個放入"可執行描述符集"(??我也不知道) 第五個參數是超時時間(如果過了這個超時時間依然沒有描述符準備好,select也返回.(如果爲NULL,那就一直等到一個描述符集變成準備好的狀態)
{ //這個大括號接上面的,返回0那麼就超過了timeout預定的時間

//處理....

}

if(FD_ISSET(sock,&rfd))
{      //有一個描述符準備好了

socka=accept(sock,0,0);     //好了 接受它吧

//你還要判斷一下socka是不是有效的socket才行....

-------------------------------------------------------------------------------------------------------------------------------

一般的情況下

假設你要判斷兩個socket 是否可讀可寫 那就這樣:

假設 socka 和sockb 是兩個socket 他們已經被連接上,並且能夠收發數據

fd_set rfd,wfd;//一個用來測試讀 一個用來測試寫

FD_ZERO(&rfd);

FD_ZERO(&wfd);

FD_SET(socka,&rfd);//把socka放入讀描述符集

FD_SET(sockb,&rfd);//把sockb放入讀描述符集

FD_SET(socka,&wfd);把socka放入寫描述符集

FD_SET(sockb,&wfd);把sockb放入寫描述符集

if(SOCKET_ERROR!=select(0,&rfd,&wfd,0,0))      //測試這兩個描述符集,永不超時 其中rfd只用來測試讀 wfd只用來測試寫

{      //沒有錯誤

if(FD_ISSET(socka,&rfd))    //socka可讀

{...}

if(FD_ISSET(sockb,&rfd)   //sockb可讀

{...}

if(FD_ISSET(socka,&wfd) //socka 可寫

{...}

if(FD_ISSET(sockb,&wfd) //sockb可寫

{...}

}


.............................................................................................


下面是linux環境下select的一個簡單用法

#i nclude
#i nclude
#i nclude
#i nclude
#i nclude
#i nclude

int main ()
{
int keyboard;
int ret,i;
char c;
fd_set readfd;
struct timeval timeout;
keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK);
assert(keyboard>0);
while(1)
    {
timeout.tv_sec=1;
timeout.tv_usec=0;
FD_ZERO(&readfd);
FD_SET(keyboard,&readfd);
ret=select(keyboard+1,&readfd,NULL,NULL,&timeout);
if(FD_ISSET(keyboard,&readfd))
    {
      i="read"(keyboard,&c,1);
          if(''\n''==c)
          continue;
      printf("hehethe input is %c\n",c);
    
       if (''q''==c)
      break;
      }
}
}
用來循環讀取鍵盤輸入

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