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;
}
}
}
用來循環讀取鍵盤輸入