iocp简单例子

首先说明:纯iocp使用的例子看:纯iocp例子(里面的代码可能无法运行,但是下面的代码一定可以运行,可以看看它里面的 PostQueuedCompletionStatus函数的使用,参考参考然后拿出来放到下面的代码里测试,搞几下就能懂了),主要涉及api:PostQueuedCompletionStatus(它可以触发一次iocp回调,也就是可以手动触发一次iocp回调,可以用来做多线程环境的锁)


带网络的例子:下方的代码是服务端,客户端后面写

下方代码观看方式(观看思路):

从main函数开始看,或者搜索 WSARecv、AcceptEx、WSASend、CreateIoCompletionPort、GetQueuedCompletionStatus这些关键字

相关函数说明:

WSARecv是注册一个接收数据的iocp,当接收到数据会进入iocp回调函数,也就是 GetQueuedCompletionStatus退出阻塞状态,可以从第二个参数的内存地址得到接收的数据

AcceptEx是注册一个新连接的iocp,当新的连接连接成功后会进入iocp回调函数,也就是 GetQueuedCompletionStatus退出阻塞状态

WSASend是注册一个发送数据的iocp,当发送成功会进入iocp回调函数,也就是 GetQueuedCompletionStatus退出阻塞状态

CreateIoCompletionPort让一个socket绑定一个iocp

GetQueuedCompletionStatus获取一个iocp,如果没有iocp,将进入阻塞

iocp:iocp(完成端口)官方说明

大概意思就是当一个进程创建了iocp之后,操作系统会给创建一个队列,让这个队列与进程进行关联,专门给这个进程处理请求服务

服务端代码说明:

共涉及两个函数 iocp、ThreadFunc函数,从main函数开始看

#pragma comment(lib,"Ws2_32.lib")
#pragma comment(lib,"Mswsock.lib")
//#include <Windows.h>
#include <process.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <mswsock.h>
#include <stdio.h>int i = 0; // 这个不要管,写着玩的
char strarr[999999]{};// 接收数据的缓存,也就是 WSARecv 接收的数据class COverlapped { // 这是重叠结构,用于iocp传参使用
public:/**重叠结构 就是 OVERLAPPED(它的中文意思是重叠,所以称它是重叠结构)*/OVERLAPPED m_overlapped;DWORD m_operator;int clientIndex;char m_buffer[4096];COverlapped() {m_operator = 0;memset(&m_overlapped, 0, sizeof(m_overlapped));memset(m_buffer, 0, sizeof(m_buffer));}
};
HANDLE hIOCP;
SOCKET sock;
//SOCKET client;
SOCKET clients[5];
void iocp() {// SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); // TCPWSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {printf("WSAStartup failed.\n");return;}for (int i = 0; i < 5; i++){clients[i] = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);}// 创建一个套接字用于绑定iocp,让AcceptEx生效sock = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);//client = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);if (sock == INVALID_SOCKET) {int ttt = WSAGetLastError();int a = ttt;return;}// 重建一个iocphIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, sock, 4);// SOCKET client = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);// sock与iocp绑定CreateIoCompletionPort((HANDLE)sock, hIOCP, 0, 0);// 下方是设置套接字ip与端口sockaddr_in addr;addr.sin_family = PF_INET;// PF_INET 与 AF_INET是一个东西,用哪一个都行,混着用也行addr.sin_addr.s_addr = inet_addr("0.0.0.0");// 监听所有ipaddr.sin_port = htons(9527);bind(sock, (sockaddr*)&addr, sizeof(addr)); // ip信息绑定socketlisten(sock, 5);COverlapped overlapped{}; // 创建重叠结构overlapped.m_operator = 1; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用overlapped.clientIndex = 0;DWORD received = 0; // 固定写死// char buffer[4096] = "";// AcceptEx是异步的,调用之后很快就会返回,投递给iocp一个新连接事件if (AcceptEx(sock, clients[overlapped.clientIndex], overlapped.m_buffer, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, &received, &overlapped.m_overlapped) == FALSE) {// WSAGetLastError 获取上一个函数执行结果,只能针对套接字相关使用,它的返回值是上一个函数的执行状态,可以得到执行成功还是执行失败// 它有许多错误类型,可以去 msdn(微软官方文档简称msdn) 搜 WSAGetLastError,然后从 WSAGetLastError函数说明的页面里可以找到返回值都有哪些以及返回值说明if (WSAGetLastError() != WSA_IO_PENDING){return;}}// 到这环境就搞完了,剩下的就是死循环,不要让主线程结束,等待客户端连接触发 AcceptEx 注册的iocp// 然后进入iocp回调,也就是 ThreadFunc 函数里的死循环,然后高性能使用iocp思路// 当有连接发送消息来了之后,第一时间想办法放缓存里,其余操作都不要做// 就只做一个数据放缓存的动作,然后开几个线程从缓存中获取数据然后再处理// iocp的优点就是响应快,可以当线程锁用,如果线程不安全的场景就没必要使用iocp// iocp再快也快不过无锁操作
}int i2 = 1;unsigned int __stdcall  ThreadFunc(void* a) {// 开启线程while (true){LPOVERLAPPED pOverlapped{};DWORD transferred = 0;DWORD key = 0;// transferred可以得到有效字节数,也就是 WSARecv 接收数据的长度// 依据tcp或udp协议不同,数据大会造成分包发送,分包是顺序的,不用担心数据会乱,所以需要在发送的数据包里,设置数据包大小// 然后通过 transferred 判断数据包是否接收完毕,比如 transferred 它的数字小于,我们数据包里设置的大小,就说明数据包没有接收完毕,需要拼接数据// pOverlapped 获取重叠结构,重叠结构是用来使用 iocp 时进行iocp内部的传参// hIOCP 绑定了什么socket,它就触发什么,如果socket没有与它绑定,任何操作都不会触发 GetQueuedCompletionStatus 函数if (GetQueuedCompletionStatus(hIOCP, &transferred, &key, &pOverlapped, WSA_INFINITE)) {COverlapped* pO = CONTAINING_RECORD(pOverlapped, COverlapped, m_overlapped);switch (pO->m_operator){case 1: {printf("AcceptEx\n");// 有客户端连接,这里把客户端连接绑定到 IOCP 里,如果不绑定,WSARecv会失效,WSASend可以发送,但是发送成功无法触发 GetQueuedCompletionStatus 函数的回调CreateIoCompletionPort((HANDLE)clients[pO->clientIndex], hIOCP, 0, 0);/*********************************************************************************************************************************************/DWORD Flags = 0; // 固定写死DWORD dwRecv = 0; // 固定写死COverlapped overlapped3{}; // 创建重叠结构,并初始化内存overlapped3.m_operator = 3; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用overlapped3.clientIndex = pO->clientIndex;WSABUF m_wsabuffer2{}; // 接收数据的缓冲区结构m_wsabuffer2.buf = strarr; // 设置缓冲区,也就是一块存放数据的内存,当Recv成功之后,会把数据写到这里m_wsabuffer2.len = 999999; // 内存大小,buf只是一块内存,只有内存地址没有大小,所以这里告知程序此处内存有多大// 添加Recv监听,告诉iocp只要有Recv操作,也就是有人给我发了消息你就触发 GetQueuedCompletionStatus 函数的回调// 然后通过 m_operator 就能知道这是什么操作,然后在对应的switch里处理WSARecv(clients[overlapped3.clientIndex], &m_wsabuffer2, 1, &dwRecv, &Flags, &overlapped3.m_overlapped, NULL);/*********************************************************************************************************************************************//*============================================================================================================================================*/WSABUF m_wsabuffer{}; // 缓冲区,发送的数据内容DWORD m_received = 0; // 固定写死DWORD m_flags = 0; // 固定写死DWORD received = 0; // 固定写死COverlapped overlapped2{}; // 创建重叠结构overlapped2.m_operator = 2; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用overlapped2.clientIndex = pO->clientIndex;char* str = (char*)"000";m_wsabuffer.buf = str; // 发送的数据内容m_wsabuffer.len = strlen(str) + 1; // 数据大小,C++里的字符串是以0结尾,strlen(str)计算的长度不包含0,所以这里+1// 发送数据WSASend(clients[overlapped3.clientIndex], &m_wsabuffer, 1, &m_received, m_flags, &overlapped2.m_overlapped, NULL);/*============================================================================================================================================*//*--------------------------------------------------------------------------------------------------------------------------------------------*/COverlapped overlapped{}; // 创建重叠结构overlapped.m_operator = 1; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用overlapped.clientIndex = i2++;// 当前是在 case 1 里,也就是当有了新的连接,它就会调用 GetQueuedCompletionStatus 函数的回调,然后再得到这是什么类型,如果类型是1就会进入 case 1 里// 下方的写法,首先AcceptEx是注册一个新连接事件(准确说是投递到iocp的队列里),也就是告诉iocp,当有了新连接就通知 GetQueuedCompletionStatus 函数的回调,通知完AcceptEx注册了事件将被删除// 被删除了,就说明下次没法触发了,所以这里再次注册 AcceptExAcceptEx(sock, clients[overlapped.clientIndex], overlapped.m_buffer, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, &received, &overlapped.m_overlapped);/*--------------------------------------------------------------------------------------------------------------------------------------------*/i++;break;}case 2: { // 这里是 WSASend 成功的处理//printf("send %d--\n", i);printf("recv %d -- %s\n", i, strarr);WSABUF m_wsabuffer{}; // 缓冲区,发送的数据内容DWORD m_received = 0; // 固定写死DWORD m_flags = 0; // 固定写死DWORD received = 0; // 固定写死COverlapped overlapped2{}; // 创建重叠结构overlapped2.m_operator = 2; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用overlapped2.clientIndex = pO->clientIndex;char* str = (char*)"000";m_wsabuffer.buf = str; // 发送的数据内容m_wsabuffer.len = strlen(str) + 1; // 数据大小,C++里的字符串是以0结尾,strlen(str)计算的长度不包含0,所以这里+1// 发送数据WSASend(clients[overlapped2.clientIndex], &m_wsabuffer, 1, &m_received, m_flags, &overlapped2.m_overlapped, NULL);break;}case 3: { // 这里是 WSARecv 成功的处理printf("recv %d -- %s\n", i, strarr);DWORD Flags = 0; // 固定写死DWORD dwRecv = 0; // 固定写死COverlapped overlapped3{}; // 创建重叠结构,并初始化内存overlapped3.m_operator = 3; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用overlapped3.clientIndex = pO->clientIndex;WSABUF m_wsabuffer2{}; // 接收数据的缓冲区结构m_wsabuffer2.buf = strarr; // 设置缓冲区,也就是一块存放数据的内存,当Recv成功之后,会把数据写到这里m_wsabuffer2.len = 999999; // 内存大小,buf只是一块内存,只有内存地址没有大小,所以这里告知程序此处内存有多大// 添加Recv监听,告诉iocp只要有Recv操作,也就是有人给我发了消息你就触发 GetQueuedCompletionStatus 函数的回调// 然后通过 m_operator 就能知道这是什么操作,然后在对应的switch里处理WSARecv(clients[overlapped3.clientIndex], &m_wsabuffer2, 1, &dwRecv, &Flags, &overlapped3.m_overlapped, NULL);break;}default:break;}}}return 0;
}
int main() {// 初始化 iocp 环境iocp();// iocp处理事件线程,主要看 ThreadFunc函数 里的case 1里的代码,看懂了它,就懂iocp网络了_beginthreadex(NULL, 0, ThreadFunc, (LPVOID)hIOCP, 0, NULL);// 避免主线程结束,导致程序结束while (true){}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/688331.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VQ23 请按城市对客户进行排序,如果城市为空,则按国家排序(order by和case when的连用)

代码 select * from customers_info order by (case when city is null then country else city end)知识点 order by和case when的连用

VQ30 广告点击的高峰期(order by和limit的连用)

代码 select hour(click_time) as click_hour ,count(hour(click_time)) as click_cnt from user_ad_click_time group by click_hour order by click_cnt desc limit 1知识点 order by和limit的连用&#xff0c;取出所需结果 YEAR() 返回统计的年份 MONTH() 返回统计的月份 D…

解决Ubuntu下网络适配器桥接模式下ping网址不通的情况

问题反应&#xff1a;ping不通网址 打开虚拟机中的设置&#xff0c;更改网络适配器为NAT模式 确定保存更改之后&#xff0c;退出输入如下命令。 命令1&#xff1a; sudo /etc/network/inferfaces 命令2&#xff1a; sudo /etc/init.d/network/ restart

《生产调度优化》专栏导读

文章分类 生产调度优化问题入门相关问题求解调度问题求解效率探讨相关论文解读 生产调度优化问题入门 文章包含重点简述生产车间调度优化问题两种常用的FJSP模型解析FJSP问题的标准测试数据集的Python代码解析FJSP标准测试数据代码 相关问题求解 文章求解器问题类型【作业车…

使用 C++23 从零实现 RISC-V 模拟器(5):CSR

&#x1f449;&#x1f3fb; 文章汇总「从零实现模拟器、操作系统、数据库、编译器…」&#xff1a;https://okaitserrj.feishu.cn/docx/R4tCdkEbsoFGnuxbho4cgW2Yntc RISC-V为每个hart定义了一个独立的控制状态寄存器&#xff08;CSR&#xff09;地址空间&#xff0c;提供了4…

小程序列表下拉刷新和加载更多

配置 在小程序的app.json中&#xff0c;检查window项目中是否已经加入了"enablePullDownRefresh": true&#xff0c;这个用来开启下拉刷新 "window": {"backgroundTextStyle": "light","navigationBarBackgroundColor": &q…

unity C#中的封装、继承和多态简单易懂的经典实例

文章目录 封装 (Encapsulation)继承 (Inheritance)多态 (Polymorphism) C#中的封装、继承和多态是面向对象编程&#xff08;OOP&#xff09;的三大核心特性。下面分别对这三个概念进行深入解释&#xff0c;并通过实例来说明它们在实际开发中的应用。 封装 (Encapsulation) 实例…

【北京航空航天大学】【信息网络安全实验】【实验一、密码学:DES+RSA+MD5编程实验】

信息网络安全实验 实验一、DES RSA MD5 一、实验目的 1. 通过对DES算法的代码编写,了解分组密码算法的设计思想和分组密码算法工作模式; 2. 掌握RSA算法的基本原理以及素数判定中的Rabin-Miller测试原理、Montgomery快速模乘(模幂)算法,了解公钥加密体制的优缺点及其常…

gem5学习(21):索引策略——Indexing Policies

目录 一、集合关联&#xff08;Set Associative&#xff09; 二、倾斜关联&#xff08;Skewed Associative&#xff09; 索引策略确定基于地址将一个块映射到哪个位置。 索引策略的最重要方法是getPossibleEntries()和regenerateAddr()&#xff1a; getPossibleEntries()用…

数组转二叉树的一种方法-java(很特殊)

上代码 Node节点的代码 public class ThreadNode {private int data;private ThreadNode left;private boolean leftTag; // 左子节点是否为线索private ThreadNode right;private boolean rightTag; // 右子节点是否为线索// ... 省略get和set方法// ... 省略构造方法// ... …

【MySQL】学习多表查询和笛卡尔积

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-N8PeTKG6uLu4bJuM {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

Linux命令-netstat

用于端口和服务之间的故障排除 格式&#xff1a;netstat [常用参数] | grep 端口号/进程名称 -n&#xff1a;显示接口和端口的编号 -t&#xff1a;显示TCP套接字 -u&#xff1a;显示UDP套接字 -l&#xff1a;显示监听中的套接字 -p&#xff1a;显示端口对应的进程信息 -a&a…

一些常见的激活函数介绍

文章目录 激活函数1. sigmoid2. relu3. leakyReLu4. nn.PReLU5. nn.ReLU66. Softplus函数7. softmin, softmax, log softmax8. ELU 激活函数 1. sigmoid https://zhuanlan.zhihu.com/p/172254089 sogmoid函数的梯度范围在 0-0.25&#xff0c; 容易梯度消失 2. relu ReLU激…

1.函数模板基础

1.1函数模板作用&#xff1a; 建立一个通用函数&#xff0c;其函数返回值类型和形参类型可以不具体指定&#xff0c;用一个虚拟的类型来代表&#xff0c;提高复用性 1.2语法&#xff1a; //第一种 template <typename T> 函数声明或定义//第二种 template <class T&…

AI趋势(06) Sora,AI对世界的新理解

说明&#xff1a;使用 黄金圈法则学习和解读Sora&#xff08;what、why、how&#xff09; 1 Sora是什么&#xff1f; 1.1 Sora的基本解读 Sora是OpenAl在2024年2月16日发布的首个文本生成视频模型。该模型能够根据用户输入的文本自动生成长达60秒的1080p复杂场景视频&#xf…

Android稳定性相关知识

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览三、相关方法论3.1 crash3.2 性能3.3 高…

Python:异常处理

异常处理已经成为判断一门编程语言是否成熟的标准&#xff0c;除传统的像C语言没有提供异常机制之外&#xff0c;目前主流的编程语言如Python、Java、Kotlin等都提供了成熟的异常机制。异常机制可以使程序中的异常处理代码和正常业务代码分离&#xff0c;保证代码更加优雅&…

Linux中MySQL表名与@TableName中大小写关系

在使用SpringBoot时&#xff0c;我们普遍会使用注解&#xff0c;实体类中使用注解TableName指明表&#xff0c;以下是TableName的一些注意事项。 【说明】 在MySQL中&#xff0c;表名的大小写处理与操作系统和数据库服务器的配置有关。MySQL默认是在Linux系统上区分大小写的&…

2024年重磅消息:来自OpenAI发布的视频生成模型Sora

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

Jupyter的全面探索:从入门到高级应用

1. 引言 Jupyter项目的简介 Jupyter项目是一个开源项目&#xff0c;旨在为科学计算、数据分析和教育提供交互式计算和数据科学环境。它允许用户创建和共享包含实时代码、方程、可视化以及叙述性文本的文档&#xff0c;这些文档被称为“笔记本”。Jupyter支持超过40种编程语言…