1,函數(shù)原型:
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, const struct timeval* timeout);
2,參數(shù):
nfds: 本參數(shù)忽略,僅起到兼容作用,設(shè)為0即可;
readfds: (可選)指針,指向一組等待可讀性檢查的套接口;
writefds: (可選)指針,指向一組等待可寫性檢查的套接口;
exceptfds:(可選)指針,指向一組等待錯(cuò)誤檢查的套接口;
timeout: 本函數(shù)最多等待時(shí)間,對(duì)阻塞操作則為NULL。
3,返回值:
(1)select()調(diào)用返回處于就緒狀態(tài)并且已經(jīng)包含在fd_set結(jié)構(gòu)中的描述字總數(shù);
(2)如果超時(shí)則返回0;
(3)否則的話,返回SOCKET_ERROR錯(cuò)誤,應(yīng)用程序可通過WSAGetLastError()獲取相應(yīng)錯(cuò)誤代碼。
4,注釋:
本函數(shù)用于確定一個(gè)或多個(gè)套接口的狀態(tài)。對(duì)每一個(gè)套接口,調(diào)用者可查詢它的可讀性、可寫性及錯(cuò)誤狀態(tài)信息。用fd_set結(jié)構(gòu)來表示一組等待檢查的套接口。在調(diào)用返回時(shí),這個(gè)結(jié)構(gòu)存有滿足一定條件的套接口組的子集,并且select()返回滿足條件的套接口的數(shù)目。有一組宏可用于對(duì)fd_set的操作,這些宏與Berkeley Unix軟件中的兼容,但內(nèi)部的表達(dá)是完全不同的。
readfds參數(shù)標(biāo)識(shí)等待可讀性檢查的套接口。如果該套接口正處于監(jiān)聽listen()狀態(tài),則若有連接請(qǐng)求到達(dá),該套接口便被標(biāo)識(shí)為可讀,這樣一個(gè)accept()調(diào)用保證可以無(wú)阻塞完成。對(duì)其他套接口而言,可讀性意味著有排隊(duì)數(shù)據(jù)供讀取。或者對(duì)于SOCK_STREAM類型套接口來說,相對(duì)于該套接口的虛套接口已關(guān)閉,于是recv()或recvfrom()操作均能無(wú)阻塞完成。如果虛電路被“優(yōu)雅地”中止,則recv()不讀取數(shù)據(jù)立即返回;如果虛電路被強(qiáng)制復(fù)位,則recv()將以WSAECONNRESET錯(cuò)誤立即返回。如果SO_OOBINLINE選項(xiàng)被設(shè)置,則將檢查帶外數(shù)據(jù)是否存在(參見setsockopt())。
writefds參數(shù)標(biāo)識(shí)等待可寫性檢查的套接口。如果一個(gè)套接口正在connect()連接(非阻塞),可寫性意味著連接順利建立。如果套接口并未處于connect()調(diào)用中,可寫性意味著send()和sendto()調(diào)用將無(wú)阻塞完成?!驳⑽粗赋鲞@個(gè)保證在多長(zhǎng)時(shí)間內(nèi)有效,特別是在多線程環(huán)境中〕。
exceptfds參數(shù)標(biāo)識(shí)等待帶外數(shù)據(jù)存在性或意味錯(cuò)誤條件檢查的套接口。請(qǐng)注意如果設(shè)置了SO_OOBINLINE選項(xiàng)為假FALSE,則只能用這種方法來檢查帶外數(shù)據(jù)的存在與否。對(duì)于SO_STREAM類型套接口,遠(yuǎn)端造成的連接中止和KEEPALIVE錯(cuò)誤都將被作為意味出錯(cuò)。如果套接口正在進(jìn)行連接connect()(非阻塞方式),則連接試圖的失敗將會(huì)表現(xiàn)在exceptfds參數(shù)中。
如果對(duì)readfds、writefds或exceptfds中任一個(gè)組類不感興趣,可將它置為空NULL。
在winsock2.h頭文件中共定義了四個(gè)宏來操作描述字集。FD_SETSIZE變量用于確定一個(gè)集合中最多有多少描述字(FD_SETSIZE缺省值為64,可在包含winsock.h前用#define FD_SETSIZE來改變?cè)撝担?duì)于內(nèi)部表示,fd_set被表示成一個(gè)套接口的隊(duì)列,最后一個(gè)有效元素的后續(xù)元素為INVAL_SOCKET。宏為:
FD_CLR(s,*set): 從集合set中刪除描述字s。
FD_ISSET(s,*set): 若s為集合中一員,非零;否則為零。
FD_SET(s,*set): 向集合添加描述字s。
FD_ZERO(*set): 將set初始化為空集NULL。
timeout參數(shù)控制select()完成的時(shí)間。若timeout參數(shù)為空指針,則select()將一直阻塞到有一個(gè)描述字滿足條件。否則的話,timeout指向一個(gè)timeval結(jié)構(gòu),其中指定了select()調(diào)用在返回前等待多長(zhǎng)時(shí)間。如果timeval為{0,0},則select()立即返回,這可用于探詢所選套接口的狀態(tài)。如果處于這種狀態(tài),則select()調(diào)用可認(rèn)為是非阻塞的,且一切適用于非阻塞調(diào)用的假設(shè)都適用于它。
5,錯(cuò)誤代碼:
WSANOTINITIALISED:在使用此API之前應(yīng)首先成功地調(diào)用WSAStartup()。
WSAENETDOWN: WINDOWS套接口實(shí)現(xiàn)檢測(cè)到網(wǎng)絡(luò)子系統(tǒng)失效。
WSAEINVAL: 超時(shí)時(shí)間值非法。
WSAEINTR: 通過一個(gè)WSACancelBlockingCall()來取消一個(gè)(阻塞的)調(diào)用。
WSAEINPROGRESS: 一個(gè)阻塞的WINDOWS套接口調(diào)用正在運(yùn)行中。
WSAENOTSOCK: 描述字集合中包含有非套接口的元素。
6,如何處理
上面在說明FD_SETSIZE時(shí),winsock2.h中定義FD_SETSIZE的大小為64,這樣就對(duì)readfds、writefds、exceptfds的socket句柄數(shù)進(jìn)行了限制。在實(shí)際應(yīng)用中可以使用端口分組或者重新定義FD_SETSIZE的方式進(jìn)行解決。在stdAfx.h最末行添加如下定義:
define FD_SETSIZE 1024 //socket句柄數(shù)
define MAXIMUM_WAIT_OBJECTS 1024 //要等待的對(duì)象數(shù)
要注意的是我們還重定義了要另一個(gè)宏MAXIMUM_WAIT_OBJECTS,它表示要等待的對(duì)象數(shù)。重定義后,程序在現(xiàn)場(chǎng)運(yùn)行正常。