Windows IOCP

article/2025/11/8 7:24:16

Windows IOCP

IOCP全称I/O Completion Port,中文译为I/O完成端口。IOCP是一个异步I/O的Windows API,它可以高效地将I/O事件通知给应用程序,类似于Linux中的Epoll。

简介


 IOCP模型属于一种通讯模型,适用于Windows平台下高负载服务器的一个技术。在处理大量用户并发请求时,如果采用一个用户一个线程的方式那将造成CPU在这成千上万的线程间进行切换,后果是不可想象的。而IOCP完成端口模型则完全不会如此处理,它的理论是并行的线程数量必须有一个上限-也就是说同时发出500个客户请求,不应该允许出现500个可运行的线程。目前来说,IOCP完成端口是Windows下性能最好的I/O模型,同时它也是最复杂的内核对象。它避免了大量用户并发时原有模型采用的方式,极大的提高了程序的并行处理能力。 

原理图


这里写图片描述


从图中可以看到,一共包括三部分:完成端口(存放重叠的I/O请求),客户端请求的处理,等待者线程队列(一定数量的工作者线程,一般采用CPU*2个)。

  完成端口中所谓的[端口]并不是我们在TCP/IP中所提到的端口,可以说是完全没有关系。它其实就是一个通知队列,由操作系统把已经完成的重叠I/O请求的通知放入其中。当某项I/O操作一旦完成,某个可以对该操作结果进行处理的工作者线程就会收到一则通知。

  通常情况下,我们会在创建一定数量的工作者线程来处理这些通知,也就是线程池的方法。线程数量取决于应用程序的特定需要。理想的情况是,线程数量等于处理器的数量,不过这也要求任何线程都不应该执行诸如同步读写、等待事件通知等阻塞型的操作,以免线程阻塞。每个线程都将分到一定的CPU时间,在此期间该线程可以运行,然后另一个线程将分到一个时间片并开始执行。如果某个线程执行了阻塞型的操作,操作系统将剥夺其未使用的剩余时间片并让其它线程开始执行。也就是说,前一个线程没有充分使用其时间片,当发生这样的情况时,应用程序应该准备其它线程来充分利用这些时间片。

IOCP的优点


基于IOCP的开发是异步IO的,决定了IOCP所实现的服务器的高吞吐量。

  完成端口的线程并发量可以在创建该完成端口时指定,从而限制了与该完成端口相关联的可运行线程的数目。

 通过引入IOCP,会大大减少Thread切换带来的额外开销,最小化的线程上下文切换,减少线程切换带来的巨大开销,让CPU把大量的事件用于线程的运行。当与该完成端口相关联的可运行线程的总数目达到了该并发量,系统就会阻塞任何与该完成端口相关联的后续线程的执行,直到与该完成端口相关联的可运行线程数目下降到小于该并发量为止。

  Select是先查询再发起IO请求,IOCP是先发起IO请求再接收通知。但是Select方式在处理大量非活动连接时是比较低效的,因为每次Select需要对所有的Socket状态进行查询,而对非活动的Socket查询是没有意义的浪费,另外由于Socket句柄不能设置用户私有数据,当查询返回Socket句柄时还需要一个额外的查询来找到关联的用户对象,这两点是Select低效的关键。

IOCP的具体实现步骤


TCP IOCP实现具体步骤:

  1. 创建好 IOCP
  2. 创建 Socket ( socket 可以是由 Accept 得到)
  3. 将 Socket 关联到 IOCP
  4. socket 向 IOCP 提交各种所需请求
  5. IOCP 操作完成之后将结果返回给 socket
  6. 重复步骤 3 和 4 ,直到 socket 关闭

IOCP中用到单个函数,分为用于创建关联完成端口、获取完成状态和投递完成状态,函数原型:

//功能:创建完成端口和关联完成端口HANDLE WINAPI CreateIoCompletionPort(*    __in   HANDLE FileHandle,              // 已经打开的文件句柄或者空句柄,一般是客户端的句柄*    __in   HANDLE ExistingCompletionPort,  // 已经存在的IOCP句柄*    __in   ULONG_PTR CompletionKey,        // 完成键,包含了指定I/O完成包的指定文件*    __in   DWORD NumberOfConcurrentThreads // 真正并发同时执行最大线程数,一般推介是CPU核心数*2* );//例子
//创建完成端口句柄
HANDLE completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);typedef struct{SOCKET socket;//客户端socketSOCKADDR_STORAGE ClientAddr;//客户端地址
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA;//与socket进行关联
CreateIoCompletionPort((HANDLE)(PerHandleData -> socket), completionPort, (DWORD)PerHandleData, 0);
//功能:获取队列完成状态
BOOL   GetQueuedCompletionStatus(HANDLE   CompletionPort,          //完成端口句柄LPDWORD   lpNumberOfBytes,    //一次I/O操作所传送的字节数PULONG_PTR   lpCompletionKey, //当文件I/O操作完成后,用于存放与之关联的CKLPOVERLAPPED   *lpOverlapped, //IOCP特定的结构体DWORD   dwMilliseconds);           //调用者的等待时间
/*
返回值:
调用成功,则返回非零数值,相关数据存于lpNumberOfBytes、lpCompletionKey、lpoverlapped变量中。失败则返回零值。
*///用于IOCP的特定函数
typedef struct _OVERLAPPEDPLUS{OVERLAPPED ol;      //一个固定的用于处理网络消息事件返回值的结构体变量SOCKET s, sclient;  int OpCode;      //用来区分本次消息的操作类型(在完成端口的操作里面,是以消息通知系统,读数据/写数据,都是要发这样的消息结构体过去的)WSABUF wbuf;     //读写缓冲区结构体变量 DWORD dwBytes, dwFlags; //一些在读写时用到的标志性变量 
}OVERLAPPEDPLUS;
//功能:投递一个队列完成状态
BOOL PostQueuedCompletionStatus( HANDLE CompletlonPort, //指定想向其发送一个完成数据包的完成端口对象DW0RD dwNumberOfBytesTrlansferred, //指定—个值,直接传递给GetQueuedCompletionStatus函数中对应的参数 DWORD dwCompletlonKey, //指定—个值,直接传递给GetQueuedCompletionStatus函数中对应的参数LPOVERLAPPED lpoverlapped, ); //指定—个值,直接传递给GetQueuedCompletionStatus函数中对应的参数
#include <winsock2.h>
#include <windows.h>
#include <string>
#include <iostream>
using namespace std;#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"kernel32.lib")HANDLE g_hIOCP;enum IO_OPERATION{IO_READ,IO_WRITE};struct IO_DATA{OVERLAPPED                  Overlapped;WSABUF                      wsabuf;int                         nBytes;IO_OPERATION                opCode;SOCKET                      client;
};char buffer[1024];DWORD WINAPI WorkerThread (LPVOID WorkThreadContext) {IO_DATA *lpIOContext = NULL; DWORD nBytes = 0;DWORD dwFlags = 0; int nRet = 0;DWORD dwIoSize = 0; void * lpCompletionKey = NULL;LPOVERLAPPED lpOverlapped = NULL;while(1){GetQueuedCompletionStatus(g_hIOCP, &dwIoSize,(LPDWORD)&lpCompletionKey,(LPOVERLAPPED *)&lpOverlapped, INFINITE);lpIOContext = (IO_DATA *)lpOverlapped;if(dwIoSize == 0){cout << "Client disconnect" << endl;closesocket(lpIOContext->client);delete lpIOContext;continue;}if(lpIOContext->opCode == IO_READ) // a read operation complete{ZeroMemory(&lpIOContext->Overlapped, sizeof(lpIOContext->Overlapped));lpIOContext->wsabuf.buf = buffer;lpIOContext->wsabuf.len = strlen(buffer)+1;lpIOContext->opCode = IO_WRITE;lpIOContext->nBytes = strlen(buffer)+1;dwFlags = 0;nBytes = strlen(buffer)+1;nRet = WSASend(lpIOContext->client,&lpIOContext->wsabuf, 1, &nBytes,dwFlags,&(lpIOContext->Overlapped), NULL);if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()) ) {cout << "WASSend Failed::Reason Code::"<< WSAGetLastError() << endl;closesocket(lpIOContext->client);delete lpIOContext;continue;}memset(buffer, NULL, sizeof(buffer));}else if(lpIOContext->opCode == IO_WRITE) //a write operation complete{// Write operation completed, so post Read operation.lpIOContext->opCode = IO_READ; nBytes = 1024;dwFlags = 0;lpIOContext->wsabuf.buf = buffer;lpIOContext->wsabuf.len = nBytes;lpIOContext->nBytes = nBytes;ZeroMemory(&lpIOContext->Overlapped, sizeof(lpIOContext->Overlapped));nRet = WSARecv(lpIOContext->client,&lpIOContext->wsabuf, 1, &nBytes,&dwFlags,&lpIOContext->Overlapped, NULL);if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()) ) {cout << "WASRecv Failed::Reason Code1::"<< WSAGetLastError() << endl;closesocket(lpIOContext->client);delete lpIOContext;continue;} cout<<lpIOContext->wsabuf.buf<<endl;}}return 0;
}
void main ()
{WSADATA wsaData;WSAStartup(MAKEWORD(2,2), &wsaData);SOCKET    m_socket = WSASocket(AF_INET,SOCK_STREAM, IPPROTO_TCP, NULL,0,WSA_FLAG_OVERLAPPED);sockaddr_in server;server.sin_family = AF_INET;server.sin_port = htons(6000);server.sin_addr.S_un.S_addr = htonl(INADDR_ANY);bind(m_socket ,(sockaddr*)&server,sizeof(server));listen(m_socket, 8);SYSTEM_INFO sysInfo;GetSystemInfo(&sysInfo);int g_ThreadCount = sysInfo.dwNumberOfProcessors * 2;g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,g_ThreadCount);//CreateIoCompletionPort((HANDLE)m_socket,g_hIOCP,0,0);for( int i=0;i < g_ThreadCount; ++i){HANDLE  hThread;DWORD   dwThreadId;hThread = CreateThread(NULL, 0, WorkerThread, 0, 0, &dwThreadId);CloseHandle(hThread);}while(1){SOCKET client = accept( m_socket, NULL, NULL );cout << "Client connected." << endl;if (CreateIoCompletionPort((HANDLE)client, g_hIOCP, 0, 0) == NULL){cout << "Binding Client Socket to IO Completion Port Failed::Reason Code::"<< GetLastError() << endl;closesocket(client);}else { //post a recv requestIO_DATA * data = new IO_DATA;memset(buffer, NULL ,1024);memset(&data->Overlapped, 0 , sizeof(data->Overlapped));data->opCode = IO_READ;data->nBytes = 0;data->wsabuf.buf  = buffer;data->wsabuf.len  = sizeof(buffer);data->client = client;DWORD nBytes= 1024 ,dwFlags=0;int nRet = WSARecv(client,&data->wsabuf, 1, &nBytes,&dwFlags,&data->Overlapped, NULL);if(nRet == SOCKET_ERROR  && (ERROR_IO_PENDING != WSAGetLastError())){cout << "WASRecv Failed::Reason Code::"<< WSAGetLastError() << endl;closesocket(client);delete data;}cout<<data->wsabuf.buf<<endl;}}closesocket(m_socket);WSACleanup();
}
#include <iostream>
#include <WinSock2.h>
using namespace std;#pragma comment(lib,"ws2_32.lib")void main()
{WSADATA wsaData;  WSAStartup(MAKEWORD(2,2), &wsaData);sockaddr_in server;server.sin_family = AF_INET;server.sin_port   = htons(6000);server.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");SOCKET client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);int flag;flag = connect(client, (sockaddr*)&server, sizeof(server));if(flag < 0){cout<<"error!"<<endl;return;}while(1){cout<<"sent hello!!!!"<<endl;char buffer[1024];strcpy(buffer,"hello");send(client, buffer, 1024, 0);memset(buffer, NULL, sizeof(buffer));cout<<"recv: "<<endl;int rev = recv(client, buffer, 1024, 0);if(rev == 0)cout<<"recv nothing!"<<endl;cout<<buffer<<endl;Sleep(10000);}closesocket(client);WSACleanup();
}

参考


http://www.cnblogs.com/lidabo/archive/2012/12/10/2812230.html

http://www.codeproject.com/KB/IP/iocp-multicast-udp.aspx

http://blog.csdn.net/zhongguoren666/article/details/7386592

http://www.baike.com/wiki/%E5%AE%8C%E6%88%90%E7%AB%AF%E5%8F%A3%E6%A8%A1%E5%9E%8B

http://blog.csdn.net/neicole/article/details/7549497

http://ycool.com/post/zgu6hbp

转载

http://www.cnblogs.com/coder2012


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

相关文章

IOCP小结

文章目录 一 什么是完成端口&#xff08;completion port&#xff09;对象二 使用IOCP的方法创建完成端口对象I/O服务线程和完成端口完成端口和重叠I/O 三 恰当地关闭IOCP四 IOCP大概的处理流程五 一个简单示例具体编程流程 当应用程序必须一次管理多个套接字时&#xff0c;完成…

IOCP高性能服务器的实现

应用场景说明&#xff1a;完成端口在面向现实应用的许多网络通信中应用很广泛&#xff0c;例如大型多人在线游戏&#xff0c;大型即时通信系统&#xff0c;网吧管理系统以及企业管理系统等具有大量并发用户请求的场合。 实现目标说明&#xff1a;通过完成端口模型构建一款服务…

IOCP 详解

IOCP 详解 网络上关于epoll的介绍资料多如牛毛&#xff0c;大多数已经讲解的非常细致。相比而言epoll只有三个接口调用&#xff0c;IOCP却有一堆的接口调用&#xff0c;更关键的是Windows的闭源性质&#xff0c;我们不知道调用之后Windows究竟做了哪些操作。众所周知IOCP是基于…

IOCP简介

1.1 环境要求本文读者需要熟悉C、TCP/IP、Socket编程、MFC&#xff0c;和多线程。源码使用Winsock 2.0和IOCP技术&#xff0c;要求&#xff1a;Windows NT/2000或以上&#xff1a;要求Windows NT3.5或以后版本Windows 95/98/ME&#xff1a;不支持Visual C.NET&#xff0c;或完整…

什么是IOCP

百度词条 输入输出完成端口&#xff08;Input/Output Completion Port&#xff0c;IOCP&#xff09;, 是支持多个同时发生的异步I/O操作的应用程序编程接口 一个IOCP对象&#xff0c;在操作系统中可关联着多个Socket和&#xff08;或&#xff09;文件控制端。 IOCP对象内部有一…

IOCP

载自&#xff1a;http://blog.csdn.net/markman101/article/details/6235516 本文主要探讨一下windows平台上的完成端口开发及其与之相关的几个重要的技术概念&#xff0c;这些概念都是与基于IOCP的开发密切相关的&#xff0c;对开发人员来讲&#xff0c;又不得不给予足够重视的…

IOCP模型与网络编程

IOCP模型与网络编程 一。前言&#xff1a; 在老师分配任务&#xff08;“尝试利用IOCP模型写出服务端和客户端的代码”&#xff09;给我时&#xff0c;脑子一片空白&#xff0c;并不知道什么是IOCP模型&#xff0c;会不会是像软件设计模式里面的工厂模式&#xff0c;装…

Windows下的IOCP模型(一):介绍与简单使用

一、IOCP简介 IOCP&#xff08;I/O Completion Port&#xff0c;I/O完成端口&#xff09;是Windows操作系统中伸缩性最好的一种I/O模型。I/O 完成端口是应用程序使用线程池处理异步 I/O 请求的一种机制。处理多个并发异步I/O请求时&#xff0c;使用 I/O 完成端口比在 I/O 请求时…

完成端口(IOCP)编程探讨

FW: http://www.zhuaxia.com/item/473350387 完成端口(IOCP)编程探讨 2007-08-26 16:06:00 来自&#xff1a;C博客-首页原创精华区 新建目录...根目录新手试用频道 本文主要探讨一下windows平台上的完成端口开发及其与之相关的几个重要的技术概念&#xff0c;这些概念都是与…

异步通信之IOCP详解

一、 概述 学习完网络基础&#xff0c;在写C/S应用程序时&#xff0c;大多童靴写服务器基本都没有用到io模型&#xff0c;基本都是采用“accept同步拥塞通讯和多线程方式”与客户端通讯。但当有成千上万客户端请求连接并与服务器通讯时&#xff0c;多线程的创建与CPU上下文的切…

IOCP详解

IOCP详解 IOCP&#xff08;I/O Completion Port&#xff0c;I/O完成端口&#xff09;是性能最好的一种I/O模型。它是应用程序使用线程池处理异步I/O请求的一种机制。在处理多个并发的异步I/O请求时&#xff0c;以往的模型都是在接收请求是创建一个线程来应答请求。这样就有很多…

IOCP技术详解

这几周我接触了Windows网络通讯中的IOCP模型,自己在网上找了相关的知识进行学习&#xff0c;自己又下了好多服务器端的代码&#xff0c;但都运行不了&#xff0c;也是自己菜&#xff0c;能力还需加强。幸好我师父资助了我一个能运行的服务端IOCP代码&#xff0c;自己参照网上的…

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;内容丰…