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

网络编程(52)—— Windows下使用WSAEventSelect实现异步通知IO

2017-01-04 09:02 627 查看

一、同步IO和异步IO

        同步IO是指发生IO事件的时间点和相关函数返回的时间点一致。如使用send函数发送数据时,所有的数据发送到输出缓冲区后,send函数才会返回,这种IO方式就是同步IO。异步IO指函数先于IO事件返回。还是以send函数为例,调用send函数后其马上返回,而数据传送到输出缓冲区交给操作系统完成。这种IO方式就是同步IO。

        什么是异步IO通知?

        异步IO通知是指每当发生了IO事件——有数据需要写或读,操作系统就会产生一个事件,而我们可以根据这个事件进行相应的处理。

二、使用WSAEventSelect函数监视套接字

        WSAEventSelect函数可以监视一个套接字,当套接字发生IO事件后,它会产生一个异步事件,并将该事件传到WSAEventSelect函数的参数中。WSAEventSelect的函数原型如下:

int WSAEventSelect(
__in SOCKET s,
__in WSAEVENT hEventObject,
__in long lNetworkEvents
);


s —— 需要监视的socket文件描述符。

hEventObject —— 异步事件句柄。当发生lNetworkEvents所指定的的事件时,WSAEventSelect会将该句柄所指的内核对象改为signaled状态。

lNetworkEvents —— 注册的需要监视的事件,按位表示。支持的事件列表如下:

事件宏
描述
FD_READ
是否存在需要读取的数据
FD_WRITE
是否有需要传递的数据
FD_ACCEPT
是否有新的连接请求
FD_CLOSE
是否有需要断开的连接

三、使用WSACreateEvent创建non-signaled事件

        现在,我们需要创建一个man-reset的non-signaled事件,来传递给WSAEventSelect的第二个参数。我们可以选择使用CreateEvent函数,它可以选择创建的事件是man-reset还是auto-reset、signaled还是non-signaled。但是使用WSACreateEvent函数会更加方便,因为它直接就可以创建一个man-reset的non-signaled事件,而无需任何参数。WSACreateEvent的函数原型如下:

HANDLE WSACreateEvent(void);
如果需要关闭事件,则调用WSACloseEvent函数:
BOOL WSACloseEvent(
__in  WSAEVENT hEvent
);

四、使用WSAWaitForMultipleEvents验证是否发生事件

        WSAWaitForMultipleEvents用来验证是否发生了相关的异步事件,其原型如下:

DWORD WSAWaitForMultipleEvents(
__in  DWORD cEvents,
__in  const WSAEVENT *lphEvents,
__in  BOOL fWaitAll,
__in  DWORD dwTimeout,
__in  BOOL fAlertable
);

cEvents —— 需要验证是否转变为signaled事件的总的个数。

lphEvents —— 存放事件句柄的数组。

fWaitAll —— 置为True时,所有事件变成Signaled状态时返回;置为False时,只要发生一个事件变成signaled状态就返回。

dwTimeout —— 设置等待超时,如果设为WSA_INFINITE则一直等待,直到事件变为signaled状态。

fAlertable —— 传递为True时进入alertable wait状态。

返回值 —— 返回值减去WSA_WAIT_EVENT_0时,得到是发生变成signaled状态的事件在lphEvents的索引值(是索引最小的那个事件的索引值)。

五、使用WSAEnumNetworkEvents区分事件类型

        当我们通过使用WSAWaitForMultipleEvents等待到一个事件所指的内核对象变成singnaled状态之后,我们可以使用WSAEnumNetworkEvents来验证该事件的类型,是FD_READ事件、FD_WRITE事件还是FD_ACCEPT事件?

        WSAEnumNetworkEvents的原型如下:

int WSAEnumNetworkEvents(
__in   SOCKET s,
__in   WSAEVENT hEventObject,
__out  LPWSANETWORKEVENTS lpNetworkEvents
);

s —— 是监视的socket描述符。

hEventObject——
是创建的异步通知IO事件。


lpNetworkEvents —— 是WSANETWORKEVENTS结构体对象,该结构体的定义如下:

typedef struct _WSANETWORKEVENTS {
long lNetworkEvents;
int  iErrorCode[FD_MAX_EVENTS];
} WSANETWORKEVENTS, *LPWSANETWORKEVENTS;

WSANETWORKEVENTS里的lNetworkEvents用来保存发生的事件类型,我们可以通过位与操作判断是否是发生了某事件:


If((netEvents.lNetworkEvents &FD_ACCEPT)
{
……
}


iErrorCode保存的是发生的错误码的位数组,通过数组成员判断发生的错误类型:


if(netEvents.iErrorCode[FD_CLOSE_BIT]!=0)
{
puts("close error");
}


六、代码示例

        以下代码,是使用异步IO通知事件实现的服务端代码:

// WSAEventSelectServ.cpp : 定¡§义°?控?制?台¬¡§应®|用®?程¨¬序¨°的Ì?入¨?口¨²点Ì?。¡ê
//

#include "stdafx.h"
#include<stdio.h>
#include<stdlib.h>
#include<winsock2.h>

#pragma comment(lib,"ws2_32.lib")

#define BUF_SIZE 30
#define EVENT_SIZE 64

void ErrorHandler(const char* message);
void CompressEvents(HANDLE* events,int pos,int size);
void CompressSocks(SOCKET* socks,int pos,int size);

int _tmain(int argc, _TCHAR* argv[])
{
WSADATAwsaData;

SOCKETservSock,clntSock;
SOCKADDR_INservAddr,clntAddr;
int clntAddrSz;
SOCKETsocks[EVENT_SIZE];
int strLen;
int eventNum = 0;

char buf[BUF_SIZE];

HANDLEhEvent;
HANDLEevents[EVENT_SIZE];
int minPos;
WSANETWORKEVENTSnetEvents;

if(WSAStartup(MAKEWORD(2,2),&wsaData)==SOCKET_ERROR)
ErrorHandler("WSAStartUp Error");

servSock=socket(AF_INET,SOCK_STREAM,0);
if(servSock==INVALID_SOCKET)
ErrorHandler("socket error");

memset(&servAddr,0,sizeof(servAddr));
servAddr.sin_family=AF_INET;
servAddr.sin_addr.s_addr=INADDR_ANY;
servAddr.sin_port=htons(atoi("8888"));

if(bind(servSock,(constsockaddr*)&servAddr,sizeof(servAddr))==SOCKET_ERROR)
ErrorHandler("bind error");

if(listen(servSock,5)==SOCKET_ERROR)
ErrorHandler("listen error");

hEvent=WSACreateEvent();
//将?用®?来¤¡ä监¨¤视º¨®servSock的Ì?事º?件t放¤?到Ì?第̨²一°?个?位?置?
events[eventNum]=hEvent;
socks[eventNum]=servSock;

//注Á¡é册¨¢servSock
WSAEventSelect(servSock,hEvent,FD_ACCEPT);

while(1)
{
//调Ì¡Â用®?WSAWaitForMultipleEvents等̨¨待äy事º?件t变À?成¨¦signaled状Á¡ä态¬?,ê?设¦¨¨置?等̨¨待äy一°?个?事º?件t即¡ä返¤¦Ì回?
//且¨°不?设¦¨¨置?超?时º¡À,ê?永®¨¤远?等̨¨待äy
minPos=WSAWaitForMultipleEvents(eventNum+ 1,events,false,WSA_INFINITE,false);
//得Ì?到Ì?索¡Â引°y值¦Ì
minPos=minPos-WSA_WAIT_EVENT_0;
//遍À¨¦历¤¨²数ºy组Á¨¦其?他?元a素?,ê?调Ì¡Â用®?WSAWaitForMultipleEvents,ê?验¨¦证¡è其?他?元a素?对?应®|的Ì?内¨²核?对?象¨®是º?否¤?进?入¨?
//signaled状Á¡ä态¬?
for (inti=minPos;i<eventNum + 1;i++)
{
int otherPos = minPos;
if(i!=minPos)
otherPos=WSAWaitForMultipleEvents(1,events,true,0,false);
//排?除y未¡ä编À¨¤程¨¬signaled状Á¡ä态¬?的Ì?事º?件t
if(otherPos==WSA_WAIT_FAILED||otherPos==WSA_WAIT_TIMEOUT)
continue;

WSAEnumNetworkEvents(socks[i],events[i],&netEvents);
if(netEvents.lNetworkEvents & FD_ACCEPT)
{
if(netEvents.iErrorCode[FD_ACCEPT_BIT]!=0)
{
puts("accept error");
break;
}
clntAddrSz=sizeof(clntAddr);
clntSock=accept(socks[i],(SOCKADDR*)&clntAddr,&clntAddrSz);
eventNum++;
socks[eventNum]=clntSock;
events[eventNum]=WSACreateEvent();
WSAEventSelect(socks[eventNum],events[eventNum],FD_READ|FD_CLOSE);
puts("New Client Connected ...");
}

if(netEvents.lNetworkEvents & FD_READ)
{
if(netEvents.iErrorCode[FD_READ_BIT]!=0)
{
puts("recv error");
break;
}
strLen=recv(socks[i],buf,BUF_SIZE,0);
send(socks[i],buf,strLen,0);
}

if(netEvents.lNetworkEvents & FD_CLOSE)
{
if(netEvents.iErrorCode[FD_CLOSE_BIT]!=0)
{
puts("close error");
break;
}
WSACloseEvent(events[i]);
closesocket(socks[i]);
CompressEvents(events,eventNum,EVENT_SIZE);
CompressSocks(socks,eventNum,EVENT_SIZE);
eventNum--;
}

}
}
WSACleanup();
return 0;
}

void ErrorHandler(const char* message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}

void CompressEvents(HANDLE* events,int pos,int size)
{
while(pos<size-1)
{
events[pos]=events[pos+1];
}
}

void CompressSocks(SOCKET* socks,int pos,int size)
{
while(pos<size-1)
{
socks[pos]=socks[pos+1];
}
}


 
 Github位置:
https://github.com/HymanLiuTS/NetDevelopment
克隆本项目:
git clone git@github.com:HymanLiuTS/NetDevelopment.git
获取本文源代码:
git checkout NL52

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