Windows网络编程~TCP/IP~WsaEveentSelect事件选择模型(五)

前言:
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值