select()

select()

select(),本函式用於確定一個或多個套接口的狀態,對每一個套接口,調用者可查詢它的可讀性、可寫性及錯誤狀態信息,用fd_set結構來表示一組等待檢查的套接口,在調用返回時,這個結構存有滿足一定條件的套接口組的子集,並且select()返回滿足條件的套接口的數目。有一組宏可用於對fd_set的操作,這些宏與Berkeley Unix軟體中的兼容,但內部的表達是完全不同的。

基本介紹

  • 中文名:select()
  • 外文名:select()
  • 作用:多路檢測可用套接字
  • 系統:Unix、Linux
  • 返回:滿足條件的套接口的數目
簡述,注釋,返回值,錯誤代碼,調用,相關參考,事件用法,

簡述

確定一個或多個套接口的狀態,如需要則等待。
#include <sys/select.h>
int select( int nfds, fd_set FAR* readfds, fd_set * writefds, fd_set * exceptfds, const struct timeval * timeout);
nfds:是一個整數值,是指集合中所有檔案描述符的範圍,即所有檔案描述符的最大值加1,不能錯!在Windows中這個參數的值無所謂,可以設定不正確。
readfds:(可選)指針,指向一組等待可讀性檢查的套接口。
writefds:(可選)指針,指向一組等待可寫性檢查的套接口。
exceptfds:(可選)指針,指向一組等待錯誤檢查的套接口。
timeout:select()最多等待時間,對阻塞操作則為NULL。

注釋

