select函数及fd_set介绍
1. select函數
1. 用途
? ? ? ?在編程的過程中,經常會遇到許多阻塞的函數,好像read和網絡編程時使用的recv, recvfrom函數都是阻塞的函數,當函數不能成功執行的時候,程序就會一直阻塞在這里,無法執行下面的代碼。這時就需要用到非阻塞的編程方式,使用select函數就可以實現非阻塞編程。
? ? ? ?select函數是一個輪循函數,循環詢問文件節點,可設置超時時間,超時時間到了就跳過代碼繼續往下執行。
2. 大致原理
? ? ? ?select需要驅動程序的支持,驅動程序實現fops內的poll函數。select通過每個設備文件對應的poll函數提供的信息判斷當前是否有資源可用(如可讀或寫),如果有的話則返回可用資源的文件描述符個數,沒有的話則睡眠,等待有資源變為可用時再被喚醒繼續執行。詳細的原理請看這里
3. 函數定義
??該函數聲明如下
int select(int nfds, fd_set *readset, fd_set *writeset, fe_set* exceptset,
struct timeval* timeout);
參數:
? ? ?nfds? ? 需要檢查的文件描述字個數
? ? ?readset? ?用來檢查可讀性的一組文件描述字。
? ? ?writeset? ? 用來檢查可寫性的一組文件描述字。
? ? ?exceptset? 用來檢查是否有異常條件出現的文件描述字。(注:錯誤不包括在異常條件之內)
? ? ?timeout? ? 超時,填NULL為阻塞,填0為非阻塞,其他為一段超時時間
返回值:
? ? ?返回fd的總數,錯誤時返回SOCKET_ERROR
2. fd_set結構體
? ? ?
上面select函數中需要用到兩個fd_set形參,這個結構體到底做什么用的呢
? ? ? ?fd_set其實這是一個數組的宏定義,實際上是一long類型的數組,每一個數組元素都能與一打開的文件句柄(socket、文件、管道、設備等)建立聯系,建立聯系的工作由程序員完成,當調用select()時,由內核根據IO狀態修改fd_set的內容,由此來通知執行了select()的進程哪個句柄可讀。
? ? ? ?系統提供了FD_SET,?FD_CLR,?FD_ISSET,?FD_ZERO進行操作,聲明如下:
FD_SET(int fd, fd_set *fdset); //將fd加入set集合 FD_CLR(int fd, fd_set *fdset); //將fd從set集合中清除 FD_ISSET(int fd, fd_set *fdset); //檢測fd是否在set集合中,不在則返回0 FD_ZERO(fd_set *fdset); //將set清零使集合中不含任何fd下面寫一段程序探究一下這幾個宏的工作:
#include <WINSOCK2.H>int main() {fd_set fdset;FD_ZERO(&fdset);FD_SET(1, &fdset);FD_SET(2, &fdset);FD_SET(3, &fdset);FD_SET(7, &fdset);int isset = FD_ISSET(3, &fdset);printf("isset = %d\n", isset);FD_CLR(3, &fdset);isset = FD_ISSET(3, &fdset);printf("isset = %d\n", isset);return 0; }?當使用FD_SET添加完1、2、3、7后,fdset的值如下:
? ?然后經過FD_CLR以后,fd_array[2]就被清除了,數組后面的數據一次往前提,即7被放到了fd_array[2]
? ?所以isset前后兩次打印的值分別為1和0
3. 小結
? ?select的結果會對fd_set造成影響。例如,對于一個監聽的socket:
#include <WinSock2.h> #include <stdio.h> #pragma comment(lib,"WS2_32.lib") int main() {FD_SET ReadSet;FD_ZERO(&ReadSet);WSADATA wsaData;WSAStartup(MAKEWORD(2, 2), &wsaData); //初始化SOCKET ListenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); //定義一個監聽套接字//bind 等操作這里省略.... //......FD_SET(ListenSocket, &ReadSet); //將套接字加入ReadSet集合中int isset = FD_ISSET(ListenSocket, &ReadSet); //這里并沒有通過select對fd_set進行篩選 printf("Before select, isset = %d\n", isset); //所以這里打印結果為1 struct timeval tTime; tTime.tv_sec = 10; tTime.tv_usec = 0; select(0, &ReadSet, NULL, NULL, &tTime); //通過select篩選處于就緒狀態的fd //這時,剛才的ListenSocket并不在就緒狀態(沒有連接連入),那么就從ReadSet中去除它 isset = FD_ISSET(ListenSocket, &ReadSet); printf("After select, isset = %d\n", isset); //所以這里打印的結果為0 system("pause"); return 0; }所以可以使用select以及fd的操作來完成異步的網絡消息處理,具體的實現請看這里的例子
總結
以上是生活随笔為你收集整理的select函数及fd_set介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 远程桌面提示:身份验证错误 要求的函数不
- 下一篇: setsockopt()和getsock