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

一、同步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

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值