readfds參數標識等待可讀性檢查的套接口。如果該套接口正處於監聽listen()狀態,則若有連線請求到達,該套接口便被標識為可讀,這樣一個accept()調用保證可以無阻塞完成,對其他套接口而言,可讀性意味著有排隊數據供讀取。或者對於SOCK_STREAM類型套接口來說,相對於該套接口的虛套接口已關閉,於是recv()recvfrom()操作均能無阻塞完成,writefds參數標識等待可寫性檢查的套接口。如果一個套接口正在connect()連線(非阻塞),可寫性意味著連線順利建立。如果套接口並未處於connect()調用中,可寫性意味著send()sendto()調用將無阻塞完成。〔但並未指出這個保證在多長時間內有效,特別是在多執行緒環境中〕。
exceptfds參數標識等待帶外數據存在性或意味錯誤條件檢查的套接口,請注意如果設定了SO_OOBINLINE選項為假FALSE,則只能用這種方法來檢查帶外數據的存在與否,對於SO_STREAM類型套接口,遠端造成的連線中止和KEEPALIVE錯誤都將被作為意味出錯。如果套接口正在進行連線connect()(非阻塞方式),則連線試圖的失敗將會表現在exceptfds參數中。
如果對readfds、writefds或exceptfds中任一個組類不感興趣,可將它置為空NULL。
在socket.h頭檔案中共定義了四個宏來操作描述字集。FD_SETSIZE變數用於確定一個集合中最多有多少描述字(FD_SETSIZE預設值為64,可在包含socket.h前用#define FD_SETSIZE來改變該值)。對於內部表示,fd_set被表示成一個套接口的佇列,最後一個有效元素的後續元素為INVAL_SOCKET。宏為:FD_CLR(s,*set):從集合set中刪除描述字s。FD_ISSET(s,*set):若s為集合中一員,非零;否則為零。FD_SET(s,*set):向集合添加描述字s。FD_ZERO(*set):將set初始化為空集NULL。
timeout參數控制select完成的時間。若timeout參數為空指針,則select將一直阻塞到有一個描述字滿足條件,否則的話,timeout指向一個timeval結構,其中指定了select調用在返回前等待多長時間。如果timeval為{0,0},則select立即返回,這可用於探詢所選套接口的狀態,如果處於這種狀態,則select調用可認為是非阻塞的,且一切適用於非阻塞調用的假設都適用於它,舉例來說,阻塞鉤子函式不應被調用,且套接口實現不應yield。

返回值

select()調用返回處於就緒狀態並且已經包含在fd_set結構中的描述字總數;如果逾時則返回0;否則的話,返回SOCKET_ERROR錯誤,應用程式可通過WSAGetLastError獲取相應錯誤代碼。
當返回為-1時,所有描述符集清0。
當返回為0時,表示逾時。
當返回為正數時,表示已經準備好的描述符數。
select()返回後,在3個描述符集裡,依舊是1的位就是準備好的描述符。這也就是為什麼,每次用select後都要用FD_ISSET的原因。

錯誤代碼

WSANOTINITIALISED:在使用此API之前應首先成功地調用WSAStartup()。
WSAENETDOWN:套接口實現檢測到網路子系統失效。
WSAEINVAL:逾時時間值非法,
WSAEINTR:通過一個WSACancelBlockingCall()來取消一個阻塞的
WSAEINPROGRESS:一個阻塞的套接口調用正在運行中。
WSAENOTSOCK:描述字集合中包含有非套接口的元素。

調用

//下面是示例代碼://代碼是伺服器TCP模型,採用多路復用的select函式實現了循環的監聽並接受客戶端的功能,其中也包含了上傳下載的功能*/#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/socket.h>#include<string.h>#include<netinet/in.h>#include<sys/ioctl.h>#include<sys/stat.h>#include<fcntl.h>#include<sys/types.h>#include<dirent.h>int main(){    struct sockaddr_in seraddr,cliaddr;    int listenfd,connfd,fd1,fd2,n,m,l,port;    char user[20],buf[4096];    fd_set  readfds,tmpfds;//崗哨監控集合    socklen_t addrlen;    DIR *dr;    struct dirent *file;    printf("請輸入需要設定的伺服器名稱:");    scanf("%s",user);        printf("請輸入需要設定的伺服器連線埠:");    scanf("%d",&port);    getchar();    if((listenfd=socket(AF_INET,SOCK_STREAM,0))<0)    {        perror("創建失敗");       exit(-1);    }            /*開始設定伺服器的參數地址類型,IP,PORT*/    memset(&seraddr,0,sizeof(seraddr));//將伺服器的初值空間清空,防止轉化過程有影響    seraddr.sin_family=AF_INET;    seraddr.sin_port=htons(port);//將得到的本地連線埠轉換為網路位元組序    seraddr.sin_addr.s_addr=htonl(INADDR_ANY);//將得到的ip地址字元串轉換為網路位元組序的ip地址數值    if((bind(listenfd,(struct sockaddr*)&seraddr,sizeof(seraddr))<0))    {        perror("綁定失敗");       exit(-1);    }    printf("綁定創建\n");    if((connfd=listen(listenfd,50))<0)    {        perror("監聽失敗");        exit(-1);    }    printf("開始監聽\n");    FD_ZERO(&readfds);//初始化檔案集    FD_SET(listenfd,&readfds);//將需要監視的listenfd放入readfds集中    while(1)//循環監聽    {        int nread,n;        tmpfds=readfds;//將監視集傳遞給臨時的監視集中,防止後續的操作修改了監視集        if(select(FD_SETSIZE,&tmpfds,NULL,NULL,NULL)<0)//設定監視,監視tmpfds內的子fd變化,發生變化的將會被保留在tmpfds中          {           perror("監視未成功");            exit(-1);         }                 for(fd1=0;fd1<FD_SETSIZE;fd1++)//循環找在最大範圍內的fd1        {           if(FD_ISSET(fd1,&tmpfds))//查找是否fd1在tmpfds裡面            {              if(fd1==listenfd)//判定fd1等於監聽fd,即監聽fd在監視過程中出現變化被發現              {                  addrlen=sizeof(cliaddr);                 connfd=accept(listenfd,(struct sockaddr*)&cliaddr,&addrlen);//開始接收客戶                 FD_SET(connfd,&readfds);//將connfd加入監視集,監視接入的變化                 printf("接入新的連線\n");              }              else                  {                      ioctl(fd1,FIONREAD,&nread);//測試在fd中還有nread個字元需要讀取                      if(nread==0)//如果需要讀取的數據為0,則關閉檢測出來的fd1,並且從監視中移除                    {                     close(fd1);                     FD_CLR(fd1,&readfds);                      printf("移除\n");                    }                      else//如果可讀數據不為0,則讀出來                    {                        int i;                        char *p=NULL,*q=NULL;                      n=read(fd1,buf,nread);                      buf[n]=0;                      p=buf;                                                                      if((strncmp(p,"-get",4)==0))                          {                               q=p+5;                             printf("客戶下載檔案>%s",q);                             if((fd2=open(q,O_RDONLY))<0)                                 perror("打開檔案錯誤");                                                         while((m=read(fd2,buf,4096))>0)                             {                                 write(connfd,buf,m);                                                              }                             bzero(buf,sizeof(buf));                             close(fd1);                             close(fd2);                             FD_CLR(fd1,&readfds);                                                    }                                                    if((strncmp(p,"-up",3)==0))                          {                               q=p+4;                             printf("客戶上傳檔案%s\n",buf+4);                             if((fd2=open(q,O_CREAT | O_WRONLY | O_APPEND ,0666))<0)                             {                                perror("打開檔案寫入失敗");                                                              }                                                          while((m=read(connfd,buf,128))>0)                             {                                  printf("%s",buf);                                 write(fd2,buf,m);                             }                             bzero(buf,sizeof(buf));                             close(fd1);                             close(fd2);                             FD_CLR(fd1,&readfds);                                                    }                                                    if((strncmp(p,"-ls",3)==0))                          {                               q=p+4;                             printf("客戶查看檔案……");                             if((dr=opendir(q))==NULL)                                 perror("打開目錄失敗");                             while((file=readdir(dr))!=NULL)                             {                                   printf("%s    ",file->d_name);                                  write(connfd,file->d_name,sizeof(file->d_name));                             }                             close(fd1);                             close(connfd);                             closedir(dr);                             FD_CLR(fd1,&readfds);                                                    }                                            printf("從客戶收取的信息:%s\n",buf);                    }                  }            }//end if 0                             }//end for 0        }//end while0        exit(0);}//end main

相關參考

WSAAsyncSelect(),accept(),connect(), recv(), recvfrom(),send()。

事件用法

概述
觸發每一個匹配元素的select事件
這個函式會調用執行綁定到select事件的所有函式,包括瀏覽器的默認行為。可以通過在某個綁定的函式中返回false來防止觸發瀏覽器的默認行為。
參數
參數描述
可選。規定當 select 事件被觸發時運行的函式。
返回值
jQuery
示例
觸發所有input元素的select事件:
jQuery 代碼:
$("input").select();

相關詞條

熱門詞條

聯絡我們