IOCP技术详解

article/2025/11/8 10:32:45

这几周我接触了Windows网络通讯中的IOCP模型,自己在网上找了相关的知识进行学习,自己又下了好多服务器端的代码,但都运行不了,也是自己菜,能力还需加强。幸好我师父资助了我一个能运行的服务端IOCP代码,自己参照网上的相关知识后又与这个能运行的代码做了参照,算是勉强理解了构造IOCP的一般方法,对IOCP的使用也有了很大的心得。接下来我就把自己对IOCP相关知识的理解记录下来,方便自己以后的复习,当然这篇文章如果对正在阅读的你有帮助也算是很好的。

Windows下的六种通讯模型

讲IOCP之前先把它之前的五种通讯模型讲下,由于异步选择模型、事件选择模型和重叠I/O模型没有接触过,所以就不再评论。

阻塞模型

我之前写过Windows下socket编程,在这篇文章中写的TCP服务端代码使用的就是阻塞模型。在阻塞模型中,send和recv时要看对应的缓冲区中是否有数据,有过有数据就进行发送,如果没有数据send或recv行为就进入阻塞等待状态,直到缓冲区内有数据进入为止。该模型效率比较低下。大致创建步骤如下:
客户端

  • connect
  • send
  • recv
  • closesocket
    服务端:
  • bind
  • listen
  • accept
  • recv
  • send
    示意图如下:
    在这里插入图片描述
    该模式只是简单的实现了服务端和客户端的通讯问题,对多线程、服务器并发效率等问题并没有进行考虑,算是最原始的网络通讯模型。

选择模型

选择(select)模型是Winsock中最常见的 I/O模型。核心便是利用 select 函数,实现对 I/O的管理!利用 select 函数来判断某Socket上是否有数据可读,或者能否向一个套接字写入数据,防止程序在Socket处于阻塞模式中时,在一次 I/O 调用(如send或recv、accept等)过程中,被迫进入“锁定”状态;同时防止在套接字处于非阻塞模式中时,产生WSAEWOULDBLOCK错误。
select函数原型:

int select(__in          int nfds,__in_out      fd_set* readfds,//检查可读性__in_out      fd_set* writefds,//检查可写性__in_out      fd_set* exceptfds,//用于例外数据__in          const struct timeval* timeout
);

异步选择模型

没接触,不评论。

事件选择模型

没接触,不评论。

重叠I/O模型

没接触,不评论。

IOCP模型

好嘞,终于来到了我们的重头戏——IOCP模型,IOCP模型又叫完成端口。IOCP(输入输出完成端口)服务器端模型是Windows上网络模型的一个重点。IOCP简单的说明就是创建专用的I/O线程,该线程负责与所有客户端进行I/O,类似于one connection one thread,但是IOCP并不会无限制的创建线程,而是在并行的线程之间有一个上限。
IOCP的完成端口不是指TCP/IP端口号,而是一个消息队列,当某项I/O完成之后,其对应的工作者就会收到一个通知,然后进行其他的操作。IOCP是进行的异步I/O,其依赖于一个工作者线程池。使用工作者线程池限制线程的数量以避免创建太多thread而导致在切换线程时浪费大量的时间。IOCP会充分利用Windows内核来进行I/O的调度,是用于C/S通信模式中性能最好的网络通信模型,没有之一;

  • IOCP模型的优点:
  1. 异步通讯

IOCP使用的是异步通讯的机制,避免了因为信息阻塞而耽误后续进程的执行。所以高性能的服务器模型一定是异步的

  1. 效率高
    IOCP因为采用了异步通讯的机制,和阻塞模型相比避免了阻塞的问题,大大提高了效率。和“阻塞通信+多线程”相比,减少了进程之间切换造成的时间浪费,所以相较于其他模型,IOCP的效率很高。

IOCP实现详解

首先对IOCP模型中用到的结构体和函数模型解释说明下:

  1. 单句柄数据:该结构体用于管理具体哪个socket在进行io请求操作。
typedef struct PER_HANDLE_DATA{SOCKET 				socket;SOCKADDR_IN 		addr;
}*pPER_HANDLE_DATA,PER_HANDLE_DATA;
  1. 单I/O数据:该结构体用于每一次客户端socket向IOCP提交请求时提交给系统,在其结构内可定义任意参数(overlapped必须在第一个),当请求完成后,IOCP**“原封不动”**的返回到工作线程,但给相应参数进行了赋值,如用于接收数据的buffer数组。
typedef struct PER_IO_DATA{OVERLAPPED       overlapped;        //类似id,每个io都必须有一个SOCKET			 socket;            //io请求的套接字WSABUF			 wsabuf;            //用于从缓冲区获取数据的结构char			 buffer[LENGTH];    //保存获得的数据OPT_TYPE         opt_type;          //这次io请求的类型,如ACCEPT,RECV,SEND等
}*pPER_IO_DATA,PER_IO_DATA;
  1. WSABUF结构体:
typedef struct _WSABUF{ULONG len;                            /*the length of buffer*/__field_bcount(len) CHAR FAR *buf;    /*  the pointer to the buffer  */
};
  1. CreateIoCompletionPort函数
HANDLE WINAPI CreateIoCompletionPort(__in      HANDLE  FileHandle,             // 这里当然是连入的这个套接字句柄了__in_opt  HANDLE  ExistingCompletionPort, // 这个就是前面创建的那个完成端口__in      ULONG_PTR CompletionKey,        // 这个参数就是类似于线程参数一样,在// 绑定的时候把自己定义的结构体指针传递// 这样到了Worker线程中,也可以使用这个// 结构体的数据了,相当于参数的传递__in      DWORD NumberOfConcurrentThreads // 这里同样置0
);
  1. 监听窗口状态函数(GetQueuedCompletionStatus)
BOOL WINAPI GetQueuedCompletionStatus(  __in   HANDLE          CompletionPort,    // 建立的完成端口  __out  LPDWORD         lpNumberOfBytes,   //返回的字节数  __out  PULONG_PTR      lpCompletionKey,   // 与完成端口的时候绑定的那个socket对应的自定义结构体PER_HANDLE_DATA__out  LPOVERLAPPED    *lpOverlapped,     // 重叠结构  __in   DWORD           dwMilliseconds     // 等待完成端口的超时时间,一般设置INFINITE  ); 
  1. 投递WSARecv函数
//传递参数,调用就行
int WSARecv(  SOCKET socket,                       // 投递的套接字  LPWSABUF lpBuffers,                 // 接收缓冲区WSABUF结构构成的数组  DWORD dwBufferCount,                // 数组中WSABUF结构的数量,设置为1  LPDWORD lpNumberOfBytesRecvd,       // 返回函数调用所接收到的字节数  LPDWORD lpFlags,                    // 设置为0  LPWSAOVERLAPPED lpOverlapped,       // Socket对应的重叠结构  NULL                                // 设置完成例程模式,这里设置为NULL 
); 
  1. 通知工作线程退出函数
BOOL WINAPI PostQueuedCompletionStatus(  __in      HANDLE CompletionPort,  //当初创建的完成端口__in      DWORD dwNumberOfBytesTransferred, //可做为通知线程退出的一个标示码,其对应于GetQueuedCompletionStatus中的参数lpNumberOfBytes,所以可做文章__in      ULONG_PTR dwCompletionKey,  //PER_HANDLE_DATA结构体__in_opt  LPOVERLAPPED lpOverlapped  
);

大致的步骤如下:
1、创建一个完成端口

HANDLE m_hIOCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0 );  

每错,就是那么简单。
2、根据CPU个数创建工作者线程,把完成端口传进去线程里

// 创建对应数量的工作者线程,一般是CPU核心数量*2SYSTEM_INFO si;GetSystemInfo(&si);int m_nThreads = si.dwNumberOfProcessors * 2;HANDLE* m_phWorkerThreads = new HANDLE[m_nThreads];for (int i = 0; i < m_nThreads; i++) {//将IOCP对象作为线程参数传递m_phWorkerThreads[i] = ::CreateThread(0, 0, (LPTHREAD_START_ROUTINE)_WorkerThread, m_hIOCompletionPort, 0, 0);}

3、创建侦听SOCKET,把SOCKET和完成端口关联起来

CreateIoCompletionPort((HANDLE)m_listensocket, m_hIOCompletionPort, 0, 0);

4、创建PerIOData,向连接进来的SOCKET投递WSARecv操作

