[C++ 网络协议] IOCP(Input Output Completion Port)

1.什么是IOCP

IOCP(Input Output Completion Port)输入输出完成端口。其实就是基于重叠I/O的一种改进的模型。

重叠I/O具有缺点:重复调用非阻塞模式的accpet函数和以进入alertablewait状态为目的的SleepEx函数会影响程序性能

而IOCP提供的解决方案便是:让主线程调用accept函数,单独创建至少一个线程来负责所有I/O的前后处理

但请不要过分关注在线程上,主要还是如下问题:

        1.I/O是否以非阻塞模式工作?

        2.如何确定非阻塞模式的I/O是否完成?

2.分阶段实现IOCP程序

2.1 实现原理

IOCP会将已完成的I/O信息注册到CP对象(Completion Port完成端口),而我们就可以通过CP对象来获取I/O是否完成的信息,所以有下面两项工作:

  • 创建完成端口对象
  • 建立完成端口对象和套接字之间的联系 

此时的套接字必须赋予重叠属性。

2.2 创建CP对象

#include<windows.h>HANDLE CreateIoCompletionPort(
HANDLE fileHandle,                //创建CP对象时传递INVALID_HANDLE_VALUE
HANDLE ExistingCompletionPort,    //创建CP对象时传递NULL
ULONG_PTR CompletionKey,          //创建CP对象时传递0
DWORD NumberOfConcurrentThreads   //分配给CP对象的用于处理I/O的线程数。//例如:该参数为2时,说明分配给CP对象的可以同时运行的线程数最多为2个//如果为0时,那么系统中CPU的个数就是可同时运行的最大线程数
);
成功返回CP对象句柄
失败返回NULL

2.3 创建和套接字连接完成的端口对象

#include<windows.h>HANDLE CreateIoCompletionPort(
HANDLE FileHandle,                //要连接到CP对象的套接字句柄
HANDLE ExistingCompletionPort,    //要连接套接字的CP对象句柄
ULONG_PTR CompletionKey,          //传递已完成I/O相关信息
DWORD NumberOfConcurrentThreads   //无论传递何值,只要第二个参数非NULL就会被忽略
);
成功返回CP对象句柄
失败返回NULL

函数功能:将FileHandle句柄指向的套接字和ExistingCompletionPort指向的CP对象相连。

调用此函数后:只要针对FileHandle的I/O完成,相关信息就会注册到ExistingCompletionPort里。

注意:第三个参数“传递已完成I/O相关信息”的意思是,你可以像重叠I/O里使用Complition routine来确认I/O方式里把相关信息填写到hEvent里的那样,写入其他信息,这样当I/O完成就可以获取了。

2.4 确认完成端口已完成的I/O和线程I/O处理

#include<windows.h>BOOL GetQueuedCompletionStatus(
HANDLE CompletionPort,        //注册有已完成I/O信息的CP对象句柄
LPDWORD lpNumberOfBytes,      //保存I/O过程中传输的数据大小的变量地址值
PULONG_PTR lpCompletionKey,   //保存CreateIoCompleytionPort函数第三个参数值得变量地址值
LPOVERLAPPED* lpOverlapped,   //保存调用WSASend、WSARecv函数时传递的OVERLAPPED结构体地址的变量地址值
DWORD dwMilliseconds          //超时信息,超过该指定时间后将返回FALSE并跳出函数。//传递INFINITE时,程序将阻塞,直到已完成I/O信息写入CP对象
);
成功返回TRUE
失败返回FALSE

注意:

  • 调用此函数的线程数量不能超过CreateIoCompletionPort时指定的线程数。
  • 此函数并不知道当前是输入信息状态还是输出信息状态,需要自行判断。

3. 实现IOCP模型的回声服务器端

思路:每连接一个客户端就创建一个线程,然后主线程里先接收一次数据,在子线程里通过GetQueuedCompletionStatus函数阻塞住线程,判断I/O状态,接着把接收的数据发送给客户端,再次进入接收状态,如此循环通信。

变量:

