前言:
Windows处理用户行为有消息和事件两种行为方式
1、消息机制(WSaAsyncSelect)
- 消息队列:将用户所有的操作(键盘,鼠标等)一次按顺序记录,装进一个队列。
2、事件机制(WSAEventSelect)
- 为用户的操作绑定一个事件,将事件投递给系统,如果操作发生,则事件会被置为有信号,然后获得并处理有信号的事件
3、和select对比
4、WSAEventSelect使用流程:
1、WSACreateEvent:创建一个事件对象(变量)
2、WSAEventSelect:为每个事件对象绑定socket及操作(accept,read,close)并投递给系统
3、WSAWaitDorMultipleEvents :查看事件是否有信号
4、WSAEnumNetworkEvents:有事件分类处理
5、API介绍:
WSACreateEvent:创建网络事件对象
WSAEVENT
WSAAPI
WSACreateEvent(
void
);
demo:
//创建网络对象事件
WSAEVENT eventServer = WSACreateEvent();
if (WSA_INVALID_EVENT == eventServer)
{
//出错了
int a = WSAGetLastError();
//释放
closesocket(socketServer);
//清理网络库
WSACleanup();
return 0;
}
WSAEventSelect:给事件绑定socket操作码,传递给操作系统
int
WSAAPI
WSAEventSelect(
_In_ SOCKET s,//被绑定的socket
_In_opt_ WSAEVENT hEventObject,//被绑定的网络对象事件
_In_ long lNetworkEvents//具体事件
);
参数3,具体事件:多个属性并列可用 | 连接
具体事件 | 事件功能 |
---|---|
FD_ACCEPT | 有客户端连接,与服务器socket绑定 |
FD_READ | 有服务器发来消息,与客户端socket绑定 |
FD_CLOSE | 客户端下线,与客户端socket绑定 |
FD_WRITE | 可以给客户端发送消息,在FD_ACCEPT 后产生信号 |
FD_CONNECT | 有客户端连接,与服务器socket绑定 |
0 | 取消对事件的监听 |
FD_OOB | 带外数据 |
FD_ACCEPT | 有客户端连接,与服务器socket绑定 |
demo:
if (SOCKET_ERROR == WSAEventSelect(socketServer, eventServer, FD_ACCEPT | FD_READ ))
{
//释放事件
WSACloseEvent(eventServer);
//释放socket
closesocket(socketServer);
//清理网络库
WSACleanup();
return 0;
}
3、WSAwaitDorMulTipleEvents:询问事件
功能
- 获取发生事件的信号
DWORD
WSAAPI
WSAWaitForMultipleEvents(
_In_ DWORD cEvents, //事件个数
_In_reads_(cEvents) const WSAEVENT FAR * lphEvents,//事件列表
_In_ BOOL fWaitAll,//等待事件方式
_In_ DWORD dwTimeout,//超时时间
_In_ BOOL fAlertable//是否使用重叠IO
);
参数3:
- TRUE 所有事件都设置威威产生信号才返回
- FALSE
1、任何一个事件产生信号,立即返回,返回值
2、返回值减去WSA_WWAIT_EVENT_0 标识事件对象的索引
3、若在调用期间发出多个事件对象的信号,则为信号事件对象的数组索引
参数4:
- 100 等待100毫秒,超时返回WSA_WAIT_TIMEOUT
- 0 检查事件对象的状态并立即返回
- WSA_INFINTE
等待直到事件发生
参数5:
- TRUE : 使用重叠IO模型
- FALSE: WsaEveentSelect事件选择模型
返回值:
1、数组下标的运算值:
参数3为true :所有事件均有信号
参数3为false:返回值减去WSA_WAIT_EVENT_0 ==> 数组中事件的下标
2、WSA_WAWIT_IO_COMPLETION 参数5为true,返回该值
3、WSA_WAIT_TIMEOUT> 超时,continue
6、demo:
DWORD nRes = WSAWaitForMultipleEvents(esSet.count, esSet.evnetall, FALSE, WSA_INFINITE, FALSE);
if (WSA_WAIT_FAILED == nRes)
{
int a = WSAGetLastError();
printf("错误码:%D\n", a);
break;
}
//错误码,超时时间判断
if (WSA_WAIT_EVENT_0 == nRes)
{
printf("超时");
continue;
}
4、列举事件WSAEnumNetWorkEvents
功能:
获取事件类型,将事件上的信号重置
int
WSAAPI
WSAEnumNetworkEvents(
_In_ SOCKET s, //对应的socket
_In_ WSAEVENT hEventObject, //对应的事件
_Out_ LPWSANETWORKEVENTS lpNetworkEvents //出发的事件类型
);
参数3:
typedef struct _WSANETWORKEVENTS {
long lNetworkEvents; //具体操作
int iErrorCode[FD_MAX_EVENTS];//错误码
} WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;
lNetworkEvents:一个信号可能包含多个消息,以按位或的形式存在
返回值:
- 成功 0
- 失败 SOCKET_ERROR ,调用WSAGetLastError()
demo:
WSANETWORKEVENTS NetworkEvents;
if (SOCKET_ERROR == WSAEnumNetworkEvents(esSet.sockall[nIndex], esSet.evnetall[nIndex], &NetworkEvents))
{
int a = WSAGetLastError();
printf("错误码:%D\n", a);
break;
}
5、分类处理逻辑
因为一个信号可能包含多个信号,此处采用if循环处理各类事件
if (NetworkEvents.lNetworkEvents & FD_ACCEPT)
{
if (NetworkEvents.iErrorCode[FD_ACCEPT_BIT])
{
//正常处理
//创建事件对象
//投递给系统
//装入结构体
}
demo:
#define _CRT_SECURE_NO_WARNINGS
//#define FD_SETSIZE 128
#include <stdio.h>
#include <stdlib.h>
#include <Winsock2.h>
#include <string.h>
#pragma comment(lib, "Ws2_32.lib")
struct fd_es_set
{
unsigned short count;
SOCKET sockall[WSA_MAXIMUM_WAIT_EVENTS];
WSAEVENT evnetall[WSA_MAXIMUM_WAIT_EVENTS];
};
struct fd_es_set esSet[20];
BOOL WINAPI fun(DWORD dwCtrlType)
{
switch (dwCtrlType)
{
case CTRL_CLOSE_EVENT:
//释放所有socket
for (int j = 0; j < 20; j++)
{
for (int i = 0; i < esSet[j].count; i++)
{
closesocket(esSet[j].sockall[i]);
WSACloseEvent(esSet[j].evnetall[i]);
}
}
break;
}
return TRUE;
}
int main(void)
{
SetConsoleCtrlHandler(fun, TRUE);
WORD wdVersion = MAKEWORD(2, 2); //2.1 //22
//int a = *((char*)&wdVersion);
//int b = *((char*)&wdVersion+1);
WSADATA wdScokMsg;
//LPWSADATA lpw = malloc(sizeof(WSADATA));// WSADATA*
int nRes = WSAStartup(wdVersion, &wdScokMsg);
if (0 != nRes)
{
switch (nRes)
{
case WSASYSNOTREADY:
printf("重启下电脑试试,或者检查网络库");
break;
case WSAVERNOTSUPPORTED:
printf("请更新网络库");
break;
case WSAEINPROGRESS:
printf("请重新启动");
break;
case WSAEPROCLIM:
printf("请尝试关掉不必要的软件,以为当前网络运行提供充足资源");
break;
}
return 0;
}
//校验版本
if (2 != HIBYTE(wdScokMsg.wVersion) || 2 != LOBYTE(wdScokMsg.wVersion))
{
//说明版本不对
//清理网络库
WSACleanup();
return 0;
}
SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//int a = WSAGetLastError();
if (INVALID_SOCKET == socketServer)
{
int a = WSAGetLastError();
//清理网络库
WSACleanup();
return 0;
}
struct sockaddr_in si;
si.sin_family = AF_INET;
si.sin_port = htons(12345);
si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//int a = ~0;
if (SOCKET_ERROR == bind(socketServer, (const struct sockaddr*) & si, sizeof(si)))
{
//出错了
int a = WSAGetLastError();
//释放
closesocket(socketServer);
//清理网络库
WSACleanup();
return 0;
}
if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))
{
//出错了
int a = WSAGetLastError();
//释放
closesocket(socketServer);
//清理网络库
WSACleanup();
return 0;
}
//memset(esSet, 0, sizeof(struct fd_es_set)*20);
//创建事件
WSAEVENT eventServer = WSACreateEvent();
if (WSA_INVALID_EVENT == eventServer)
{
//出错了
int a = WSAGetLastError();
//释放
closesocket(socketServer);
//清理网络库
WSACleanup();
return 0;
}
if (SOCKET_ERROR == WSAEventSelect(socketServer, eventServer, FD_ACCEPT))
{
//出错了
int a = WSAGetLastError();
//释放事件句柄
WSACloseEvent(eventServer);
//释放所有socket
closesocket(socketServer);
//清理网络库
WSACleanup();
return 0;
}
//装进去
esSet[0].evnetall[esSet[0].count] = eventServer;
esSet[0].sockall[esSet[0].count] = socketServer;
esSet[0].count++;
while (1)
{
for (int j = 0; j < 20; j++)
{
if (0 == esSet[j].count)
{
continue;
}
DWORD nRes = WSAWaitForMultipleEvents(esSet[j].count, esSet[j].evnetall, FALSE, 0, FALSE);
if (WSA_WAIT_FAILED == nRes)
{
int a = WSAGetLastError();
//出错了
printf("错误码:%d\n", a);
break;
}
if (WSA_WAIT_TIMEOUT == nRes)
{
continue;
}
DWORD nIndex = nRes - WSA_WAIT_EVENT_0;
for (int i = nIndex; i < esSet[j].count; i++)
{
DWORD nRes = WSAWaitForMultipleEvents(1, &esSet[j].evnetall[i], FALSE, 0, FALSE);
if (WSA_WAIT_FAILED == nRes)
{
int a = WSAGetLastError();
//出错了
printf("错误码:%d\n", a);
continue;
}
//超时使用
if (WSA_WAIT_TIMEOUT == nRes)
{
continue;
}
//得到下标对应的具体操作
WSANETWORKEVENTS NetworkEvents;
if (SOCKET_ERROR == WSAEnumNetworkEvents(esSet[j].sockall[i], esSet[j].evnetall[i], &NetworkEvents))
{
int a = WSAGetLastError();
//出错了
printf("错误码:%d\n", a);
break;
}
if (NetworkEvents.lNetworkEvents & FD_ACCEPT)
{
if (0 == NetworkEvents.iErrorCode[FD_ACCEPT_BIT])
{
//正常处理
SOCKET socketClient = accept(esSet[j].sockall[i], NULL, NULL);
if (INVALID_SOCKET == socketClient)
{
continue;
}
//创建事件对象
WSAEVENT wsaClientEvent = WSACreateEvent();
if (WSA_INVALID_EVENT == wsaClientEvent)
{
closesocket(socketClient);
continue;
}
//投递给系统
if (SOCKET_ERROR == WSAEventSelect(socketClient, wsaClientEvent, FD_READ | FD_CLOSE | FD_WRITE))
{
closesocket(socketClient);
WSACloseEvent(wsaClientEvent);
continue;
}
for (int m = 0; m < 20; m++)
{
if (esSet[m].count < 64)
{
//装进结构体
esSet[m].sockall[esSet[m].count] = socketClient;
esSet[m].evnetall[esSet[m].count] = wsaClientEvent;
esSet[m].count++;
break;
}
}
printf("accept event\n");
}
else
{
continue;
}
}
if (NetworkEvents.lNetworkEvents & FD_WRITE)
{
if (0 == NetworkEvents.iErrorCode[FD_WRITE_BIT])
{
//初始化
if (SOCKET_ERROR == send(esSet[j].sockall[i], "connect success", strlen("connect success"), 0))
{
int a = WSAGetLastError();
printf("send faild, error code:%d\n", a);
continue;
}
printf("write event\n");
}
else
{
printf("socket error code:%d\n", NetworkEvents.iErrorCode[FD_WRITE_BIT]);
continue;
}
}
if (NetworkEvents.lNetworkEvents & FD_READ)
{
if (0 == NetworkEvents.iErrorCode[FD_READ_BIT])
{
char strRecv[1500] = { 0 };
if (SOCKET_ERROR == recv(esSet[j].sockall[i], strRecv, 1499, 0))
{
int a = WSAGetLastError();
printf("recv faild, error code:%d\n", a);
continue;
}
printf("recv data: %s\n", strRecv);
}
else
{
printf("socket error code:%d\n", NetworkEvents.iErrorCode[FD_READ_BIT]);
continue;
}
}
if (NetworkEvents.lNetworkEvents & FD_CLOSE)
{
//打印
printf("client close\n");
printf("client force out: %d\n", NetworkEvents.iErrorCode[FD_CLOSE_BIT]);
//清理下线的客户端 套接字 事件
//套接字
closesocket(esSet[j].sockall[i]);
esSet[j].sockall[i] = esSet[j].sockall[esSet[j].count - 1];
//事件
WSACloseEvent(esSet[j].evnetall[i]);
esSet[j].evnetall[i] = esSet[j].evnetall[esSet[j].count - 1];
//数量减一
esSet[j].count--;
}
}
}
}
for (int j = 0; j < 20; j++)
{
for (int i = 0; i < esSet[j].count; i++)
{
closesocket(esSet[j].sockall[i]);
WSACloseEvent(esSet[j].evnetall[i]);
}
}
//清理网络库
WSACleanup();
system("pause");
return 0;
}