WSARecv(PerHandleData->m_clientSock, &PerIoData->m_wsaBuf, 1, &dwRecv, &Flags, &PerIoData->m_Overlapped, NULL);

5、线程里所做的事情:
a、GetQueuedCompletionStatus,在退出的时候就可以使用 PostQueudCompletionStatus使线程退出;

BOOL bReturn = GetQueuedCompletionStatus(CompletionPort, &dwBytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE);

b、取得数据并处理;

完整的代码见链接:
点此下载


http://chatgpt.dhexx.cn/article/BtWj8LyM.shtml

相关文章

Python正则表达式模式

在 Python 程序中&#xff0c;模式字符串使用如下特殊的语法来表示一个正则表达式&#xff1a; 字母和数字表示它们自身&#xff0c;一个正则表达式模式中的字母和数字匹配同样的字符串&#xff1b;当大多数字母和数字前加一个反斜杠时&#xff0c;它们会拥有不同的含义&#…

Python正则表达式实例详解

一、正则表达式语法 正则表达式是用匹配或者描述字符串的工具。 用处&#xff1a; a.判断字符串是否满足某个条件—判断输入的字符串是否是邮箱/手机号码。是否是ip地址 b.提取满足条件的字符串 c.字符串替换 Python中通过re模块中相应的方法来支持正则表达式的匹配、查找和替…

Python正则表达式语法快速入门

文章目录 1 正则符号初阶代码举例1&#xff1a;不同符号的组合代码举例2&#xff1a;符号加&#xff0c;代表连续的一个或多个代码举例3&#xff1a;匹配字符串开始代码举例4&#xff1a;匹配字符串结束代码举例5&#xff1a;匹配单词边界 2 正则符号进阶代码举例1&#xff1a;…

python正则表达式入门

&#x1f64a;今天我们来学习python的正则表达式的部分&#xff0c;先说下为什么要学习这一部分呢&#xff0c;当然是因为正则表达式处理文本类型的数据实在是太方便了。为以后进入nlp领域打打基础&#xff01; 先给大家推荐一个网站: 用于正则表达式验证. 大致就长这个样子。…

python使用正则表达式

一、使用正则表达式步骤 1、寻找规律&#xff1b; 2、使用正则符号表示规律&#xff1b; 3、提取信息&#xff0c;如果每一个字符都能匹配&#xff0c;则匹配成功&#xff1b;一旦有匹配不成功的字符则匹配失败。 二、正则表达式中常见的基本符号 1&#xff0e;点号“.”一…

Python 正则表达式

1.正则表达式的定义: 正则表达式是对字符串进行解析&#xff08;获取一大串字符串信息中&#xff0c;你想要的部分&#xff09;。 正则表达式是一种文本模式&#xff0c;模式描述在搜索文本时要匹配的一个或多个字符串。 正则表达式&#xff0c;又成正规表示式&#xff0c;正规…

python正则表达式详解

正则表达式是一个很强大的字符串处理工具&#xff0c;几乎任何关于字符串的操作都可以使用正则表达式来完成&#xff0c;作为一个爬虫工作者&#xff0c;每天和字符串打交道&#xff0c;正则表达式更是不可或缺的技能&#xff0c;正则表达式的在不同的语言中使用方式可能不一样…

详解Python正则表达式(含丰富案例)

前言&#xff1a;正则表达式在网络爬虫、数据分析中有着广泛使用&#xff0c;掌握正则表达式能够达到事半功倍的效果。本文详细介绍正则表达式中各种规则及其符号含义&#xff0c;并结合Python中的Re库进行演示&#xff0c;由浅入深&#xff0c;即学即练即用&#xff0c;内容丰…

Python超详细的正则表达式

目录 介绍 常用的正则表达式匹配规则表格 1.match() .group() 常用知识点 1.通用匹配 2.贪婪与非贪婪 3.修饰符 4.转义匹配 2.search() 3.findall() 4.sub() 总结 Hello大家好我是&#xff0c;PYmili&#xff01;今天给大家带来正则表达式的使用教程即匹配规则表格…

Python正则表达式(一看就懂)

目录 哈喽O(∩_∩)O&#x1f604; 什么是正则表达式(⊙_⊙) 简单说&#xff0c;正则表达式是… 正则表达式怎么用❓ sreach的用法&#x1f34a; 匹配连续的多个数值&#x1f349; 字符""重复前面一个匹配字符一次或者多次&#x1f349; 字符"*"重复前…

Python正则表达式详解 (超详细,看完必会!)

正则表达式详解 正则表达式 英文名称叫 Regular Expression简称RegEx&#xff0c;是用来匹配字符的一种工具&#xff0c;它常被用在网页爬虫&#xff0c;文稿整理&#xff0c;数据筛选等方面&#xff0c;最常用的就是用在网页爬虫&#xff0c;数据抓取。 一、正则表达式的各种…

Python 正则表达式详解(建议收藏!)

目录 match 匹配字符串 单字符匹配 . 匹配任意一个字符 \d 匹配数字 \D 匹配非数字 \s 匹配特殊字符&#xff0c;如空白&#xff0c;空格&#xff0c;tab等 \S 匹配非空白 \w 匹配单词、字符&#xff0c;如大小写字母&#xff0c;数字&#xff0c;_ 下划线 \W 匹配非…

Android工程中方法数超过65536解决方法(Kotlin)

Android Studio报错&#xff1a; The number of method references in a .dex file cannot exceed 64K. Caused by: com.android.tools.r8.utils.AbortException: Error: Cannot fit requested classes in a single dex file (# methods: 68815 > 65536) 解决方案 build.…

postman/EOLINKER测试报错 RangeError:Port should be 0 and 65536. Received 80892.

postman/EOLINKER测试报错 RangeError&#xff1a;Port should be > 0 and < 65536. Received 80892. RangeError&#xff1a;范围错误 RangeError是当一个只超出有效范围时发生的错误。主要的有几种情况&#xff0c;第一是数组长度为负数&#xff0c;第二是Number对象…

Android Studio Cannot fit requested classes in a single dex file (# methods: 72633 > 65536)解决办法

今天在Android Studio中构建Android工程时&#xff0c;出现了这样报错&#xff1a;“Cannot fit requested classes in a single dex file (# methods: 72633 &#xff1e; 65536)” 在网上找了一圈儿&#xff0c;总结个简单快速的解决方法 问题&#xff1a; Cannot fit req…

计算机端口号65536,65536端口能不能用

楼主asdd3000(asdd) 16位计算机端口65536个,32位计算机端口是65536/2个还是65536*2个?为什么? 问题点数:10、回复次数:20 Top 1 楼EvilOctal(冰血封情) 回复于 2004-04-08 20:41:04 得分 0 这个问题我真的不太清楚. 但是 好象和位数没关系吧? 服务端口是国际组织规定的,不是…

为什么89C51单片机里面有TH0=(65536-50000)/256;TL0=(65536-50000)%256;

由于89C51的晶振频率为12MHZ&#xff0c;机器周期是1us,则每1us产生一次计数&#xff0c;例如需要进行50ms的计时&#xff0c;对机器周期进行50000计数就可以得到&#xff0c;T0可以对机器周期进行65536次计数&#xff0c;为了不让T0溢出&#xff0c;我们需要对定时器/计数器赋…

NXP MCUXPresso - cc1plus.exe: out of memory allocating 65536 bytes

文章目录 NXP MCUXPresso - cc1plus.exe: out of memory allocating 65536 bytes概述实验结论补充END NXP MCUXPresso - cc1plus.exe: out of memory allocating 65536 bytes 概述 在尝试迁移 openpnp - Smoothieware project 从gcc命令行 MRI调试方式 到NXP MCUXpresso工程…

65536 65535 65534

65536 65535 65534 通常在导入模型的时候会遇到65534的限制&#xff0c;比如Unity里的“Meshes may not have more than 65534 vertices or triangles at the moment”提示。开起来像是2的16次方&#xff0c;但为什么不是65536&#xff1f;65536和65535会有什么问题&#xff1…

Eclipse中65536的解决办法

最近在做项目的时候&#xff0c;发现引入的第三方工程太多&#xff0c;无法打包成app文件了。如下 遇到这种问题怎么办呢&#xff1f;&#xff1f;&#xff1f;我们先看问题描述&#xff1a;方法数不在0-65536内&#xff0c;dalvik。。错误&#xff0c;不懂&#xff0c;还是先百…