WSAEventSelect模型是winsock IO模型中比较常见的一个异步模型,它允许应用程序在一个和多个套接字上,接受以事件为基础的网络事件通知。
步骤一:
事件通知模型要求我们针对每一个打算使用的套接字,创建一个事件模型。调用下面的函数:
WSAEVENT WSACreateEvent(void);
函数的返回值就是一个事件的句柄;
步骤二:
事件对象句柄创建成功以后,必须将其与某一个套接字关联,同时注册自己感兴趣的网络事件
int WSAEventSelect(
事件对象句柄创建成功以后,必须将其与某一个套接字关联,同时注册自己感兴趣的网络事件
int WSAEventSelect(
IN SOCKET s, //与之相关联的套接字
IN WSAEVENT hEventObject,//步骤一中的事件句柄
IN long lNetworkEvents //感兴趣的网络时间FD_READ。。。
)
注:该函数创建的事件有两种工作状态:分别是未传信和已传信;工作模式是:人工和自动。
WSACreateEvent创建是未传信的,关联某一个套接字成为已传信,
在IO请求处理完之后,我们写的程序需要把已传信状态改为未传信的状态,使用函数是WSAResetEvent;
步骤三:
套接字与一个事件句柄关联以后,就可以开始IO处理了。方法是等待我们感兴趣的网络事件触发事件句柄的
工作状态。是的下面的函数:
DWORD WSAWaitForMultipleEvents
(
IN DWORD cEvents, //事件句柄表的大小
IN const WSAEVENT FAR * lphEvents, //事件句柄表的指针
IN BOOL fWaitAll, //等待全部事件进入已传信状态为TRUE,任何一个设置为FALSE; //通常是FALSE,一次只为一个套接字事件服务
IN DWORD dwTimeout, //超时时间
IN BOOL fAlertable //忽略为FALSE
)
注意:该函数返回值表示:指向造成函数返回的事件对象。在事件数组上的索引位置是:该返回值减去一个
预定义WSA_WAIT_EVENT_0;知道了事件索引即可找到对应的套接字。
步骤四:
知道了套接字就可以查询出发生了什么类型的网络事件。使用下面的函数:
int WSAEnumNetworkEvents
(
IN SOCKET s, //造成网络事件的套接字
IN WSAEVENT hEventObject,//对应的事件
OUT LPWSANETWORKEVENTS lpNetworkEvents //指向WSANETWORKEVENTS结构;
)
typedef struct _WSANETWORKEVENTS
{
long lNetworkEvents; //对应套接字上发生的网络事件,可以是多个
int iErrorCode[FD_MAX_EVENTS];//错误码;
} WSANETWORKEVENTS, *LPWSANETWORKEVENTS;
最后,对WSANETWORKEVENTS改结构的事件处理完,我们应在所有的可用套接字上继续等待更多的网络事件。
IN WSAEVENT hEventObject,//步骤一中的事件句柄
IN long lNetworkEvents //感兴趣的网络时间FD_READ。。。
)
注:该函数创建的事件有两种工作状态:分别是未传信和已传信;工作模式是:人工和自动。
WSACreateEvent创建是未传信的,关联某一个套接字成为已传信,
在IO请求处理完之后,我们写的程序需要把已传信状态改为未传信的状态,使用函数是WSAResetEvent;
步骤三:
套接字与一个事件句柄关联以后,就可以开始IO处理了。方法是等待我们感兴趣的网络事件触发事件句柄的
工作状态。是的下面的函数:
DWORD WSAWaitForMultipleEvents
(
IN DWORD cEvents, //事件句柄表的大小
IN const WSAEVENT FAR * lphEvents, //事件句柄表的指针
IN BOOL fWaitAll, //等待全部事件进入已传信状态为TRUE,任何一个设置为FALSE; //通常是FALSE,一次只为一个套接字事件服务
IN DWORD dwTimeout, //超时时间
IN BOOL fAlertable //忽略为FALSE
)
注意:该函数返回值表示:指向造成函数返回的事件对象。在事件数组上的索引位置是:该返回值减去一个
预定义WSA_WAIT_EVENT_0;知道了事件索引即可找到对应的套接字。
步骤四:
知道了套接字就可以查询出发生了什么类型的网络事件。使用下面的函数:
int WSAEnumNetworkEvents
(
IN SOCKET s, //造成网络事件的套接字
IN WSAEVENT hEventObject,//对应的事件
OUT LPWSANETWORKEVENTS lpNetworkEvents //指向WSANETWORKEVENTS结构;
)
typedef struct _WSANETWORKEVENTS
{
long lNetworkEvents; //对应套接字上发生的网络事件,可以是多个
int iErrorCode[FD_MAX_EVENTS];//错误码;
} WSANETWORKEVENTS, *LPWSANETWORKEVENTS;
最后,对WSANETWORKEVENTS改结构的事件处理完,我们应在所有的可用套接字上继续等待更多的网络事件。
程序实例:
#include "stdafx.h"
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#define PORT 5150
#define DATA_BUFSIZE 4096*2
typedef struct SOCKET_INFO
{
char Buffer[DATA_BUFSIZE];
WSABUF Databuf;
SOCKET socket;
DWORD bytesRecv;
DWORD bytesSend;
}SOCKET_INFO,*LPSOCKET_INFO;
DWORD EventTotal=0;
WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];
LPSOCKET_INFO SocketArray[WSA_MAXIMUM_WAIT_EVENTS];
BOOL CreateSocketInfo(SOCKET s);
void FreeSocketInfo(DWORD Event);
BOOL CreateSocketInfo(SOCKET s)
{
LPSOCKET_INFO si;
if ((EventArray[EventTotal]=WSACreateEvent())==WSA_INVALID_EVENT)
{
printf("WSACreateEvent() failed with error %d\n", WSAGetLastError());
return FALSE;
}
if ((si=(LPSOCKET_INFO)GlobalAlloc(GPTR,sizeof(LPSOCKET_INFO)))==NULL)
{
printf("GlobalAlloc() failed with error %d\n", GetLastError());
return FALSE;
}
si->socket=s;
si->bytesRecv=0;
si->bytesSend=0;
SocketArray[EventTotal]=si;
EventTotal++;
return TRUE;
}
void FreeSocketInfo(DWORD Event)
{
LPSOCKET_INFO SI = SocketArray[Event];
DWORD i;
closesocket(SI->socket);
GlobalFree(SI);
WSACloseEvent(EventArray[Event]);
for (i = Event; i < EventTotal; i++)
{
EventArray[i] = EventArray[i + 1];
SocketArray[i] = SocketArray[i + 1];
}
EventTotal--;
}
int _tmain(int argc, _TCHAR* argv[])
{
WSAData wsadata;
DWORD ret;
SOCKET slisten;
SOCKET sClient;
SOCKADDR_IN listenaddr;
DWORD dwEvent;
DWORD Flags;
DWORD RecvBytes;
DWORD SendBytes;
WSANETWORKEVENTS NetworkEvents;
if ((ret=WSAStartup(MAKEWORD(2,2),&wsadata))!=0)
{
printf("WSAStartup() failed with error %d\n", ret);
return 0;
}
slisten=socket(AF_INET,SOCK_STREAM,0);
if (slisten==INVALID_SOCKET)
{
printf("socket() failed with error %d\n", ret);
return 0;
}
CreateSocketInfo(slisten);
//为事件关联套接字,以及感兴趣的网络时间
WSAEventSelect(slisten,EventArray[EventTotal-1],FD_ACCEPT|FD_CLOSE);
listenaddr.sin_family=AF_INET;
listenaddr.sin_port=htons(PORT);
listenaddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
if (bind(slisten, (PSOCKADDR)&listenaddr, sizeof(listenaddr)) == SOCKET_ERROR)
{
printf("bind() failed with error %d\n", WSAGetLastError());
return 0;
}
if (listen(slisten, 5))
{
printf("listen() failed with error %d\n", WSAGetLastError());
return 0;
}
while (TRUE)
{
//关联之后开始IO处理
if ((dwEvent=WSAWaitForMultipleEvents(EventTotal,EventArray,FALSE,WSA_INFINITE,FALSE))==WSA_WAIT_FAILED)
{
printf("WSAWaitForMultipleEvents failed with error %d\n", WSAGetLastError());
return 0;
}
//查询发生了什么网络事件
if (WSAEnumNetworkEvents(SocketArray[dwEvent-WSA_WAIT_EVENT_0]->socket, EventArray[dwEvent-WSA_WAIT_EVENT_0], &NetworkEvents) == SOCKET_ERROR)
{
printf("WSAEnumNetworkEvents failed with error %d\n", WSAGetLastError());
return 0;
}
if (NetworkEvents.lNetworkEvents&FD_ACCEPT)
{
if (NetworkEvents.iErrorCode[FD_ACCEPT_BIT] != 0)
{
printf("FD_ACCEPT failed with error %d\n", NetworkEvents.iErrorCode[FD_ACCEPT_BIT]);
break;
}
if ((sClient=accept(SocketArray[dwEvent-WSA_WAIT_EVENT_0]->socket,NULL,NULL)) == INVALID_SOCKET)
{
printf("accept() failed with error %d\n", WSAGetLastError());
break;
}
if (EventTotal > WSA_MAXIMUM_WAIT_EVENTS)
{
printf("Too many connections - closing socket.\n");
closesocket(sClient);
break;
}
CreateSocketInfo(sClient);
if (WSAEventSelect(sClient, EventArray[EventTotal - 1], FD_READ|FD_WRITE|FD_CLOSE) == SOCKET_ERROR)
{
printf("WSAEventSelect() failed with error %d\n", WSAGetLastError());
return 0;
}
printf("Socket %d connected\n", sClient);
}
//读取或者写入事件
if (NetworkEvents.lNetworkEvents & FD_READ|NetworkEvents.lNetworkEvents & FD_WRITE)
{
if (NetworkEvents.lNetworkEvents & FD_READ &&NetworkEvents.iErrorCode[FD_READ_BIT] != 0)
{
printf("FD_READ failed with error %d\n", NetworkEvents.iErrorCode[FD_READ_BIT]);
break;
}
if (NetworkEvents.lNetworkEvents & FD_WRITE&&NetworkEvents.iErrorCode[FD_WRITE_BIT] != 0)
{
printf("FD_WRITE failed with error %d\n", NetworkEvents.iErrorCode[FD_WRITE_BIT]);
break;
}
}
LPSOCKET_INFO SocketInfo=SocketArray[dwEvent-WSA_WAIT_EVENT_0];
//读取
if (SocketInfo->bytesRecv == 0)
{
SocketInfo->Databuf.buf = SocketInfo->Buffer;
SocketInfo->Databuf.len = DATA_BUFSIZE;
Flags = 0;
if (WSARecv(SocketInfo->socket, &(SocketInfo->Databuf), 1, &RecvBytes,&Flags, NULL, NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != WSAEWOULDBLOCK)
{
printf("WSARecv() failed with error %d\n", WSAGetLastError());
FreeSocketInfo(dwEvent - WSA_WAIT_EVENT_0);
return 0;
}
}
else
{
SocketInfo->bytesRecv=RecvBytes;
}
}
//写
if (SocketInfo->bytesRecv> SocketInfo->bytesSend)
{
SocketInfo->Databuf.buf = SocketInfo->Buffer + SocketInfo->bytesSend;
SocketInfo->Databuf.len = SocketInfo->bytesRecv - SocketInfo->bytesSend;
if (WSASend(SocketInfo->socket, &(SocketInfo->Databuf), 1, &SendBytes, 0,NULL, NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != WSAEWOULDBLOCK)
{
printf("WSASend() failed with error %d\n", WSAGetLastError());
FreeSocketInfo(dwEvent - WSA_WAIT_EVENT_0);
return 0;
}
// A WSAEWOULDBLOCK error has occured. An FD_WRITE event will be posted
// when more buffer space becomes available
}
else
{
SocketInfo->bytesRecv += SendBytes;
if (SocketInfo->bytesSend==SocketInfo->bytesRecv)
{
SocketInfo->bytesRecv = 0;
SocketInfo->bytesSend = 0;
}
}
}
if (NetworkEvents.lNetworkEvents & FD_CLOSE)
{
if (NetworkEvents.iErrorCode[FD_CLOSE_BIT] != 0)
{
printf("FD_CLOSE failed with error %d\n", NetworkEvents.iErrorCode[FD_CLOSE_BIT]);
break;
}
printf("Closing socket information %d\n", SocketArray[dwEvent-WSA_WAIT_EVENT_0]->socket);
FreeSocketInfo(dwEvent-WSA_WAIT_EVENT_0);
}
}
return 0;
}