[C++ 网络协议] 异步通知I/O模型

这篇具有很好参考价值的文章主要介绍了[C++ 网络协议] 异步通知I/O模型。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.什么是异步通知I/O模型

如图是同步I/O函数的调用时间流:

[C++ 网络协议] 异步通知I/O模型,网络协议,c++,网络协议

如图是异步I/O函数的调用时间流:

[C++ 网络协议] 异步通知I/O模型,网络协议,c++,网络协议

可以看出,同异步的差别主要是在时间流上的不一致。select属于同步I/O模型。epoll不确定是不是属于异步I/O模型,这个在概念上有些混乱,期望大佬的指点

这里说的异步通知I/O模型,实际上是select模型的改进方案。

2.实现异步通知I/O模型

2.1 实现异步通知I/O模型步骤

[C++ 网络协议] 异步通知I/O模型,网络协议,c++,网络协议

2.2 WSAEventSelect函数

#include<winsock2.h>

int WSAEventSelect(
SOCKET s,                //监视对象的套接字句柄
WSAEVENT hEventObject,   //传递事件对象句柄以验证事件发生与否
long lNetworkEvents      //监视的事件类型信息
);
成功返回0
失败返回SOCKET_ERROR

参数hEventObject:

#define WSAEVENT HANDLE

WSAEVENT就是HANDLE。

参数lNetworkEvents:

含义
FD_READ 是否存在需要接收的数据
FD_WRITE 能否以非阻塞的方式传输数据
FD_OOB 是否收到带外数据
FD_ACCEPT 是否有新的连接请求
FD_CLOSE 是否有断开连接的请求

可以通过位或运算指定多个信息。

函数解释:

传入的套接字参数s,只要s发送lNetworkEvents事件,就会将hEventObject事件对象所指内核对象的状态,改为signaled状态。

与select函数的比较:

每个通过WSAEventSelect函数注册的套接字信息就已经注册到操作系统中了,这意味着,无需针对已注册的套接字重复调用WSAEventSelect。

还有一个实现方式是WSAAsyncSelect函数,使用这个函数时需要指定Windows句柄以获取发生的事件(跟UI有关)

2.3 创建WSAEVENT对象

创建manual-reset模式的事件对象。

方式一:

使用“windows中的线程同步”中所讲的CreateEvent函数。

方式二:

#include<winsock2.h>

WSAEVENT WSACreateEvent(void);
成功返回事件对象句柄
失败返回WSA_INVALID_EVENT

这种方式会直接创建manual-reset模式的事件对象。 其销毁函数:

#include<winsock2.h>

BOOL WSACloseEvent(WSAEVENT hEvent);
成功返回TRUE
失败返回FALSE

2.4 验证是否发生了事件

#include<winsock2.h>

DWORD WSAWaitForMultipleEvent(
DWORD cEvents,                //需要验证是否转为signaled状态的事件对象个数
const WSAEVENT* lphEvents,    //存有事件对象句柄的数组地址值
BOOL fWaitAll,                //TRUE,所有事件对象都在signaled状态时返回
                              //FALSE,只要其中1个变为signaled状态就返回
DWORD dwTimeout,              //以1/1000秒为单位指定超时,传递WSA_INFINITE时,直到signaled状态时才返回
                              //传递0时,表明不阻塞,是否是signaled状态都返回
BOOL fAlertable               //传递TRUE可进入alertable_wait(可警告等待)状态
);
成功:
返回值减去WSA_WAIT_EVENT_0时,可以得到第一个转变为signaled状态的事件对象句柄对应的索引,可在第二个参数中查找对应句柄。
超时则返回WSA_WAIT_TIMEOUT。
失败:
返回WSA_WAIT_FAILED(注意,原版书籍里这里打印错了)

最多可监视的事件对象数量为:WSA_MAXIMUM_WAIT_EVENTS常量。

要想监视更多,要么创建线程,要么扩展保存句柄的数组并多次调用这个函数。

注意:参数fwaitAll为FALSE时,是说只要其中1个变为signaled状态就返回,函数是返回了,但其有可能有多个事件对象变为了signaled状态

通过事件对象为manual-reset模式的特点,可以获取转为signaled状态的所有事件对象的句柄。

int start;
WSAEVENT events[num];
start=WSAWaitForMultipleEvents(num,events,FALSE,WSA_INFINITE,FALSE);
int first=start-WSA_WAIT_EVENT_0;
for(int i=first,i<num;++i)    //first是变为singaled状态的事件对象的索引的最小值
{
    //从第一个的signaled状态的事件对象开始,一个个判断是否siganled
    int sigEventIdx=WSAWaitForMultipleEvents(1,&events[i],TRUE,0,FALSE);
    ......
}

2.5 区分事件类型

#include<winsock2.h>

int WSAEnumNetworkEvents(
SOCKET s,                            //发生事件的套接字句柄
WSAEVENT hEventObject,               //与套接字相连的signaled状态的事件对象句柄
LPWSANETWORKEVENTS lpNetworkEvents   //保存发生的事件类型信息和错误信息的
                                     //WSANETWORKEVENTS结构体变量地址值
);
成功返回0
失败返回SOCKET_ERROR
struct _WSANETWORKEVENTS
{
    long lNetworkEvents;            //事件类型
    int iErrorCode[FD_MAX_EVENTS];  //错误信息
}WSANETWORKEVENTS,*LPWSANETWORKEVENTS;

事件类型的验证:

就是FD_READ、FD_ACCEPT等,和WSAEventSelect第三个参数一样。

错误信息的验证:

如果发生FD_XXX相关错误,则在iErrorCode[FD_XXX_BIT]中保存除0以外的其他值。

如:文章来源地址https://www.toymoban.com/news/detail-730439.html

WSANETWORKEVENTS netEvents;
......
WSAEnumNetworkEvents(hSock,hEvent,netEvents);
......
if(netEvents.lNetworkEvents & FD_ACCEPT)
{
    ......
}
......
if(netEvents.iErrorCode[FD_READ_BIT]!=0)
{
    ......
}

3.用异步通知I/O模型实现回声服务器端

#include<iostream>
#include<WinSock2.h>
#include<Windows.h>
#include<process.h>
#include<string>
#include<vector>

std::vector<SOCKET> vecSocket;
std::vector<WSAEVENT> vecEvent;

void ErrorHandle(WSANETWORKEVENTS network);

int main()
{
	WSADATA wsaData;
	if (0 != WSAStartup(MAKEWORD(2, 2), &wsaData))
	{
		std::cout << "start up fail!" << std::endl;
		return 0;
	}
	SOCKET server = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (server == INVALID_SOCKET)
	{
		std::cout << "socket fail!" << std::endl;
		return 0;
	}

	sockaddr_in serverAddr;
	memset(&serverAddr, 0, sizeof(serverAddr));
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	serverAddr.sin_port = htons(9130);

	if (SOCKET_ERROR == bind(server, (sockaddr*)&serverAddr, sizeof(serverAddr)))
	{
		std::cout << "bind fail!" << std::endl;
		return 0;
	}
	if (SOCKET_ERROR == listen(server, 2))
	{
		std::cout << "listen fail!" << std::endl;
		return 0;
	}
	
	WSAEVENT serverEvent=WSACreateEvent();
	if (SOCKET_ERROR == WSAEventSelect(server, serverEvent, FD_ACCEPT))
	{
		std::cout << "event select fail!" << std::endl;
		return 0;
	}

	vecSocket.push_back(server);
	vecEvent.push_back(serverEvent);

	while (1)
	{
		int eventSize = vecEvent.size();
		int res=WSAWaitForMultipleEvents(eventSize, vecEvent.data(), FALSE, WSA_INFINITE, FALSE);
		int a = (int)WSA_INVALID_EVENT;
		if (res == WSA_WAIT_FAILED)
		{
			std::cout << "wait fail!" << std::endl;
			//continue;
		}
		int first = res - WSA_WAIT_EVENT_0;
		for (int i = first; i < eventSize; ++i)
		{
			int sig = WSAWaitForMultipleEvents(1, &vecEvent[i], TRUE, 0, FALSE);
			if (sig == WSA_WAIT_FAILED)
				continue;
			int index = sig - WSA_WAIT_EVENT_0;
			WSANETWORKEVENTS network;
			int result = WSAEnumNetworkEvents(vecSocket[i], vecEvent[i], &network);
			if (result == SOCKET_ERROR)
			{
				ErrorHandle(network);
			}
			else
			{
				if (network.lNetworkEvents & FD_ACCEPT)
				{
					SOCKET client;
					sockaddr_in clientAddr;
					memset(&clientAddr, 0, sizeof(clientAddr));
					int clientAddrLen = sizeof(clientAddr);
					client=accept(vecSocket[i], (sockaddr*)&clientAddr, &clientAddrLen);
					if (INVALID_SOCKET==client)
					{
						std::cout << "accept fail!" << std::endl;
						continue;
					}
					else
					{
						WSAEVENT clientEvent = WSACreateEvent();
						WSAEventSelect(client, clientEvent, FD_READ|FD_CLOSE);

						vecSocket.push_back(client);
						vecEvent.push_back(clientEvent);
					}
				}
				else if (network.lNetworkEvents & FD_READ)
				{
					char buff[1024];
					int readLen=recv(vecSocket[i], buff, sizeof(buff), 0);
					std::cout << "客户端发来的消息:" << buff << std::endl;
					if (readLen != 0)
					{
						send(vecSocket[i], buff, readLen, 0);
					}
				}
				else if (network.lNetworkEvents & FD_CLOSE)
				{
					closesocket(vecSocket[i]);
					CloseHandle(vecEvent[i]);
					auto itSocket = vecSocket.begin() + i;
					if(itSocket<vecSocket.end())
						vecSocket.erase(itSocket);
					auto itEvent = vecEvent.begin() + i;
					if (itEvent < vecEvent.end())
						vecEvent.erase(itEvent);
				}
			}
		}
	}
	CloseHandle(serverEvent);
	closesocket(server);
	WSACleanup();
	return 0;
}

void ErrorHandle(WSANETWORKEVENTS network)
{
	if (network.iErrorCode[FD_ACCEPT_BIT]!=0)
	{
		std::cout << "accept error!" << std::endl;
	}
	else if (network.iErrorCode[FD_READ_BIT] != 0)
	{
		std::cout << "read error!" << std::endl;
	}
	else if (network.iErrorCode[FD_CLOSE_BIT] != 0)
	{
		std::cout << "close error!" << std::endl;
	}
}

到了这里,关于[C++ 网络协议] 异步通知I/O模型的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 06-kafka及异步通知文章上下架

    1)自媒体文章上下架 需求分析 2)kafka概述 消息中间件对比 特性 ActiveMQ RabbitMQ RocketMQ Kafka 开发语言 java erlang java scala 单机吞吐量 万级 万级 10万级 100万级 时效性 ms us ms ms级以内 可用性 高(主从) 高(主从) 非常高(分布式) 非常高(分布式) 功能特性 成熟的产品、较全的

    2024年04月11日
    浏览(32)
  • Linux学习第26天:异步通知驱动开发: 主动

    Linux版本号4.1.15   芯片I.MX6ULL                                    大叔学Linux    品人间百味  思文短情长          在正式开启今天的学习前,讲一讲为什么标题中加入了【主动】俩字。之前学习的阻塞和非阻塞IO,都是在被动的接受应用程序的操作。而今天的学习的异

    2024年02月06日
    浏览(23)
  • 【黑马头条之kafka及异步通知文章上下架】

    本笔记内容为黑马头条项目的kafka及异步通知文章上下架部分 目录 一、kafka概述 二、kafka安装配置 三、kafka入门 四、kafka高可用设计 1、集群 2、备份机制(Replication) 五、kafka生产者详解 1、发送类型 2、参数详解 六、kafka消费者详解 1、消费者组 2、消息有序性 3、提交和偏移

    2024年02月14日
    浏览(36)
  • 06-kafka及异步通知文章上下架-黑马头条

    1)自媒体文章上下架 需求分析 2)kafka概述 消息中间件对比 特性 ActiveMQ RabbitMQ RocketMQ Kafka 开发语言 java erlang java scala 单机吞吐量 万级 万级 10万级 100万级 时效性 ms us ms ms级以内 可用性 高(主从) 高(主从) 非常高(分布式) 非常高(分布式) 功能特性 成熟的产品、较全的

    2024年04月11日
    浏览(21)
  • 【不靠谱程序员】接收到回调通知的异步处理

    ​ 支付系统中,像资金下发这种业务,通常是在我们系统发给第三方支付通道后,第三方支付通道会进行资金业务处理。然后,付款完成后,会主动发起回调,即,调用我们系统API,将付款结果通知给我们系统。 假定我们的支付系统对三方通道回调通知的处理逻辑包括:①

    2024年02月08日
    浏览(32)
  • STM32MP157驱动开发——按键驱动(异步通知)

    Linux 系统中也有很多信号,在 Linux 内核源文件 includeuapiasm-genericsignal.h 中,有很多信号的宏定义: 就 APP 而言,你想处理 SIGIO 信息,那么需要提供信号处理函数,并且要跟 SIGIO 挂钩。这可以通过一个 signal 函数 来“给某个信号注册处理函数”,用法如下: 重点从②开始:

    2024年02月15日
    浏览(34)
  • 网络模型与网络协议

    网络协议是为计算机网络中进行数据交换而建立的规则、标准或者说是约定的集合。因为不同用户的数据终端可能采取的字符集是不同的,两者需要进行通信,必须要在一定的标准上进行。一个很形象地比喻就是我们的语言,我们国家地广人多,地方性语言也非常丰富,而且

    2024年01月18日
    浏览(20)
  • OSI/RM七层网络模型和网络协议

    OSI/RM (Open System Interconnection/Reference Model,开放式系统互联参考模型)是一个由国际标准化组织(ISO)制定的网络参考模型,它将计算机网络通信划分为七个层次。每一层都有一个特定的功能,它为设计和实现网络的各个方面提供结构参考。 1.1 结构图 1.2 各层功能 OSI七层网络

    2024年02月12日
    浏览(26)
  • 网络基础 二 OSI七层模型与网络协议

    OSI/RM------开放式系统互联参考模型 数据链路层:介质访问控制层MAC+逻辑链路控制层LLC 逻辑链路控制层LLC:对数据惊醒校验,只保障数据完整性;同时增加FCS(校验核),校验数据完整性。 应用层:抽象语言----编码 表示层:编码---二进制 网络层:IP 互联网协议   数据链路

    2024年02月19日
    浏览(30)
  • Java中网络的基本介绍。网络通信,网络,ip地址,域名,端口,网络通信协议,TCP/IP传输过程,网络通信协议模型,TCP协议,UDP协议

    - 网络通信 概念:网络通信是指 通过计算机网络进行信息传输的过程 ,包括数据传输、语音通话、视频会议等。在网络通信中,数据被分成一系列的数据包,并通过网络传输到目的地。在数据传输过程中,需要确保数据的完整性、准确性和安全性。常见的网络通信协议有T

    2024年02月10日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包