您的位置:首页 > 其它

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

2009-11-28 17:22 330 查看
SAEventSelect
模型与
WSAAsyncSelect
模型类似,差别在于前者的网络事件是由事件句柄完成,后者的网络事件是由窗口过程完成。WSAEventSelect
的优点在于概念简单,不需要窗口环境。缺点在于每次只能等待
64
个事件,这就是说在处理多天
64
个的
SOCKET
时必须组织一个线程池。如需处理大量的
SOCKET
连接,此模型的伸缩性就不如重叠模型。以下为此模型的相关信息:1.为每个打算使用的套接字创建一个事件对象,创建方法:
WSAEVENT WSACreateEvent(void)
返回值是人工重设的事件对象句柄。2.创建了事件对象后必须将它与某个套接字关联在一起,同时注册感兴趣的事件类型。
int WSAEventSelect (

[/code]
  SOCKET

s

,

相关套接字
[/code]
  WSAEVENT

hEventObject

,

事件句柄
[/code]
  long

lNetworkEvents
网络事件(
参见MSDN)
[/code]
);
[/code]
事件分为“人工重设”和“自动重设”两种,因为
WSACreateEvent
创建的为人工重设,在完成一个
I/O
请求后事件就变为已经传信状态,所以要自己将已传信的工作状态变为未传信状态,方法为使用
WSAResetEvent
重置事件状态。应用程序完成了对某个事件对象的处理后应调用
WSACloseEvent
来释放由事件句柄使用的系统资源。3

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

[/code]
  DWORD

cEvents

,
事件的数量
[/code]
  const WSAEVENT FAR

*lphEvents

,
事件数组
[/code]
  BOOL

fWaitAll

,

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

数组中所有事件对象都已传信才返回,反之
[/code]
  DWORD

dwTimeOUT

,

等待网络事件发生的时间
[/code]
  BOOL

fAlertable

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

[/code]
);

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

知道了造成网络事件的SOCKET
后,并可调用WSAENumNetWorkEvents
函数,查看发生的什么网络事件,函数定义如下.
int WSAEnumNetworkEvents (
[/code]
  SOCKET

s

,

造成网络事件的SOCKET

[/code]
  WSAEVENT

hEventObject

,

打算重设的事件对象
[/code]
  LPWSANETWORKEVENTS

lpNetworkEvents
指向

WSANETWORKEVENTS结构的指针
[/code]
);

[/code]
WSANETWORKEVENTS
事件的[code]lNetworkEvents
参数指定对应于该SOCKET
上发生的网络事件类型(参见MSDN
[/code]
iErrorCode[FD_MAX_EVENTS];
指定的是一个错误代码数组,这个数组名同lNetworkEvents
中的事件关联在一起,它与事件类型的名称类似,只是在事件名后加一个“_BIT
”作为后缀.
如FD_READ
的IError
数组的标识为FD_READ_BIT
[/code]
如下述代码(针对FD_READ
)
[/code]
If (NetworkEvents.lNetworkEvents & FD_READ)
[/code]
{
[/code]
If(NetworkEvents.iErrorCode[FD_READ_BIT] != 0)
[/code]
{
[/code]
   Printf(“FD_READ failed with error %d/n,
[/code]
    NetworkEvents.iErrorCode[FD_READ_BIT]);
[/code]
}
[/code]
}
[/code]套接字IO模型(三) WSAEventSelect模型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_EVENTSfWaitAll指定等待类型:TRUE:当lphEvent数组重所有事件对象同时有信号时返回;FALSE:任一事件有信号就返回。dwTimeout为等待超时(毫秒) WSA_INFINATEfAlertable为指定函数返回时是否执行完成例程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;78
//
Set up a TCP socket for listening on port 51509
Listen=
socket(PF_INET,SOCK_STREAM,
0
);1011
InternetAddr.sin_family
=
AF_INET;12
InternetAddr.sin_addr.s_addr
=
htonl(INADDR_ANY);13
InternetAddr.sin_port
=
htons(
5150
);1415
bind(Listen,(PSOCKADDR)
&
InternetAddr,
sizeof
(InternetAddr));1617
NewEvent
=
WSACreateEvent
();1819
WSAEventSelect
(Listen
,NewEvnet
,FD_ACCEPT|
FD_CLOSE);2021
listen(Listen,
5
);2223
Socket[EventTotal]
=
Listen;24
Event[EventTotal]
=
NewEvent;25
EventTotal
++
;2627
while
(TRUE)28
{29//
Wait for network events on all sockets30
Index
=
WSAWaitForMultipleEvents
(EventTotal,EventArray,FALSE,WSA_INFINITE
,FALSE);3132
WSAEnumNewWorkEvents
(SocketArray[Index
-
WSA_WAIT_EVENT_0],33
EventArray[Index
-
WSA_WAIT_EVENT_0],34&
NetworkEvents);35//
Check for FD_ACCEPT messages36if
(
NetworkEvents.lNetworkEvents
&

FD_ACCEPT)37
{38if
(NetworkEvents.iErrorCode[FD_ACCEPT_BIT]
!=
0
)39
{40//
Error41break
;42
}43//
Accept a new connection and add it to the socket and event lists44Accept
=
accept(SocketArray[Index-

WSA_WAIT_EVENT_0
],NULL,NULL);4546//
We cannot process more than WSA_MAXIMUM_WAIT_EVENTS sockets ,47//
so close the accepted socket48if
(EventTotal
>
WSA_MAXIMUM_WAIT_EVENTS)49
{50
printf(
"..
"
);51
closesocket (Accept);52break
;53
}54
NewEvent
=
WSACreateEvent();5556WSAEventSelect(Accept
,NewEvent,FD_READ|
FD_WRITE
|

FD_CLOSE
);5758
Event[EventTotal]
=
NewEvent;59Socket[EventTotal]= Accept
;60
EventTotal
++
;61
prinrt(
"
Socket %d connect/n
"
,Accept);62
}63//
Process FD_READ notification64if(NetworkEvents.lNetwoAD)
rkEvents & FD_R
E65
{66if
(NetworkEvents.iErrorCode[FD_READ_BIT
!=
0
])67
{68//
Error69break
;70
}7172//
Read data from the socket73recv(Socket[Index-

WSA_WAIT_EVENT_0
],buffer,
sizeof
(buffer),
0
);74
}75//
process FD_WRITE notitication76if
(
NetworkEvents.lNetworkEvents & FD_WRITE
)77
{78if
(NetworkEvents.iErrorCode[FD_WRITE_BIT]
!=
0
)79
{80//
Error81break
;82
}83send(Socket[Index-

WSA_WAIT_EVENT_0]
,buffer,
sizeof
(buffer),
0
);84
}85if
(
NetworkEvents.lNetworkEvents
&

FD_CLOSE)86
{87if
(NetworkEvents.iErrorCode[FD_CLOSE_BIT]
!=
0
)88
{89//
Error90break
;91
}92
closesocket (Socket[Index
-
WSA_WAIT_EVENT_0]);93//
Remove socket and associated event from the Socket and Event arrays and94//
decrement eventTotal95
CompressArrays(Event,Socket,
&
EventTotal);96
}97
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: