若是你想在Windows平臺上構建服務器應用,那麼I/O模型是你必須考慮的。
Windows操做系統提供了五種I/O模型,分別是:程序員
■ 選擇(select);
■ 異步選擇(WSAAsyncSelect);
■ 事件選擇(WSAEventSelect);
■ 重疊I/O(Overlapped I/O);
■ 完成端口(Completion Port) 。安全
每一種模型適用於一種特定的應用場景。程序員應該對本身的應用需求很是明確,
綜合考慮到程序的擴展性和可移植性等因素,做出本身的選擇。
==============================================
█ 選擇(select)模型是Winsock中最多見的 I/O模型。核心即是利用 select 函數,實現對 I/O的管理!
利用 select 函數來判斷某Socket上是否有數據可讀,或者可否向一個套接字寫入數據,防止程序在Socket處於阻塞模式中時,
在一次 I/O 調用(如send或recv、accept等)過程當中,被迫進入「鎖定」狀態;同時防止在套接字處於非阻塞模
式中時,產生WSAEWOULDBLOCK錯誤。服務器
█ select 的函數原型以下:
int select(
__in int nfds,
__in_out fd_set* readfds,
__in_out fd_set* writefds,
__in_out fd_set* exceptfds,
__in const struct timeval* timeout
);app
其中,第一個參數nfds會被忽略。之因此仍然要提供這個參數,只是爲了保持與Berkeley套接字兼容。
後面你們看到有三個 fd_set類型的參數:
一個用於檢查可讀性(readfds),
一個用於檢查可寫性(writefds),
一個用於例外數據(exceptfds)。異步
fd_set 結構的定義以下:
typedef struct fd_set {
u_int fd_count;
SOCKET fd_array[FD_SETSIZE];
} fd_set;函數
#define FD_SETSIZE 64
因此 fd_set 結構中最多隻能監視64個套接字。性能
fdset 表明着一系列特定套接字的集合。其中, readfds 集合包括符合下述任何一個條件的套接字:
● 有數據能夠讀入。
● 鏈接已經關閉、重設或停止。
● 假如已調用了listen,並且一個鏈接正在創建,那麼accept函數調用會成功。測試
writefds 集合包括符合下述任何一個條件的套接字:
● 有數據能夠發出。
● 若是已完成了對一個非鎖定鏈接調用的處理,鏈接就會成功。操作系統
exceptfds 集合包括符合下述任何一個條件的套接字:
● 假如已完成了對一個非鎖定鏈接調用的處理,鏈接嘗試就會失敗。
● 有帶外(Out-of-band,OOB)數據可供讀取。指針
舉個例子,假設咱們想測試一個套接字是否「可讀」,必須將本身的套接字增添到readfds集合中,
而後調用 select 函數並等待其完成。select 完成以後,再次判斷本身的套接字是否仍爲 readfds 集合的一部分。
若答案是確定的,則代表該套接字「可讀」,可當即着手從它上面讀取數據。
在三個參數中(readfds、writefds 和 exceptfds),任何兩個均可以是空值( NULL);
可是,至少有一個不能爲空值!在任何不爲空的集合中,必須包含至少一個套接字句柄;
不然, select 函數便沒有任何東西能夠等待。最後一個參數 timeout 對應的是一個指針,它指向一個timeval 結構,
用於決定select 最多等待 I/O操做完成多久的時間。如 timeout 是一個空指針,那麼 select 調用會無限
期地「鎖定」或停頓下去,直到至少有一個描述符符合指定的條件後結束。
對 timeval 結構的定義以下:
tv_sec 字段以秒爲單位指定等待時間;
tv_usec 字段則以毫秒爲單位指定等待時間。
1秒 = 1000毫秒
若將超時值設置爲(0 , 0),代表 select 會當即返回,出於對性能方面的考慮,應避免這樣的設置。
█ select 函數返回值:
select 成功完成後,會在 fdset 結構中,返回恰好有未完成的 I/O操做的全部套接字句柄的總量。
若超過 timeval 設定的時間,便會返回0。若 select 調用失敗,都會返回 SOCKET_ERROR,
應該調用 WSAGetLastError 獲取錯誤碼!
用 select 對套接字進行監視以前,必須將套接字句柄分配給一個fdset的結構集合,
以後再來調用 select,即可知道一個套接字上是否正在發生上述的 I/O 活動。
Winsock 提供了下列宏操做,可用來針對 I/O活動,對 fdset 進行處理與檢查:
● FD_CLR(s, *set):從set中刪除套接字s。
● FD_ISSET(s, *set):檢查s是否set集合的一名成員;如答案是確定的是,則返回TRUE。
● FD_SET(s, *set):將套接字s加入集合set。
● FD_ZERO( * set):將set初始化成空集合。
例如,假定咱們想知道是否可從一個套接字中安全地讀取數據,同時不會陷於無休止的 「鎖定」狀態,即可使用 FDSET 宏,將本身的套接字分配給 fdread 集合,再來調用 select。要 想檢測本身的套接字是否仍屬 fdread 集合的一部分,可以使用 FD_ISSET 宏。採用下述步驟,便 可完成用 select 操做一個或多個套接字句柄的全過程: 1) 使用FDZERO宏,初始化一個fdset對象; 2) 使用FDSET宏,將套接字句柄加入到fdset集合中; 3) 調用 select 函數,等待其返回……select 完成後,會返回在全部 fdset 集合中設置的套接字句柄總數, 並對每一個集合進行相應的更新。 4) 根據 select的返回值和 FDISSET宏,對 fdset 集合進行檢查。 5) 知道了每一個集合中「待決」的 I/O操做以後,對 I/O進行處理, 而後返回步驟1 ),繼續進行 select 處理。