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的具体实现步骤

  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, , ); typedef struct{
SOCKET socket;//客户端socket
SOCKADDR_STORAGE ClientAddr;//客户端地址
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA; //与socket进行关联
CreateIoCompletionPort((HANDLE)(PerHandleData -> socket), completionPort, (DWORD)PerHandleData, );
//功能:获取队列完成状态
BOOL GetQueuedCompletionStatus(
HANDLE CompletionPort, //完成端口句柄
LPDWORD lpNumberOfBytes, //一次I/O操作所传送的字节数
PULONG_PTR lpCompletionKey, //当文件I/O操作完成后,用于存放与之关联的CK
LPOVERLAPPED *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函数中对应的参数

  TCP IOCP实现具体步骤:

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

   例子:

 #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[]; DWORD WINAPI WorkerThread (LPVOID WorkThreadContext) {
IO_DATA *lpIOContext = NULL;
DWORD nBytes = ;
DWORD dwFlags = ;
int nRet = ; DWORD dwIoSize = ;
void * lpCompletionKey = NULL;
LPOVERLAPPED lpOverlapped = NULL; while(){
GetQueuedCompletionStatus(g_hIOCP, &dwIoSize,(LPDWORD)&lpCompletionKey,(LPOVERLAPPED *)&lpOverlapped, INFINITE); lpIOContext = (IO_DATA *)lpOverlapped;
if(dwIoSize == )
{
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)+;
lpIOContext->opCode = IO_WRITE;
lpIOContext->nBytes = strlen(buffer)+;
dwFlags = ;
nBytes = strlen(buffer)+;
nRet = WSASend(
lpIOContext->client,
&lpIOContext->wsabuf, , &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 = ;
dwFlags = ;
lpIOContext->wsabuf.buf = buffer;
lpIOContext->wsabuf.len = nBytes;
lpIOContext->nBytes = nBytes;
ZeroMemory(&lpIOContext->Overlapped, sizeof(lpIOContext->Overlapped)); nRet = WSARecv(
lpIOContext->client,
&lpIOContext->wsabuf, , &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 ;
}
void main ()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(,), &wsaData); SOCKET m_socket = WSASocket(AF_INET,SOCK_STREAM, IPPROTO_TCP, NULL,,WSA_FLAG_OVERLAPPED); sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons();
server.sin_addr.S_un.S_addr = htonl(INADDR_ANY); bind(m_socket ,(sockaddr*)&server,sizeof(server)); listen(m_socket, ); SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
int g_ThreadCount = sysInfo.dwNumberOfProcessors * ; g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,,g_ThreadCount); //CreateIoCompletionPort((HANDLE)m_socket,g_hIOCP,0,0); for( int i=;i < g_ThreadCount; ++i){
HANDLE hThread;
DWORD dwThreadId;
hThread = CreateThread(NULL, , WorkerThread, , , &dwThreadId);
CloseHandle(hThread);
} while()
{
SOCKET client = accept( m_socket, NULL, NULL );
cout << "Client connected." << endl; if (CreateIoCompletionPort((HANDLE)client, g_hIOCP, , ) == NULL){
cout << "Binding Client Socket to IO Completion Port Failed::Reason Code::"<< GetLastError() << endl;
closesocket(client);
}
else { //post a recv request
IO_DATA * data = new IO_DATA;
memset(buffer, NULL ,);
memset(&data->Overlapped, , sizeof(data->Overlapped));
data->opCode = IO_READ;
data->nBytes = ;
data->wsabuf.buf = buffer;
data->wsabuf.len = sizeof(buffer);
data->client = client;
DWORD nBytes= ,dwFlags=;
int nRet = WSARecv(client,&data->wsabuf, , &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(,), &wsaData); sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons();
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 < ){
cout<<"error!"<<endl;
return;
}
while(){
cout<<"sent hello!!!!"<<endl;
char buffer[];
strcpy(buffer,"hello");
send(client, buffer, , ); memset(buffer, NULL, sizeof(buffer)); cout<<"recv: "<<endl;
int rev = recv(client, buffer, , );
if(rev == )
cout<<"recv nothing!"<<endl;
cout<<buffer<<endl;
Sleep();
} 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


IOCP模型 由 cococo点点 创作,采用 知识共享 署名-相同方式共享 3.0 未本地化版本 许可协议进行许可。欢迎转载,请注明出处:
转载自:cococo点点 http://www.cnblogs.com/coder2012

Server Develop (八) IOCP模型的更多相关文章

  1. [转]一个基于完成端口的TCP Server Framework,浅析IOCP

    [转]一个基于完成端口的TCP Server Framework,浅析IOCP http://www.cppblog.com/adapterofcoms/archive/2010/06/26/1187 ...

  2. IOCP模型与网络编程

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

  3. winsock编程IOCP模型实现代码

    winsock编程IOCP模型实现代码 话不多说,上代码.借鉴<windows核心编程>部分源码和CSDN小猪部分代码. stdafx.h依赖头文件: #include <iostr ...

  4. IOCP模型与网络编

    一.前言:        在老师分配任务(“尝试利用IOCP模型写出服务端和客户端的代码”)给我时,脑子一片空白,并不知道什么是IOCP模型,会不会是像软件设计模式里面的工厂模式,装饰模式之类的那些呢 ...

  5. HttpServer:一款Windows平台下基于IOCP模型的高并发轻量级web服务器

    HttpServer的特点1.完全采用IOCP模型,实现真正的异步IO,高并发.高可靠: 2.支持4G以上文件下载: 3.支持断点续传: 4.轻量级,体积小,服务器文件仅200多K,无任何依赖库: 5 ...

  6. Server Develop (七) Linux 守护进程

    守护进程 守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程.它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.守护进程常常在系统引导装 ...

  7. IOCP模型

    IOCP http://blog.csdn.net/zhongguoren666/article/details/7386592 Winsock IO模型之IOCP模型 http://blog.csd ...

  8. IOCP模型总结(转)

    IOCP模型总结(转) IOCP(I/O Completion Port,I/O完成端口)是性能最好的一种I/O模型.它是应用程序使用线程池处理异步I/O请求的一种机制.在处理多个并发的异步I/O请求 ...

  9. 【IOCP】 IOCP模型属于一种通讯模型- 较难

    http://baike.baidu.com/link?url=e9vXkKd2aHp8VDr1XTURdwQB4K85r28IYjeMwRIyuaXtsrCsXHY1eohiFgsDXRYRlj6x ...

随机推荐

  1. 百思不得骑姐的问题——难道是控件的bug?

    直接进入主题,困惑了一下午了. 要实现的功能: winform的checkedlistbox控件 点击  “全部”  就都选上,可是如果点击过快就会出现如上现象,下面选项未显示选中. 代码如下: pr ...

  2. JS打印页面

         打印 整个html页面(PS:样式要写在页面里面才能打印(就是用内部样式))             <a id="dayi" runat="server ...

  3. Oracle 10g -- 修改DB的编码

    修改DB的原因是:因为我的DB不支持中文,所以每当我向数据库表中插入一条数据的时候,中文就都变了类似于“?(是反问号)”的乱码,为了能顺利插入成功,故做了此次修改; 系统:windows XP 英文版 ...

  4. 解决org.openqa.selenium.WebDriverException: Unable to connect to host 127.0.0.1 on port 7055 after 45000 ms org.springframework.beans.BeanInstantiation

    解决方法为将selenium-server-standalone-2.37.0.jar升级至selenium-server-standalone-2.41.0.jar即可. 下载地址:http://s ...

  5. 转 SVN 在vs中的使用

    给大家介绍一些SVN的入门知识!希望对大家的学习起到作用!      关于SVN与CVS的相关知识,大家可以自己去google一下.      一.准备         SVN是一个开源的版本控制系统 ...

  6. js关闭浏览器的tab页(兼容)

    由于在脚本中使用了 window.close(), 当前非弹出窗口在最新版本的chrome和firefox里总是不能关闭,而在 IE中是可以关闭的 . 在console中弹出提示"Scrip ...

  7. 【实(dou)力(bi)首(mai)发(meng)】第四次CCF软件能力认证题解

    这次的题总体上相对前三次偏简单.由于实力有限,就分析前四题.     试题编号:    201503-1 试题名称:    图像旋转 时间限制:    5.0s 内存限制:    256.0MB 问题 ...

  8. PostScript的简单例子-用粗线画一个圆

    一 近期需要用到PostScript,查询资料学习PS的语法 简单的画一个圆的例子 %!PS-Adobe-3.0 /inch{72 mul} def 4.25 inch 5.5 inch 1.5 in ...

  9. toString的理解

    Super的作用: 1. super可以直接调用父类的属性和方法. 2. super可以在子类的构造器中调用父类的构造器. 我们知道:实例化一个对象时,会调用构造器. 我们发现,仅仅实例化的是Stud ...

  10. iOS删除本地文件

    以前在博客里记录的东西都是截屏,没有插入代码,今天进去一看,图片都不显示了,只好重新插入代码,发现以前写的文件操作这块,没有写本地文件删除这个功能,重新再记录一下 //需要删除文件的物理地址 NSSt ...