struct ClientInfo结构体:存有套接字和套接字地址族信息,在CreateIoCompletionPort函数里,建立套接字和CP的连接的时候,当做第三参数传入

struct CPInfo结构体:存有一个OVERLAPPED、WSABUF信息,以及还有一个int型用来判断当前是RECV还是SEND,在执行WSARecv函数时当做第六个参数进行传入。运用下面的知识点,所以可以在子线程执行GetQueuedCompletionStatus函数时,取得的第一个成员的地址,也就是这整个结构体的地址。

知识点:结构体变量地址值与结构体第一个成员的地址值相同。

struct CPInfo
{OVERLAPPED overlapped;WSABUF wsabuf;int mode;			//0:RECV 1:SEND
};
CPInfo data;
if(&data==&data.overlapped)
{std::cout<<"TRUE"<<std::endl;
}
else
{std::cout<<"FALSE"<<std::endl;
}
输出TRUE
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<iostream>
#include<WinSock2.h>
#include<process.h>
#include<Windows.h>
#include<malloc.h>
#include<string>struct ClientInfo
{SOCKET socket;sockaddr_in socketAddr;
};struct CPInfo
{OVERLAPPED overlapped;WSABUF wsabuf;int mode;			//0:RECV 1:SEND
};unsigned WINAPI threadClient(void* arg);int main()
{WSADATA wsaData;if (0 != WSAStartup(MAKEWORD(2, 2), &wsaData)){std::cout << "start up fail!" << std::endl;return 0;}SOCKET server = WSASocket(PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);if (server == INVALID_SOCKET){std::cout << "socket fail!" << std::endl;return 0;}int mode = 1;ioctlsocket(server, FIONBIO, (u_long*)&mode);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;}while (true){sockaddr_in clientAddr;memset(&clientAddr, 0, sizeof(clientAddr));int clientAddrLen = sizeof(clientAddr);SOCKET client = accept(server, (sockaddr*)&clientAddr, &clientAddrLen);if (client == SOCKET_ERROR){if (WSAGetLastError() == WSAEWOULDBLOCK)	//说明此时没有客户端连接{continue;}std::cout << "accept fail!" << std::endl;}else{HANDLE cpObject = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);if (cpObject == NULL){std::cout << "Create CP fail!" << std::endl;continue;}ClientInfo* clientinfo = new ClientInfo();clientinfo->socket = client;clientinfo->socketAddr = clientAddr;CreateIoCompletionPort((HANDLE)client, cpObject, (ULONG_PTR)clientinfo, 0);unsigned threadId;if (0 == _beginthreadex(NULL, 0, threadClient, (void*)&cpObject, 0, &threadId))	//创建一个线程{std::cout << "thread create fail!" << std::endl;continue;}CPInfo* cpinfo = new CPInfo();cpinfo->mode = 0;memset(&cpinfo->overlapped, 0, sizeof(cpinfo->overlapped));char buff[1024];cpinfo->wsabuf.buf = buff;cpinfo->wsabuf.len = sizeof(buff);DWORD readLen;DWORD flag = 0;WSARecv(client, &cpinfo->wsabuf, 1, &readLen, &flag, &cpinfo->overlapped, NULL);}}closesocket(server);WSACleanup();
}unsigned WINAPI threadClient(void* arg)
{HANDLE cpObject = *(HANDLE*)arg;CPInfo* cpinfo;ClientInfo* clientinfo;while (true){DWORD readLen;GetQueuedCompletionStatus(cpObject, &readLen, (PULONG_PTR)&clientinfo, (LPOVERLAPPED*)&cpinfo, INFINITE);if (readLen == 0){std::cout << "客户端:" << inet_ntoa(clientinfo->socketAddr.sin_addr) << "断开连接!" << std::endl;break;}if (cpinfo->mode == 0)		//recv{std::cout << "客户端发来的消息:" << cpinfo->wsabuf.buf << std::endl;DWORD flag = 0;cpinfo->mode = 1;WSASend(clientinfo->socket, &cpinfo->wsabuf, 1, &readLen, flag, &cpinfo->overlapped, NULL);CPInfo* cpinfo2 = new CPInfo();cpinfo2->mode = 0;memset(&cpinfo2->overlapped, 0, sizeof(cpinfo2->overlapped));char buff[1024];cpinfo2->wsabuf.buf = buff;cpinfo2->wsabuf.len = sizeof(buff);DWORD readLen2;WSARecv(clientinfo->socket, &cpinfo2->wsabuf, 1, &readLen2, &flag, &cpinfo2->overlapped, NULL);}else						//send{delete cpinfo;}}CloseHandle(cpObject);closesocket(clientinfo->socket);return 0;
}

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

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

相关文章

Windows10打开应用总是会弹出提示窗口的解决方法

用户们在Windows10电脑中打开应用程序&#xff0c;遇到了总是会弹出提示窗口的烦人问题。这样的情况会干扰到用户的正常操作&#xff0c;给用户带来不好的操作体验&#xff0c;接下来小编给大家详细介绍关闭这个提示窗口的方法&#xff0c;让大家可以在Windows10电脑中舒心操作…

计算机网络八股

1、请你说说TCP和UDP的区别 TCP提供面向连接的可靠传输&#xff0c;UDP提供面向无连接的不可靠传输。UDP在很多实时性要求高的场景有很好的表现&#xff0c;而TCP在要求数据准确、对速度没有硬件要求的场景有很好的表现。TCP和UDP都是传输层协议&#xff0c;都是为应用层程序服…

大数据——Spark Streaming

是什么 Spark Streaming是一个可扩展、高吞吐、具有容错性的流式计算框架。 之前我们接触的spark-core和spark-sql都是离线批处理任务&#xff0c;每天定时处理数据&#xff0c;对于数据的实时性要求不高&#xff0c;一般都是T1的。但在企业任务中存在很多的实时性的任务需求&…

超大视频如何优雅切片

背景 有一次录屏产生了一个大小为33G的文件, 我想把他上传到B站, 但是B站最大只支持4G. 无法上传, 因此做了一个简单的探索. 质疑与思考 a. 有没有一个工具或一个程序协助我做分片呢? 尝试 a. 必剪 > 有大小限制, 添加素材加不进去(而且报错信息也提示的不对) b. PR &…

C++设计模式_07_Bridge 桥模式

文章目录 1. 动机&#xff08;Motivation&#xff09;2. 代码演示Bridge 桥模式2.1 基于继承的常规思维处理2.2 基于组合关系的重构优化2.3 采用Bridge 桥模式的实现 3. 模式定义4. 结构&#xff08;Structure&#xff09;5. 要点总结 与上篇介绍的Decorator 装饰模式一样&…

从零开始读懂相对论:探索爱因斯坦的科学奇迹

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 引言 阿尔伯特爱因斯坦…

竞赛 机器视觉 opencv 深度学习 驾驶人脸疲劳检测系统 -python

文章目录 0 前言1 课题背景2 Dlib人脸识别2.1 简介2.2 Dlib优点2.3 相关代码2.4 人脸数据库2.5 人脸录入加识别效果 3 疲劳检测算法3.1 眼睛检测算法3.2 打哈欠检测算法3.3 点头检测算法 4 PyQt54.1 简介4.2相关界面代码 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#x…

Transformer预测 | Pytorch实现基于Transformer 的锂电池寿命预测(CALCE数据集)

文章目录 效果一览文章概述模型描述程序设计参考资料效果一览 文章概述 Pytorch实现基于Transformer 的锂电池寿命预测,环境为pytorch 1.8.0,pandas 0.24.2 随着充放电次数的增加,锂电池的性能逐渐下降。电池的性能可以用容量来表示,故寿命预测 (RUL) 可以定义如下: SOH(t…

flutter开发实战-video_player插件播放抖音直播实现(仅限Android端)

flutter开发实战-video_player插件播放抖音直播实现&#xff08;仅限Android端&#xff09; 在之前的开发过程中&#xff0c;遇到video_player播放视频&#xff0c;通过查看video_player插件描述&#xff0c;可以看到video_player在Android端使用exoplayer&#xff0c;在iOS端…

workerman的基本用法(示例详解)

workerman是什么&#xff1f; Workerman是一个异步事件驱动的PHP框架&#xff0c;具有高性能&#xff0c;可轻松构建快速&#xff0c;可扩展的网络应用程序。支持HTTP&#xff0c;Websocket&#xff0c;SSL和其他自定义协议。支持libevent&#xff0c;HHVM&#xff0c;ReactPH…

el-table 设置最大高度且能刚好撑满

max-height"calc(90vh - 120px)"90vh视口高度的90%自行调整即可

解决: 使用html2canvas和print-js打印组件时, 超出高度出现空白页

如果所示&#xff1a;当我利用html2canvas转换成图片后, 然后使用print-js打印多张图片, 第一张会出现空白页 打印组件可参考这个: Vue-使用html2canvas和print-js打印组件 解决: 因为是使用html2canvas转换成图片后才打印的, 而图片是行内块级元素, 会有间隙, 所以被挤下去了…

真香!Jenkins 主从模式解决问题So Easy~

01.Jenkins 能干什么 Jenkins 是一个开源软件项目&#xff0c;是基于 Java 开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件项目可以进行持续集成。 中文官网&#xff1a;https://jenkins.io/zh/ 0…

Docker基础(CentOS 7)

参考资料 hub.docker.com 查看docker官方仓库&#xff0c;需要梯子 Docker命令大全 黑马程序员docker实操教程 &#xff08;黑马讲的真的不错 容器与虚拟机 安装 yum install -y docker Docker服务命令 启动服务 systemctl start docker停止服务 systemctl stop docker重启…

Redis AOF重写原原理

重写aof之前 appendonly.aof.1.base.aof appendonly.aof.1.incr.aof appendonly.aof.manifest 重写aof 一次 appendonly.aof.2.base.aof 大小变化 appendonly.aof.2.incr.aof 大小o appendonly.aof.manifest 大小不变 AOF文件重写并不是对原文件进行重新整理&#xff0c;而是直…

Docker搭建MySQL8.0主从复制(一主一从)

0. 配置说明 宿主机使用的版本为19045的win10专业版&#xff0c;MySQL使用的是8.0&#xff0c;Docker容器使用Linux。 1. 安装Docker Desktop 略 修改Docker默认安装路径 安装包自己就提供了修改安装路径的功能&#xff0c;CMD中运行&#xff1a; “Docker Desktop Installe…

财务明细一目了然,颜色标记记录轻松掌握个人账目!

无论您是想更好地理清个人收支&#xff0c;还是希望在财务管理中更加高效&#xff0c;我们为您推荐一款绝佳的财政管理神器&#xff1a;颜色标记记录&#xff01; 第一步&#xff0c;首先&#xff0c;我们要先进入【晨曦记账本】主页面&#xff0c;并点击上方功能栏里的“添加…

[开源]MIT协议,开源论坛程序,拥有友好的用户界面和操作体验

一、开源项目简介 尤得一物是一个开源论坛程序&#xff0c;提供丰富的功能&#xff0c;可以作为管理或分享文章的论坛博客&#xff0c;也可以在此基础上进行自定义开发。 二、开源协议 使用MIT开源协议 三、界面展示 四、功能概述 尤得一物是一个开源论坛程序&#xff0c;…

如何使用Docker轻松构建和管理应用程序(一)

如今Docker的使用已经非常普遍&#xff0c;特别在一线互联网公司。使用Docker技术可以帮助企业快速水平扩展服务&#xff0c;从而到达弹性部署业务的能力。在云服务概念兴起之后&#xff0c;Docker的使用场景和范围进一步发展&#xff0c;如今在微服务架构越来越流行的情况下&a…

discuz封面设置失败的解决办法(centos系统+windows系统)

discuz封面设置失败的解决办法(centos系统windows系统&#xff09; centos系统&#xff1a;1、开启/var/www/html 这个目录的读写权限chmod -R 777 /var/www/html然后重启httpd&#xff1a;service httpd restart如果discuz论坛发布帖子&#xff0c;还是显示封面设置失败的话…