事件通知模型API函数:
WSAEVENT WSACreateEvent(); |
int WSAEventSelect(SCOKET s, WSAEVENT hEventObject, LONG lNetworkEvents); |
BOOL WSAResetEvent(WSAEVENT hEvent); |
BOOL WSACloseEvent(WSAEVENT hEvent); |
DWORD WSAWaitForMultipleEvents( DWORD cEvents, const WSAEVENT FAR * lphEvents, BOOL fWaitAll, DWORD dwTimeout, BOOL fAlertable ); |
int WSAEnumNetworkEvents( SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents ); |
事件通知模型API函数详解:
WSAEVENT WSACreateEvent();
函数返回值: 事件对象句柄。
注:WSACreateEvent创建的事件拥有两种状态,以及两种工作模式。
两种工作状态分别为: ”已传信“ 和 ”未传信“
两种工作模式分别为:”人工重设“和”自动重设“。
WSACreateEvent最开始在一种”未传信“的工作状态中,并用一种”人工重设“模式来创建事件句柄。随着网络事件触发了与一个套接字关联在一起的事件对象,工作状态便会从”未传信“转变成”已传信“。由于事件对象是在一种”人工重设“模式中创建的,所以在完成一个I/O请求的处理后,应用程序需要负责将工作状态从”已传信“更改为”未传信“。可调用 WSAResetEvent函数实现。
int WSAEventSelect(SCOKET s, WSAEVENT hEventObject, LONG lNetworkEvents);
函数功能:关键套接字与事件对象,并注册网络事件。
s: 自己感兴趣的套接字
hEventObject: 指定要与套接字关联在一起的事件对象(用WSACreateEvent取得的那一个)
lNetworkEvents: 对应一个“位掩码”,用于指定应用程序感兴趣的各种网络事件类型组合。事件类型详见下表:
网络事件类型
FD_READ | 接收可读通知,以便读入数据 |
FD_WRITE | 接收可写通知,以便写入数据 |
FD_OOB | 接收有带外数据抵达通知 |
FD_ACCEPT | 接收连接请求通知 |
FD_CONNECT | 接收一次连接或者多点join操作完成通知 |
FD_CLOSE | 接收套接字关闭有关通知 |
FD_QOS | 接收套接字“服务质量”发生改变通知 |
FD_GROUP_QOS | 接收套接字组“服务质量”发生改变通知 |
FD_ROUTING_INTERFACE_CHANGE | 接收在指定方向上与路由接口发生变化通知 |
FD_ADDRESS_LIST_CHANGE | 接收针对套接字协议家族,本地地址列表发生变化通知 |
返回值:函数执行成功返回 0,否则返回 SOCKET_ERROR,可调用WSAGetLastError获得错误代码。
BOOL WSAResetEvent(WSAEVENT hEvent);
函数功能:重置事件对象工作状态
hEvent: 事件句柄。
返回值:成功返回TRUE,失败返回FALSE
BOOL WSACloseEvent(WSAEVENT hEvent);
函数功能:释放事件对象资源
hEvent: 事件句柄
DWORD WSAWaitForMultipleEvents( DWORD cEvents, const WSAEVENT FAR * lphEvents,BOOL fWaitAll, DWORD dwTimeout, BOOL fAlertable );
函数功能:等待一个或多个事件对象句柄,并在事先制定的一个或所有句柄进入”已传信“状态后或在超过一个规定时间周期后,立即返回。
cEvent: 事件对象数量
lphEvents:事件对象指针,用于直接引用事件对象数组。注:该对象数组最大长度为 64
fWaitAll:指定如何等待事件数组中的对象。TRUE为数组中所有对象都已进入”已传信“状态函数返回。FALSE为数组中任何一个对象进入”已传信“状态,函数就会返回。
dwTimeout:指定等待时间,以毫秒为单位。超过规定时间,函数会立即返回,即使fWaitAll规定时间尚未满足。如果超时值为0,函数检查指定事件对象状态,并立即返回。
fAlertable: 在该模型中可忽略,值设为FALSE,该参数主要用于重叠式I/O模型中。
返回值:有网络事件通知时,函数会返回一个值,指出造成函数返回的事件对象。用该值对事件数组中事件进行引用时,应该用该返回值减去预定义值WSA_WAIT_EVENT_0,得到具体索引值。
int WSAEnumNetworkEvents( SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents );
函数功能:查询具体发生的网络事件
s:对应造成网络事件的套接字。
hEventObject: 可选参数。打算重置的事件对象(该重置为自动重置)。
lpNetworkEvents:指向WSANETWORKEVENTS结构指针。用于接收套接字上发生的网络事件类型以及可能出现的任何错误代码。
下面是WSANETWORKEVENTS结构的定义:
typedef struct _WSANETWORKEVENTS {
long lNetworkEvents;//该值为套接字上发生网络事件类型。注:一个事件进入“已传信”状态时,可能会同时发生多个网络事件类型。
int iErrorCode[FD_MAX_EVENTS];//指定了错误代码数组。
} WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;
事件通知创建流程:
步骤1. 创建一个事件对象,WSACreateEvent
步骤2. 将套接字与事件对象关联,并注册感兴趣网络事件,WSAEventSelect
步骤3. 等待网络事件发生, WSAWaitForMultipleEvents
步骤4. 查询具体网络事件, WSAEnumNetworkEvents
步骤5. 处理网络事件,并返回步骤 3
示例代码:
Server:
<span style="background-color: rgb(255, 255, 255);">#include "stdafx.h"
#include <windows.h>
#include <WINSOCK2.H>
#include <string>
#pragma comment(lib, "WS2_32.lib" )
using std::string ;
#define MAX_BUF 1024
char szBuf[MAX_BUF];
SOCKET ArrSocket[ WSA_MAXIMUM_WAIT_EVENTS ] = {0};
WSAEVENT ArrEvents[ WSA_MAXIMUM_WAIT_EVENTS ] = {0};
int ArrIndex[WSA_MAXIMUM_WAIT_EVENTS] = {0};
void CompareArrays( WSAEVENT WsaEvent, SOCKET sSocket, DWORD * pEventTotal );
string GetErrorInfo( DWORD dwError );
int main(int argc, char* argv[])
{
WSAData wsaData;
SOCKET sListen,sAccept;
sockaddr_in ServerAddr;
DWORD EventTotal = 0;
DWORD Index = 0;
WSAEVENT NewEvent;
WSANETWORKEVENTS wsaNetworhEvent;
WSAStartup( MAKEWORD(2,2) ,&wsaData );
sListen = socket( AF_INET, SOCK_STREAM,IPPROTO_TCP );
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.s_addr = inet_addr( "127.0.0.1");
ServerAddr.sin_port = htons(99999);
bind( sListen, (sockaddr *)&ServerAddr, sizeof(sockaddr_in));
NewEvent = WSACreateEvent();
WSAEventSelect( sListen, NewEvent, FD_ACCEPT|FD_CLOSE );
listen(sListen, 5);
ArrEvents[EventTotal] = NewEvent;
ArrSocket[EventTotal] = sListen;
EventTotal++;
while (true)
{
Index = WSAWaitForMultipleEvents(EventTotal, ArrEvents,FALSE, WSA_INFINITE, FALSE );
WSAEnumNetworkEvents( ArrSocket[Index - WSA_WAIT_EVENT_0],
ArrEvents[Index - WSA_WAIT_EVENT_0], //自动重设
&wsaNetworhEvent
);
if( wsaNetworhEvent.lNetworkEvents & FD_ACCEPT )
{
if( wsaNetworhEvent.iErrorCode[FD_ACCEPT_BIT] != 0 )
{
string str = GetErrorInfo(wsaNetworhEvent.iErrorCode[FD_ACCEPT_BIT] );
printf("Accept Faild ErrorCode: %d,ErrorMessge: %s\n",wsaNetworhEvent.iErrorCode[FD_ACCEPT_BIT],str.c_str());
continue;
}
sAccept = accept(ArrSocket[Index - WSA_WAIT_EVENT_0], NULL, NULL );
if( EventTotal > WSA_MAXIMUM_WAIT_EVENTS )
{
closesocket(sAccept);
continue;
}
NewEvent = WSACreateEvent();
WSAEventSelect(sAccept,NewEvent, FD_READ|FD_WRITE|FD_CLOSE );
ArrEvents[EventTotal] = NewEvent;
ArrSocket[EventTotal] = sAccept;
ArrIndex[ EventTotal] = EventTotal;
printf( "FD_Accept Event Socket = %d,connect,Index = %d \n",sAccept,EventTotal );
EventTotal ++;
}
if( wsaNetworhEvent.lNetworkEvents & FD_READ )
{
if( wsaNetworhEvent.iErrorCode[FD_READ_BIT] != 0 )
{
string str = GetErrorInfo( wsaNetworhEvent.iErrorCode[FD_READ_BIT] );
printf( "FD_Read Faild,ErrorCode: %d, ErrorMessage: %s\n",wsaNetworhEvent.iErrorCode[FD_READ_BIT],str.c_str() );
continue;
}
memset(szBuf, 0,MAX_BUF );
recv( ArrSocket[Index - WSA_WAIT_EVENT_0],szBuf,MAX_BUF, 0);
printf("FD_READ Event index = %d, recv Data: %s\n", ArrIndex[Index - WSA_WAIT_EVENT_0] ,szBuf );
strcat( szBuf, " Recv Ok");
send(ArrSocket[Index - WSA_WAIT_EVENT_0],szBuf,strlen(szBuf), 0);
printf("FD_READ Event index = %d, send Data: %s\n", ArrIndex[Index - WSA_WAIT_EVENT_0] ,szBuf );
}
if( wsaNetworhEvent.lNetworkEvents & FD_WRITE )
{
if( wsaNetworhEvent.iErrorCode[FD_WRITE_BIT] != 0 )
{
string str = GetErrorInfo( wsaNetworhEvent.iErrorCode[FD_WRITE_BIT] );
printf("FD_WRITE Faild,ErrorCode: %d, ErrorMessage: %s\n",wsaNetworhEvent.iErrorCode[FD_WRITE_BIT], str.c_str() );
continue;
}
printf("FD_WRITE Event index = %d, send Data: %s\n", ArrIndex[Index - WSA_WAIT_EVENT_0] ,szBuf );
}
if( wsaNetworhEvent.lNetworkEvents & FD_CLOSE )
{
if (wsaNetworhEvent.iErrorCode[FD_CLOSE_BIT] != 0 )
{
string str = GetErrorInfo( wsaNetworhEvent.iErrorCode[FD_CLOSE_BIT] );
printf("FD_CLOSE Faild,ErrorCode: %d, ErrorMessage: %s\n",wsaNetworhEvent.iErrorCode[FD_CLOSE_BIT], str.c_str() );
}
printf("FD_CLOSE Event Index = %d,Close Event \n", ArrIndex[Index - WSA_WAIT_EVENT_0]);
closesocket(ArrSocket[Index - WSA_WAIT_EVENT_0] );
WSACloseEvent( ArrEvents[Index - WSA_WAIT_EVENT_0] );
CompareArrays( ArrEvents[Index - WSA_WAIT_EVENT_0], ArrSocket[Index - WSA_WAIT_EVENT_0], &EventTotal );
}
}
WSACleanup();
return 0;
}
void CompareArrays( WSAEVENT WsaEvent, SOCKET sSocket, DWORD * pEventTotal )
{
UINT i;
for( i = 0; i < (*pEventTotal); ++i )
{
if( ArrSocket[i] == sSocket && ArrEvents[i] == WsaEvent )
{
break;
}
}
if( i< *pEventTotal )
{
for(; i < *pEventTotal - 1; ++i )
{
ArrSocket[i] = ArrSocket[i +1];
ArrEvents[i] =ArrEvents[ i + 1];
ArrIndex[i] = ArrIndex[i+1];
}
(*pEventTotal)--;
}
}
string GetErrorInfo( DWORD dwError )
{
string strErrorInfo;
HLOCAL hlocal = NULL;
DWORD systemLocale = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
BOOL fOk = FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, dwError, systemLocale, (PTSTR) &hlocal, 0, NULL);
if (fOk && (hlocal != NULL))
{
strErrorInfo= (PTSTR)hlocal;
LocalFree(hlocal);
}
return strErrorInfo;
}</span>
Client:
<span style="background-color: rgb(255, 255, 255);">#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <process.h>
#include <WINSOCK2.H>
#pragma comment(lib,"WS2_32.lib")
using namespace std;
#define MAX_BUF 1024
WSAEVENT ArrEvents[WSA_MAXIMUM_WAIT_EVENTS] = {0};
SOCKET ArrScoket[WSA_MAXIMUM_WAIT_EVENTS] = {0};
char szBuf[MAX_BUF];
DWORD WINAPI _RecvThread(LPVOID lpVoid);
int main(int argc, char* argv[])
{
WSADATA wsaData;
WSAStartup( MAKEWORD(2,2), &wsaData );
SOCKET sClient;
WSAEVENT NewEvent;
sockaddr_in ServerAddr;
sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP );
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons(99999);
ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int iR = connect( sClient,(sockaddr *)&ServerAddr, sizeof( sockaddr_in ) );
NewEvent = WSACreateEvent();
WSAEventSelect(sClient,NewEvent, FD_READ | FD_CLOSE);
ArrScoket[0] = sClient;
ArrEvents[0] = NewEvent;
CreateThread( NULL,0,(LPTHREAD_START_ROUTINE)_RecvThread,NULL,0,NULL );
while(true)
{
cin >> szBuf;
if( szBuf[0] == 'q' || szBuf[0] == 'Q' )
{
closesocket(sClient);
break;
}
send(sClient, szBuf, strlen(szBuf), 0);
}
WSACleanup();
return 0;
}
DWORD WINAPI _RecvThread(LPVOID lpVoid)
{
DWORD Index;
WSANETWORKEVENTS wsaNetworkEvent;
while(true)
{
Index = WSAWaitForMultipleEvents(1,ArrEvents,FALSE,WSA_INFINITE, FALSE );
WSAEnumNetworkEvents(ArrScoket[Index - WSA_WAIT_EVENT_0],
ArrEvents[Index - WSA_WAIT_EVENT_0],
&wsaNetworkEvent
);
if( wsaNetworkEvent.lNetworkEvents & FD_READ )
{
if( wsaNetworkEvent.iErrorCode[FD_READ_BIT] != 0 )
{
cout << "FD_READ Faild" << endl;
continue;
}
memset(szBuf, 0, MAX_BUF);
recv(ArrScoket[Index - WSA_WAIT_EVENT_0],szBuf, MAX_BUF, 0 );
cout << "FD_READ Event Recv Data:" << szBuf << endl;
}
if( wsaNetworkEvent.lNetworkEvents & FD_CLOSE )
{
if( wsaNetworkEvent.iErrorCode[FD_CLOSE_BIT] != 0 )
{
cout << "FD_CLOSE Faild ErrorCode: " << wsaNetworkEvent.iErrorCode[FD_CLOSE_BIT] << endl;
}
closesocket(ArrScoket[Index - WSA_WAIT_EVENT_0]);
WSACloseEvent(ArrEvents[Index - WSA_WAIT_EVENT_0] );
cout << "Close Event" << endl;
break;
}
}
return true;
}</span>