[C++ 网络协议] 异步通知I/O模型
1.什么是异步通知I/O模型
如图是同步I/O函数的调用时间流:
如图是异步I/O函数的调用时间流:
可以看出,同异步的差别主要是在时间流上的不一致。select属于同步I/O模型。epoll不确定是不是属于异步I/O模型,这个在概念上有些混乱,期望大佬的指点。
这里说的异步通知I/O模型,实际上是select模型的改进方案。
2.实现异步通知I/O模型
2.1 实现异步通知I/O模型步骤
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状态的事件对象开始,一个个判断是否siganledint 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以外的其他值。
如:
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模型
1.什么是异步通知I/O模型 如图是同步I/O函数的调用时间流: 如图是异步I/O函数的调用时间流: 可以看出,同异步的差别主要是在时间流上的不一致。select属于同步I/O模型。epoll不确定是不是属于异步I/O模型,这个在概念上有些混乱&a…...

Postgresql事务测试
参考一个事务中 可以查询自己未提交的数据吗_最详细MySQL事务隔离级别及原理讲解!(二)-CSDN博客 一个事务中 可以查询自己未提交的数据吗_趣说数据库事务隔离级别与原理_weixin_39747293的博客-CSDN博客 【MySql:当前读与快照读…...

【数据结构--排序】冒泡排序,选择排序,插入排序
💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …...

vue pc端/手机移动端 — 下载导出当前表格页面pdf格式
一、需求:在手机端/pc端实现一个表格页面(缴费单/体检报告单等)的导出功能,便于用户在本地浏览打印。 二、实现:之前在pc端做过预览打印的功能,使用的是print.js之类的方法让当前页面直接唤起打印机的打印预…...
125. 验证回文串 【简单题】
题目 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。 字母和数字都属于字母数字字符。 给你一个字符串 s,如果它是 回文串 ,返回 true ;否则…...

描述性统计分析
前言: 本专栏参考教材为《SPSS22.0从入门到精通》,由于软件版本原因,部分内容有所改变,为适应软件版本的变化,特此创作此专栏便于大家学习。本专栏使用软件为:SPSS25.0 本专栏所有的数据文件可在个人主页—…...
Visual Studio2019 C++ 编程问题集锦
“const char*” 类型的值不能用于初始化“char*"类型的实体 解决方案一: 点击项目->属性->C/C>语言->符合模式,将原来的“是”改为“否”即可。解决方案二: 在声明变量 char* 时改成 const char *即可...

链表的回文判断
思路: 找中间节点–>逆置->比较 代码: /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* middleNode(struct ListNode* head) { struct ListNode*slowhead; struct ListNode*f…...
281_JSON_两段例子的比较,哪一段更简洁、易懂、没有那么多嵌套
《第一份:》//组装Notificationif (bSendAINotification){BOOST_AUTO(iter_flashnotification, documentAll.FindMember("Notification"));if (iter_flashnotification != documentAll....

想要精通算法和SQL的成长之路 - 最长递增子序列 II(线段树的运用)
想要精通算法和SQL的成长之路 - 最长递增子序列 II(线段树的运用) 前言一. 最长递增子序列 II1.1 向下递推1.2 向上递推1.3 更新操作1.4 查询操作1.5 完整代码: 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 最长递增子序列 II 原题链接…...

java用easyexcel按模版导出
首先在项目的resources下面建一个template包,之后在下面创建一个模版,模版格式如下: 名称为 financeReportBillStandardTemplateExcel.xlsx: {.fee}类型的属性值,是下面实体类的属性,要注意这里面的格式&a…...

Servlet执行流程生命周期方法介绍体系结构、Request和Response的功能详解
🐌个人主页: 🐌 叶落闲庭 💨我的专栏:💨 c语言 数据结构 javaEE 操作系统 Redis 石可破也,而不可夺坚;丹可磨也,而不可夺赤。 Servlet 一、 Servlet执行流程二、Servlet生…...

软件工程之总体设计
总体设计是软件工程中的一个重要阶段,它关注整个系统的结构和组织,旨在将系统需求转化为可执行的软件解决方案。总体设计决定了系统的架构、模块划分、功能组织以及数据流和控制流等关键方面。 可行性研究 具体方面:经济可行性、技术可行性…...

监控员工电脑文件拷贝记录:电脑怎么看员工复制文件的历史记录
在现代企业管理中,数据安全和保密是极其重要的一环。企业需要确保敏感信息不被泄露,以防止可能的法律纠纷和经济损失。为此,许多公司都采取了一些措施来监控员工的电脑使用行为。其中,监控文件拷贝记录是一种常见的方法。本文将详…...
vue中request.js中axios请求和(若依)文件通用下载方法封装
vue中request.js中axios请求和(若依)文件通用下载方法封装 1.request.js import axios from axios import { Message, Loading } from element-ui import { saveAs } from file-saver // 创建axios实例 const request axios.create({// 这里可以放一…...

【大数据存储与处理】1. hadoop单机伪分布安装和集群安装
0. 写在前面 0.1 软件版本 hadoop2.10.2 ubuntu20.04 openjdk-8-jdk 0.2 hadoop介绍 Hadoop是一个由Apache基金会所开发的分布式系统基础架构。用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。Hadoop实现了一个…...

linux通过time命令统计代码编译时间
首先编写一个编译脚本 build.sh 内容如下: 然后执行time sh build.sh 编译完成后输出三个时间 time sh xxx.sh # 会返回3个时间数据 (1) real:从进程 ls 开始执行到完成所耗费的 CPU 总时间。该时间包括 ls 进程执行时实际使用的 CPU 时间,…...
logback日志是怎么保证多线程输出日志线程安全的
logback中的单例模式 logback日志框架使用了单例设计模式来进行日志输出。在logback中,Logger类是一个关键的组件,它负责记录和输出日志消息。 Logger类使用了单例设计模式,确保在一个应用程序中只存在一个Logger实例。这样做的好处是可以确…...
2022年统计用区划代码表SQL 01
行政区划代码为国家公布的六位县级以上行政区划代码 行政区编码的用途: APP里做城市级联选择根据身份证前六位获取用户所在城市区县 370786 昌邑市 370800 济宁市 370811 任城区 370812 兖州区 百度高德等接口通常都会返回adcode字段 (行政区编码)根据 行政区编…...
EM@基本初等函数@幂和根式@指数函数
abstract 基本初等函数幂和根式指数函数 指数和幂 正整指数幂 a n a^{n} an a ⋯ a ⏟ n 个 \underbrace{a\cdots{a}}_{n个} n个 a⋯a, n ∈ N n\in\mathbb{N^{}} n∈N 其中 a n a^{n} an称为** a a a的 n n n次幂** a a a叫做幂的底数, n n n叫做幂的指数 正整指数…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...

搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...