三.Windows I/O模型之事件選擇(WSAEventSelect )模型


1.事件選擇模型:和異步選擇模型類似的是,它也允許應用程序在一個或多個套接字上,接收以事件為基礎的網絡事件通知。對於異步選擇模型采用的網絡事件來說,它們均可原封不動地移植到事件選擇模型。事件選擇模型和異步選擇模型最主要的差別在於網絡事件會投遞至一個事件對象句柄,而非投遞至一個窗口例程。
2.創建事件對象:事件選擇模型要求應用程序針對打算使用事件選擇模型的每一個套接字,首先創建一個事件對象。創建方法是調用WSACreateEvent函數,它的定義如下:WSAEVENT WSACreateEvent(void);
3.綁定事件對象與套接字:
int WSAEventSelect(
    SOCKET s,
    WSAEvent hEventObject,
    long lNetworkEvents//網絡事件,就是異步選擇中的網絡事件,用法完全相同
    );
 對於事件來說,他有兩種類型,自動事件和人工事件。有兩種狀態,未觸發狀態和觸發狀態。使用WSACreateEvent函數創建的事件默認為人工事件且處於未觸發狀態。隨着網絡事件觸發了與一個套接字關聯在一起的事件對象,工作狀態便會從未觸發狀態轉變成觸發狀態。由於事件對象是在一種人工重設模式中創建的,所以在完成了一個I/O請求的處理之后,我們的應用程序需要負責將工作
狀態觸發狀態更改未觸發狀態。
4.重置事件為未觸發狀態:
BOOL WSAResetEvent(WSAEVENT hEvent);
該函數的功能就是把時間從觸發狀態重置為未觸發狀態
5.關閉事件對象,釋放其所占用的內核資源:
BOOL WSACloseEvent(WSAEVENT hEvent);
6.監視事件對象的狀態:
DWORD WSAWaitForMultipleEvents(
    DWORD cEvents;//事件對象數組中事件的數目
    const WSAEVENT FAR* lphEvents,//事件對象數組
    BOOL fWaitAll,//該參數指明了是否要等到所有事件對象變為觸發狀態函數才返回
    DWORD dwTimeout,//超時,毫秒為單位超過規定的時間,函數就會立即返回,即使由fWaitAll參數規定的條件尚未滿足也如此
    BOOL fAlertable//忽略,置為FALSE
    );
參數:
    要注意的是,WSAWaitForMultipleEvents只能支持由WSA_MAXIMUM_WAIT_EVENTS對象規定的一個最大值,在此定義成6 4個。因此,針對發出WSAWaitForMultipleEvents調用的每個線程,該I/O模型一次最多都只能支持6 4個套接字。假如想讓這個模型同時管理不止64個套接字,必須創建額外的工作者線程,以便等待更多的事件對象。fWaitAll 參數指定了指明了是否要等到所有事件對象變為觸發狀態函數才返回。若設為TRUE,那么只有等lphEvents數組內包含的所有事件對象都已進入觸發狀態,函數才會返回;但若設為FALSE,任何一個事件對象進入觸發狀態,函數就會返回。就后一種情況來說,返回值指出了到底是哪個事件對象造成了函數的返回。通常,應用程序應將該參數設為FALSE,通常,dwTimeout被置為0.
一次只為一個套接字事件提供服務    
函數解釋:
一個套接字同一個事件對象句柄關聯在一起后,應用程序便可開始I/O處理;方法是等待網絡事件觸發事件對象句柄的工作狀態。WSAWaitForMultipleEvents函數的設計宗旨便是用來等待一個或多個事件對象句柄,並在事先指定的一個或所有事件對象進入觸發狀態后,
或在超過了一個規定的時間周期后,立即返回。
7.確定網絡事件發生的套接字:若WSAWaitForMultipleEvents收到一個事件對象的網絡事件通知,便會返回一個值,指出造成函數返回的事件對象。這樣一來,我們的應用程序便可引用事件數組中已傳信的事件,並檢索與那個事件對應的套接字,判斷到底是在哪個套接字上,發生了什么網絡事件類型。對事件數組中的事件進行引用時,應該用WSAWaitForMultipleEvents的返回值,減去預定義值WSA_WAIT_EVENT_0,得到具體的引用值(即索引位置)。
Index=WSAWaitForMultipleEvents(...);
myEvent=EventArray[Index-WSA_WAIT_EVENT_0];
7.調查發生的網絡事件類型:
int WSAEnumNetworkEvents(
    SOCKET s,
    WSAEVENT hEventObjects,//參數可選,對應於打算重設的事件對象,即設置事件為未觸發狀態。和WSAtResetEvent函數功能相同
    LPWSANETWORKEVENTS lpNetworkEvents//用來接受發生的網絡事件類型以及可能出現的任何錯誤代碼
    );
該函數中的第四個參數用來接收發生的網絡事件類型
8.WSANETWORKEVENTS結構:
tydef struct _WSANETWORKEVENTS
{
    long lNetworkEvents;//網絡事件類型
    long iErrorCode[FD_MAX_EVENTS];//錯誤代碼
}WSANETWORKEVENTS,FAR* LPWSANETWORKEVENTS;
iErrorCode參數指定的是一個錯誤代碼數組,同lNetworkEvents中的事件關聯在一起。iErrorCode針對每個網絡事件類型,都存在着一個特殊的事件索引,名字與事件類型的名字類似,只是要在事件名字后面添加一個“ _BIT”后綴字串即可.

示例代碼:

 1 SOCKET Socket[WSA_MAXIMUM_WAIT_EVENTS];//64
 2 WSAEVENT event[WSA_MAXIMUM_WAIT_EVENTS];
 3 SOCKET accept,listen;
 4 DWORD eventTotal=0;
 5 DWORD index;
 6 
 7 //創建套接字
 8 listen=socket(...);
 9 
10 //綁定本地地址
11 bind(...);
12 
13 //創建事件對象
14 WSAEVENT newEvent;
15 newEvent=WSACreateEvent();
16 
17 //注冊網絡事件
18 WSAEventSelect(listen,newEvent,FD_ACCEPT|FD_CLOSE);
19 
20 Socket[eventTotal]=listen;
21 event[eventTotal]=newEventl;
22 eventTotal++;
23 
24 
25 while(1)
26 {
27     //等待事件觸發狀態
28     index=WSAWaitForMultipleEvents(eventTotal,event,FALSE,WSA_INFINITE,FALSE);
29 
30     //查看發生的網絡事件類型,確定發生網絡事件的套接字
31     WSANETWORKEVENTS networkEvents;
32     WSAEnumNetworkEvents(Socket[index-WSA_WAIT_EVENT_0],event[index-WSA_WAIT_EVENT_0],&networkEvents);
33 
34     //確定發生的網絡事件類型
35     if(networkEvents.lNetworkEvents&FD_ACCEPT)
36     {
37         if(networkEvents.iErrorCode[FD_ACCEPT_BIT]!=0)
38         {
39             printf("FD_ACCEPT failed with error %d\n",networkEvents.iErrorCode[FD_ACCEPT_BIT]);
40             break;
41         }
42         
43         //FD_ACCEPT事件發生后,則進行后續處理
44         accept=accept(Socket[index_WSA_WAIT_EVENT_0],NULL,NULL);
45         
46         //查看事件對象的數目
47         if(eventTotal>WSA_MAXIMUM_WAIT_EVENTS)
48         {
49             printf("too many connections\n");
50             closesocket(accept);
51             break;
52         }
53         
54         //再次創建事件,再次進行上述操作,進行循環
55         newEvent=WSACreateEvent();
56         
57         WSAEventSelect(listen, newEvent, FD_READ|FD_WRITE | FD_CLOSE);
58         
59         event[eventTotal]=newEvent;
60         Socket[eventTotal]=accept;
61         eventTotal++;
62         printf("socket %d connected\n",accept);
63     }
64 
65     //FD_READ事件的處理
66     //雷同於FD_ACCEPT網絡事件的處理
67     if(networkEvents.lNetworkEvents&FD_READ)
68     {
69         if(networkEvents.iErrorCode[FD_READ_BIT]!=0)
70         {
71             printf("FD_READ failed with error %d\n",networkEvents.iErrorCode[FD_READ_BIT]);
72             break;
73         }
74         //讀取數據
75         recv(Socket[index-WSA_WAIT_EVENT_0],buf,sizeof(buf),0);
76     }
77 
78     //接下來其他網絡事件的處理同上,但是需要注意一點,事件選擇模型是基於窗口程序的,並且需要消息發送,只是這部分代碼為給出而已
79     
80 }
81 
82 
83 
84     
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM