Server Develop (八) IOCP模型

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, 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函数中对应的参数

  TCP IOCP实现具体步骤:

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

    例子:

  1 #include <winsock2.h>
  2 #include <windows.h>
  3 #include <string>
  4 #include <iostream>
  5 using namespace std;
  6 
  7 #pragma comment(lib,"ws2_32.lib")
  8 #pragma comment(lib,"kernel32.lib")
  9 
 10 HANDLE g_hIOCP;
 11 
 12 enum IO_OPERATION{IO_READ,IO_WRITE};
 13 
 14 struct IO_DATA{
 15     OVERLAPPED                  Overlapped;
 16     WSABUF                      wsabuf;
 17     int                         nBytes;
 18     IO_OPERATION                opCode;
 19     SOCKET                      client;
 20 };
 21 
 22 char buffer[1024];
 23 
 24 DWORD WINAPI WorkerThread (LPVOID WorkThreadContext) {
 25     IO_DATA *lpIOContext = NULL; 
 26     DWORD nBytes = 0;
 27     DWORD dwFlags = 0; 
 28     int nRet = 0;
 29 
 30     DWORD dwIoSize = 0; 
 31     void * lpCompletionKey = NULL;
 32     LPOVERLAPPED lpOverlapped = NULL;
 33 
 34     while(1){
 35         GetQueuedCompletionStatus(g_hIOCP, &dwIoSize,(LPDWORD)&lpCompletionKey,(LPOVERLAPPED *)&lpOverlapped, INFINITE);
 36         
 37         lpIOContext = (IO_DATA *)lpOverlapped;
 38         if(dwIoSize == 0)
 39         {
 40             cout << "Client disconnect" << endl;
 41             closesocket(lpIOContext->client);
 42             delete lpIOContext;
 43             continue;
 44         }
 45 
 46         if(lpIOContext->opCode == IO_READ) // a read operation complete
 47         {
 48             ZeroMemory(&lpIOContext->Overlapped, sizeof(lpIOContext->Overlapped));
 49             lpIOContext->wsabuf.buf = buffer;
 50             lpIOContext->wsabuf.len = strlen(buffer)+1;
 51             lpIOContext->opCode = IO_WRITE;
 52             lpIOContext->nBytes = strlen(buffer)+1;
 53             dwFlags = 0;
 54             nBytes = strlen(buffer)+1;
 55             nRet = WSASend(
 56                 lpIOContext->client,
 57                 &lpIOContext->wsabuf, 1, &nBytes,
 58                 dwFlags,
 59                 &(lpIOContext->Overlapped), NULL);
 60             if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()) ) {
 61                 cout << "WASSend Failed::Reason Code::"<< WSAGetLastError() << endl;
 62                 closesocket(lpIOContext->client);
 63                 delete lpIOContext;
 64                 continue;
 65             }
 66             memset(buffer, NULL, sizeof(buffer));
 67         }
 68         else if(lpIOContext->opCode == IO_WRITE) //a write operation complete
 69         {
 70             // Write operation completed, so post Read operation.
 71             lpIOContext->opCode = IO_READ; 
 72             nBytes = 1024;
 73             dwFlags = 0;
 74             lpIOContext->wsabuf.buf = buffer;
 75             lpIOContext->wsabuf.len = nBytes;
 76             lpIOContext->nBytes = nBytes;
 77             ZeroMemory(&lpIOContext->Overlapped, sizeof(lpIOContext->Overlapped));
 78 
 79             nRet = WSARecv(
 80                 lpIOContext->client,
 81                 &lpIOContext->wsabuf, 1, &nBytes,
 82                 &dwFlags,
 83                 &lpIOContext->Overlapped, NULL);
 84             if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()) ) {
 85                 cout << "WASRecv Failed::Reason Code1::"<< WSAGetLastError() << endl;
 86                 closesocket(lpIOContext->client);
 87                 delete lpIOContext;
 88                 continue;
 89             } 
 90             cout<<lpIOContext->wsabuf.buf<<endl;
 91         }
 92     }
 93     return 0;
 94 }
 95 void main ()
 96 {
 97     WSADATA wsaData;
 98     WSAStartup(MAKEWORD(2,2), &wsaData);
 99 
100     SOCKET    m_socket = WSASocket(AF_INET,SOCK_STREAM, IPPROTO_TCP, NULL,0,WSA_FLAG_OVERLAPPED);
101     
102     sockaddr_in server;
103     server.sin_family = AF_INET;
104     server.sin_port = htons(6000);
105     server.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
106     
107     bind(m_socket ,(sockaddr*)&server,sizeof(server));
108 
109     listen(m_socket, 8);
110 
111     SYSTEM_INFO sysInfo;
112     GetSystemInfo(&sysInfo);
113     int g_ThreadCount = sysInfo.dwNumberOfProcessors * 2;
114 
115     g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,g_ThreadCount);
116     
117     //CreateIoCompletionPort((HANDLE)m_socket,g_hIOCP,0,0);
118 
119     for( int i=0;i < g_ThreadCount; ++i){
120         HANDLE  hThread;
121         DWORD   dwThreadId;
122         hThread = CreateThread(NULL, 0, WorkerThread, 0, 0, &dwThreadId);
123         CloseHandle(hThread);
124     }
125 
126     while(1)
127     {
128         SOCKET client = accept( m_socket, NULL, NULL );
129         cout << "Client connected." << endl;
130         
131 
132         if (CreateIoCompletionPort((HANDLE)client, g_hIOCP, 0, 0) == NULL){
133             cout << "Binding Client Socket to IO Completion Port Failed::Reason Code::"<< GetLastError() << endl;
134             closesocket(client);
135         }
136         else { //post a recv request
137             IO_DATA * data = new IO_DATA;
138             memset(buffer, NULL ,1024);
139             memset(&data->Overlapped, 0 , sizeof(data->Overlapped));
140             data->opCode = IO_READ;
141             data->nBytes = 0;
142             data->wsabuf.buf  = buffer;
143             data->wsabuf.len  = sizeof(buffer);
144             data->client = client;
145             DWORD nBytes= 1024 ,dwFlags=0;
146             int nRet = WSARecv(client,&data->wsabuf, 1, &nBytes,
147                 &dwFlags,
148                 &data->Overlapped, NULL);
149             if(nRet == SOCKET_ERROR  && (ERROR_IO_PENDING != WSAGetLastError())){
150                 cout << "WASRecv Failed::Reason Code::"<< WSAGetLastError() << endl;
151                 closesocket(client);
152                 delete data;
153             }
154             cout<<data->wsabuf.buf<<endl;
155         }
156     }
157     closesocket(m_socket);
158     WSACleanup();
159 }
View Code
 1 #include <iostream>
 2 #include <WinSock2.h>
 3 using namespace std;
 4 
 5 #pragma comment(lib,"ws2_32.lib")
 6 
 7 void main()
 8 {
 9     WSADATA wsaData;  
10     WSAStartup(MAKEWORD(2,2), &wsaData);
11 
12     sockaddr_in server;
13     server.sin_family = AF_INET;
14     server.sin_port   = htons(6000);
15     server.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
16 
17     SOCKET client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
18 
19     int flag;
20     flag = connect(client, (sockaddr*)&server, sizeof(server));
21     if(flag < 0){
22         cout<<"error!"<<endl;
23         return;
24     }
25     while(1){
26         cout<<"sent hello!!!!"<<endl;
27         char buffer[1024];
28         strcpy(buffer,"hello");
29         send(client, buffer, 1024, 0);
30 
31         memset(buffer, NULL, sizeof(buffer));
32         
33         cout<<"recv: "<<endl;
34         int rev = recv(client, buffer, 1024, 0);
35         if(rev == 0)
36             cout<<"recv nothing!"<<endl;
37         cout<<buffer<<endl;
38         Sleep(10000);
39     }
40 
41     closesocket(client);
42     WSACleanup();
43 }
View Code

参考

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

转载于:https://www.cnblogs.com/coder2012/p/3185715.html

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

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

相关文章

理解RESTful架构

2019独角兽企业重金招聘Python工程师标准>>> 原文&#xff1a;http://www.ruanyifeng.com/blog/2011/09/restful.html?20160826000527 越来越多的人开始意识到&#xff0c;网站即软件&#xff0c;而且是一种新型的软件。 这种"互联网软件"采用客户端/服务…

凡是过往,皆为序章。

凡是过往&#xff0c;皆为序章今天是元旦假期的最后一天&#xff0c;这三天我一直在想&#xff0c;21年的年终总结应该如何下笔。其实心里面想表达和记录的事情很多&#xff0c;可当真正坐在电脑前时&#xff0c;却发现没有头绪。贵有恒&#xff0c;何必三更起五更眠。最无益&a…

查看操作系统版本linux_LINUX操作系统常用操作收录(二):查看文件内容命令小结...

先整体了解一下如何查看文件内容的命令vi 兼有修改功能的查看方式&#xff0c;会单独打开整个文件vim 可修改文件内容并且显示当前查看位置在文件中的%多少cat 由第一行开始显示内容&#xff0c;并将所有内容输出tac 从最后一行倒序显示内容&#xff0c;并将所有内容输出more…

crond定时任务详细分析

一、定时任务crond的介绍 crond是linux系统中用来定期执行命令或指定程序任务的一种服务或软件。一般情况下&#xff0c;我们安装文成系统之后&#xff0c;默认变回启动crond任务调度服务&#xff0c;crond服务会定期(默认每分钟检查一次)检查系统中是否有要执行的任务工作。…

OCS2007R2升级LyncSrv2013 PART4:关联边缘

完成拓扑的合并后&#xff0c;由于边缘服务器尚未做迁移和升级&#xff0c;所以此时我们的Lync Server没有边缘服务、联盟路由等。我们就可以把OCS的边缘服务器与Lync Server前端进行关联&#xff0c;暂时使用OCS的联盟路由和边缘服务。要关联OCS边缘服务器&#xff0c;需要打开…

2021技术文大盘点 | 打包过去,​面向未来

先用四句诗词快速描述 一下我的写作心得1. 只在此山中&#xff0c;云深不知处作为开发人员&#xff0c;常执着于机器0,1代码&#xff0c;非假既真&#xff1b;真实世界是很主观的&#xff0c;需要精致细节&#xff0c;更多时候需要全局把控。带着问题写作&#xff0c;对事物理解…

Ansible 一步一步从入门到精通(一)

一&#xff1a;安装ansiblemac&#xff1a;1. 安装 Homebrew (get the installation command from the Homebrew website).2. 安装Python 2.7.x ( brew install python ).3. 安装 Ansible ( sudo pip install ansible ).linux&#xff1a;如果系统中安装了python-pip和python-d…

客户细分总结

随着营销方式的多变、客户需求各异、营销增长受阻等多方面影响&#xff0c;企业的营销面临前所未有的挑战和机遇&#xff0c;精准化营销似乎已成为很多公司的选择&#xff0c;本文针对以下客户细分五大模块进行总结&#xff1a; 一&#xff1a;客户细分的必要性&#xff1a; 顾…

Linux操作系统备份之二:通过tar拷贝分区实现Linux操作数据的在线备份

http://www.tektea.com/archives/2163.html。 在《Linux操作系统备份之一&#xff1a;使用LVM快照实现Linux操作系统数据的在线备份》文章中&#xff0c;我们介绍了使用LVM快照实现操作性系统在线备份的方法&#xff0c;LVM快照可以实现在线操作系统数据的备份&#xff0c;在线…

cad快捷命令大全_最全CAD快捷键命令大全(图文版、文字版、键盘版)

在 CAD操作中我们常用一些快捷键来代替鼠标操作从而提高绘图效率&#xff0c;以下是小编为大家整理的常用快捷键大全&#xff0c;涵盖图文版、文字版、键盘版。图文版&#xff1a;文字版&#xff1a;一、常用功能键F1: 获取帮助F2:实现作图窗和文本窗口的切换F3:控制是否实现对…

WEB API:语音识别

2019独角兽企业重金招聘Python工程师标准>>> x-webkit-speech 语音输入功能&#xff1a; http://www.iinterest.net/2012/01/07/x-webkit-speech/ HTML5语音输入&#xff08;淘宝语音搜索&#xff09;x-webkit-speech方法 支持webkit内核&#xff1a; http://www.wu…

cad在线转换_CAD转PDF批量转换怎么转?教你一次性操作,一看就会

接触AutoCAD设计&#xff0c;自然离不开各种的格式转换&#xff0c;CAD转PDF&#xff0c;CAD转JPG、CAD版本转换……感觉不知道哪里下手。特别是批量进行转换操作&#xff0c;更是没有头绪&#xff0c;其实很简单&#xff0c;这里教你一招轻松转换&#xff0c;看一遍变就会操作…

怎么写一个高性能应用?

首先声明&#xff0c;这不是写一个高性能应用的唯一选择&#xff0c;只是自己实践后的一些心得分享。开发前定个小目标有目标的好处是不会降配开发&#xff0c;也不会过度开发目标指标&#xff1a;并发数&#xff0c;TPS&#xff0c;响应时间等1、模块独立性让路高性能&#xf…

[Linux]Linux下安装和配置solr/tomcat/IK分词器 详细实例二.

为了更好的排版, 所以将IK分词器的安装重启了一篇博文, 大家可以接上solr的安装一同查看.[Linux]Linux下安装和配置solr/tomcat/IK分词器 详细实例一: http://www.cnblogs.com/wang-meng/p/5814798.html8, 打开浏览器查看solr可视化界面到了这里solr就配置好了, 可是我们的IK …

百度pcs 如何获取Access Token

为什么80%的码农都做不了架构师&#xff1f;>>> 看官方两篇文章&#xff1a; 《获取Access Token》 http://developer.baidu.com/wiki/index.php?titledocs/pcs/guide/token_authorize 《使用Refresh Token获取Access Token》 http://developer.baidu.com/wik…

怎么把ppt文字大小设置一致_PPT“烫金字”,不用再劳烦设计师了

平时&#xff0c;我们经常看到“烫金字”&#xff0c;好像要设计师用Photoshop才能做的样子。想到Photoshop&#xff0c;很多人便止步了。今天&#xff0c;菜鸟菌教大家使用PPT轻松制作烫金字&#xff0c;不用再麻烦设计师了&#xff0c;自己也可以简单做出这样的烫金字。只要3…

无状态服务(stateless service)

一、定义 无状态服务&#xff08;stateless service&#xff09;对单次请求的处理&#xff0c;不依赖其他请求&#xff0c;也就是说&#xff0c;处理一次请求所需的全部信息&#xff0c;要么都包含在这个请求里&#xff0c;要么可以从外部获取到&#xff08;比如说数据库&#…