程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> WinSock異步IO模型之Select

WinSock異步IO模型之Select

編輯:C++入門知識

如果你想在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
);

其中,第一個參數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 處理。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved