您的位置:首页 > 理论基础 > 计算机网络

网络学习之WSAEventSelect模型

2011-05-05 23:48 295 查看
WSAEventSelect模型是一个异步事件通知模型。允许应用程序在一个或多个套接字上接收收基于事件的网络通知。它与WSAAsyncSelect模型类似,但不是依靠windows的

消息驱动机制,而是经由事件对象句柄通知。

主要函数:

WSACreateEvent 创建一个事件对象

WSAEventSelect 网络事件与事件对象进行关联。

具体参数:

socket s 套接字句柄

WSAEvent h 事件对象句柄

long NetWorkEvents FD_xxx网络事件组合。FD_ACCEPT,FD_READ,FD_WRITE,FD_CLOSE

WSAWaitForMultipleEvents 在一个或多个事件对象上等待网络事件的发生,如果指定时间已过,返回WSA_WAIT_TIMEOUT,如果调用失败则返回WSA_WAIT_FAILED

如果成功,则返回受信的事件对象。

具体参数:

DWORD cEvents 指定事件数组中句柄的个数

WSAEVENT* lphEvents 指向一个事件对象句柄数组

BOOL fWaitAll 指定是否等待所有事件对象都变成受信状态

DWORD dwtimeout

BOOL falertable 在使用WSAEventSelect模型时可以忽略,应设为FALSE

WSAEnumNetworkEvents 找到受信事件对像与之对应的套接字。

具体参数:

SOCKET S //套接字句柄。

WSAEVENT hEventObject 对应的事件对象句柄。如果提供了此参数,本函数会重置这个事件对象的状态

LPWSANETWORKEVENTS lpNetworkEvents //指向一个WSANETWORKEVENTS结构。

最后这个WSANETWORKEVENTS结构是最重要的,因为它取得了套接字上发生的网络事件和相关的出错代码。

定义如下:

LPWSANETWORKEVENTS=^WSANETWORKEVENTS

WSANETWORKEVENTS=RECORD

INetworkEvents : longint;

iErrorCode : array[0..FD_MAX_EVENTS] OF INT;

end;

iErrorCode 是一个数组,数组的每个成员对应着一个网络事件的出错代码。可以用预定义标识FD_READ_BIT,FD_WRITE_BIT,FD_ACCEPT_BIT,FD_CLOSE_BIT来索引FD_READ等事件发生的出错代码。

具体的DELPHI代码如下:

program Project1;
{$APPTYPE CONSOLE}
uses
Windows,SysUtils,JwaWinsock2;

var
eventarray :array[0..WSA_MAXIMUM_WAIT_EVENTS-1] OF WSAEVENT; //事件数组
sockarray :array[0..WSA_MAXIMUM_WAIT_EVENTS-1] of TSocket; //套接字数组
nEventTotal :Integer; //当前总事件数,不能大于64
nport :Integer; //端口
sListen :TSocket; //主套接字
v_sin :sockaddr_in;
event :WSAEVENT; //事件对象变量,用于创建新的事件
sEvent :_WSANETWORKEVENTS;
nIndex :Integer; //接收函数WSAWaitForMultipleEvents返回的值。
i,j :Integer; //循环变量
sNew :TSocket; //客户端的SOCKET
sztext :array[0..255] of AnsiChar; //接收客户端的信息。
vwsadata :WSAData; //初始化socket的变量;
begin
WSAStartup($0202,vwsadata); //初始化socket,此处用socket2.0。
try
slisten:=INVALID_SOCKET;
nport:=4567;
nEventTotal:=0;
try
sListen:=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
v_sin.sin_family:=AF_INET;
v_sin.sin_port :=htons(nport);
v_sin.sin_addr.S_addr:=INADDR_ANY;

if bind(sListen,@v_sin,SizeOf(v_sin))=SOCKET_ERROR then
begin
Writeln('绑定端口发生错误。');
end;
listen(sListen,200); //开始监听。
Writeln('网络开始进行监听中......');
//创建事件对象,并关联到新的套接字上。
event:=WSACreateEvent();
WSAEventSelect(sListen,event,FD_ACCEPT OR FD_CLOSE);
//将事件和套接字存入相应的数组中。
eventarray[nEventTotal]:=event;
sockarray [nEventTotal]:=sListen;
inc(nEventTotal);
while True do
begin
{若收到一个事件对象的网络事件通知后,便会返回一个值指出造成函数返回的事件对象,
这样应用程序便可引用事件数组中已传信的事件,并检索与事件对应的 SOCKET ,
方法是用 WSAWaitForMultipleEvents 的返回值减去相应的预定义值 WSA_WAIT_EVENT_0, 得到具体的引用值
将fAlertable设为False后,如果同时有几个事件对象受信,WSAWaitForMultipleEvents函数的返回值仅能指明
一个,就是句柄数组中最前面的那个。如果指明的事件对象总有网络时间发生,那么后面的其他事件对象所关联的
网络事件就得不到处理。解决的方法:当WSAWaitForMultipleEvents函数返回后,对每个事件都再次调用这个函数,
以便确定其状态。
}
nindex:=WSAWaitForMultipleEvents(nEventTotal,@eventarray,False,WSA_INFINITE,False);
nindex:=nIndex-WSA_WAIT_EVENT_0;
for i:=nIndex to nEventTotal-1 do
begin
nIndex:=WSAWaitForMultipleEvents(1,@eventarray[i],True,1000,False);
if (nIndex=WSA_WAIT_FAILED) or (nIndex=WSA_WAIT_TIMEOUT) then
begin
Continue;
end
else
begin
WSAEnumNetworkEvents(sockarray[i],eventarray[i],@sEvent);
if sEvent.lNetworkEvents=FD_ACCEPT then
begin
if sEvent.iErrorCode[FD_ACCEPT_BIT]=0 then
begin
if nEventTotal>WSA_MAXIMUM_WAIT_EVENTS-1 then
begin
Writeln('有太多的连接!已经大于64了。');
Continue;
end;
snew:=accept(sockarray[i],nil,nil);
writeln('有新的连接进入。');
event:=WSACreateEvent();
WSAEventSelect(sNew,event,FD_READ OR FD_CLOSE OR FD_WRITE);
eventarray[nEventTotal]:=event;
sockarray [nEventTotal]:=sNew;
inc(nEventTotal);
end;
end
else if sEvent.lNetworkEvents=FD_READ then
begin
if recv(sockarray[i],szText,SizeOf(szText),0)>0 then
begin
Writeln('接收到的信息:'+strpas(sztext));
end;
end
else if sEvent.lNetworkEvents=FD_CLOSE then
begin
if sEvent.iErrorCode[FD_CLOSE_BIT]=0 then
begin
closesocket(sockarray[i]);
for j:=i to nEventTotal-1 do
begin
sockarray[j]:=sockarray[j+1];
eventarray[j]:=eventarray[j+1];
end;
nEventTotal:=nEventTotal-1;
end;
end
else if sEvent.lNetworkEvents=FD_WRITE then
begin

end;

end;
end;
end;
finally
closesocket(sListen); //关闭套接字
end;
finally
WSACleanup; //清除socket。
end;
end.

WSAEVENTSELECT模型最多等待64个事件对象的限制,当套接字连接数量增加时,必须创建多个线程来处理I/O,也就是使用线程池。

在我写的WSAEVENTSELECT模型实例中,我做了测试发现,当我连接的数量越来越多时(当然少于64个连接),服务端的反应就越来越慢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: