套接字IO模型(三) WSAEventSelect模型

SAEventSelect 模型与 WSAAsyncSelect 模型类似,差别在于前者的网络事件是由事件句柄完成,后者的网络事件是由窗口过程完成。

WSAEventSelect 的优点在于概念简单,不需要窗口环境。

缺点在于每次只能等待 64 个事件,这就是说在处理多天 64 个的 SOCKET 时必须组织一个线程池。如需处理大量的 SOCKET 连接,此模型的伸缩性就不如重叠模型。

 

以下为此模型的相关信息:

1.    为每个打算使用的套接字创建一个事件对象,创建方法: WSAEVENT WSACreateEvent(void) 返回值是人工重设的事件对象句柄。

2.    创建了事件对象后必须将它与某个套接字关联在一起,同时注册感兴趣的事件类型。

int WSAEventSelect (




  SOCKET

 s

,               



相关套接字

  WSAEVENT

 hEventObject





事件句柄

  long

 lNetworkEvents     
网络事件(
参见MSDN)




);




事件分为“人工重设”和“自动重设”两种,因为 WSACreateEvent 创建的为人工重设,在完成一个 I/O 请求后事件就变为已经传信状态,所以要自己将已传信的工作状态变为未传信状态,方法为使用 WSAResetEvent 重置事件状态。

应用程序完成了对某个事件对象的处理后应调用 WSACloseEvent 来释放由事件句柄使用的系统资源。

3 SOCKET 同一个事件对象关联在一起后,程序便可开始 I/O 处理,这就需要应用程序等待网络事件来触发事件对象句柄的工作状态。使用 WSAWaitForMultipleEvents 函数来等待网络事件的触发。

DWORD WSAWaitForMultipleEvents(




  DWORD

 cEvents

,                  
事件的数量



  const WSAEVENT FAR

 *lphEvents


事件数组



  BOOL

 fWaitAll

,                  



等待数组的状态,TRUE
时只有在lphEvents



数组中所有事件对象都已传信才返回,反之

  DWORD

 dwTimeOUT

,                



等待网络事件发生的时间

  BOOL

 fAlertable                 



此参数设为FALSE
,主要用在重叠I/O
的完成例程中,在此无具体意义



);




若收到一个事件对象的网络事件通知后,便会返回一个值指出造成函数返回的事件对象,这样应用程序便可引用事件数组中已传信的事件,并检索与事件对应的 SOCKET ,方法是用 WSAWaitForMultipleEvents 的返回值减去相应的预定义值 WSA_WAIT_EVENT_0, 得到具体的引用值,如下所示

Index = WSAWaitForMultipleEvents(…)

myEvent = EventArray[Index – WSA_WAIT_EVENT_0];

 

4.  知道了造成网络事件的SOCKET 后,并可调用WSAENumNetWorkEvents 函数,查看发生的什么网络事件,函数定义如下.

int WSAEnumNetworkEvents (




  SOCKET

 s

,                    



造成网络事件的SOCKET



  WSAEVENT

 hEventObject

,       



打算重设的事件对象

  LPWSANETWORKEVENTS

 lpNetworkEvents  
指向

WSANETWORKEVENTS


结构的指针

);




WSANETWORKEVENTS




事件的lNetworkEvents
参数指定对应于该SOCKET
上发生的网络事件类型(参见MSDN

iErrorCode[FD_MAX_EVENTS];
指定的是一个错误代码数组,这个数组名同lNetworkEvents
中的事件关联在一起,它与事件类型的名称类似,只是在事件名后加一个“_BIT
”作为后缀.FD_READIError
数组的标识为FD_READ_BIT



如下述代码(针对FD_READ


If (NetworkEvents.lNetworkEvents & FD_READ)



{



If(NetworkEvents.iErrorCode[FD_READ_BIT] != 0)



{



   Printf(“FD_READ failed with error %d/n,



    NetworkEvents.iErrorCode[FD_READ_BIT]);



}



}



 

WSAEventSelect模型类似WSAAsynSelect模型,但最主要的区别是网络事件发生时会被发送到一个事件对象句柄,而不是发送到一个窗口。这样可能更加的好,对于服务器端的程序来说。

使用步骤如下:

a、 创建事件对象来接收网络事件:

WSAEVENT WSACreateEvent( void );

该函数的返回值为一个事件对象句柄,它具有两种工作状态:已传信(signaled)和未传信(nonsignaled)以及两种工作模式:人工重设(manual reset)和自动重设(auto reset)。默认未未传信的工作状态和人工重设模式。


b、将事件对象与套接字关联,同时注册事件,使事件对象的工作状态从未传信转变未已传信。

int WSAEventSelect( SOCKET s,WSAEVENT hEventObject,long lNetworkEvents );
s为套接字
hEventObject为刚才创建的事件对象句柄

lNetworkEvents为掩码,定义如上面所述


c、I/O处理后,设置事件对象为未传信
BOOL WSAResetEvent( WSAEVENT hEvent );
Hevent为事件对象

成功返回TRUE,失败返回FALSE。


d、等待网络事件来触发事件句柄的工作状态:

DWORD WSAWaitForMultipleEvents( DWORD cEvents,const WSAEVENT FAR * lphEvents, BOOL fWaitAll,DWORD dwTimeout, BOOL fAlertable );
lpEvent为事件句柄数组的指针
cEvent为为事件句柄的数目,其最大值为WSA_MAXIMUM_WAIT_EVENTS
fWaitAll指定等待类型:TRUE:当lphEvent数组重所有事件对象同时有信号时返回;
FALSE:任一事件有信号就返回。
dwTimeout为等待超时(毫秒) WSA_INFINATE

fAlertable为指定函数返回时是否执行完成例程


nIndex=WSAWaitForMultipleEvents(…);

MyEvent=EventArray[Index- WSA_WAIT_EVENT_0];



事 件选择模型也比较简单,实现起来也不是太复杂,它的基本思想是将每个套接字都和一个WSAEVENT 对象对 应起来,并且在关联的时候指定需要关注的哪些网 络事件。一旦在某个套接字上发生了我们关注的事件(FD_READ和FD_CLOSE),与之相关联的WSAEVENT对象被Signaled。程序定义 了两个全局数组,一个套接字数组,一个WSAEVENT对象数组,其大小都是MAXIMUM_WAIT_OBJECTS(64),两个数组中的元素一一对 应。
同样的,这里的程序没有考虑两个问题,一是不能无条件的调用accept,因为我们支持的并发连接数有限 。解决方法是将套接字按 MAXIMUM_WAIT_OBJECTS分组,每MAXIMUM_WAIT_OBJECTS个套接字一组,每一组分配一个工作者线程 ;或者采用 WSAAccept代替accept,并回调自己定义的Condition Function 。第二个问题是没有对连接数为0的情形做特殊处理,程序在连接数为0的时候CPU占用率为100%。


 1  SOCKET       Socket[WSA_MAXIMUM_WAIT_EVENTS];
 2  WSAEVENT   Event[WSA_MAXINUM_WAIT_EVENTS];
 3  SOCKET    Accept, Listen;
 5  DWORD     EventTotal  =   0 ;
 6  DWORD     Index;
 7  
 8  // Set up a TCP socket for listening on port 5150
 9  Listen   =  socket(PF_INET,SOCK_STREAM, 0 );
10  
11  InternetAddr.sin_family       =  AF_INET;
12  InternetAddr.sin_addr.s_addr  =  htonl(INADDR_ANY);
13  InternetAddr.sin_port         =  htons( 5150 );
14  
15  bind(Listen,(PSOCKADDR)  & InternetAddr, sizeof (InternetAddr));
16  
17  NewEvent  =  WSACreateEvent ();
18  
19  WSAEventSelect (Listen ,NewEvnet ,FD_ACCEPT | FD_CLOSE);
20  
21  listen(Listen, 5 );
22  
23  Socket[EventTotal]  =  Listen;
24  Event[EventTotal]  =  NewEvent;
25  EventTotal ++ ;
26  
27  while  (TRUE)
28  {
29       // Wait for network events on all sockets
30      Index  =  WSAWaitForMultipleEvents (EventTotal,EventArray,FALSE,WSA_INFINITE ,FALSE);
31  
32      WSAEnumNewWorkEvents (SocketArray[Index - WSA_WAIT_EVENT_0],
33          EventArray[Index - WSA_WAIT_EVENT_0],
34           & NetworkEvents);
35       // Check for FD_ACCEPT messages
36       if  ( NetworkEvents.lNetworkEvents  &  FD_ACCEPT )
37      {
38           if  (NetworkEvents.iErrorCode[FD_ACCEPT_BIT]  != 0 )
39          {
40               // Error
41               break ;
42          }
43           // Accept a new connection and add it to the socket and event lists
44           Accept  =  accept(SocketArray[Index - WSA_WAIT_EVENT_0 ],NULL,NULL) ;
45  
46           // We cannot process more than WSA_MAXIMUM_WAIT_EVENTS sockets ,
47           // so close the accepted socket
48           if  (EventTotal  >  WSA_MAXIMUM_WAIT_EVENTS)
49          {
50              printf( " .. " );
51              closesocket (Accept);
52               break ;
53          }
54          NewEvent  =  WSACreateEvent();
55  
56           WSAEventSelect(Accept ,NewEvent,FD_READ| FD_WRITE | FD_CLOSE );
57  
58          Event[EventTotal]  =  NewEvent;
59            Socket[EventTotal]= Accept ;
60          EventTotal ++ ;
61          prinrt( " Socket %d connect/n " ,Accept);
62      }
63       // Process FD_READ notification
64       if   (NetworkEvents.lNetwoAD ) rkEvents & FD_R E
65      {
66           if  (NetworkEvents.iErrorCode[FD_READ_BIT  != 0 ])
67          {
68               // Error
69               break ;
70          }
71  
72           // Read data from the socket
73           recv(Socket[Index- WSA_WAIT_EVENT_0 ],buffer, sizeof (buffer), 0 );
74      }
75       // process FD_WRITE notitication
76       if  ( NetworkEvents.lNetworkEvents & FD_WRITE )
77      {
78           if  (NetworkEvents.iErrorCode[FD_WRITE_BIT]  != 0 )
79          {
80               // Error
81               break ;
82          }
83           send(Socket[Index- WSA_WAIT_EVENT_0] ,buffer, sizeof (buffer), 0 );
84      }
85       if  ( NetworkEvents.lNetworkEvents  &  FD_CLOSE)
86      {
87           if (NetworkEvents.iErrorCode[FD_CLOSE_BIT]  != 0 )
88          {
89               // Error
90               break ;
91          }
92          closesocket (Socket[Index - WSA_WAIT_EVENT_0]);
93           // Remove socket and associated event from the Socket and Event arrays and
94           // decrement eventTotal
95          CompressArrays(Event,Socket, &  EventTotal);
96      }
97  }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值