I/O模型——完成端口


设计目的:

  常见的网络通信分为两种:同步和异步。

  在同步通信中,每一次接受数据都会导致主线程的挂起,从而阻塞住了其他操作。为了解决这一问题,我们通常会采取同步通信+多线程的策略,即为每一个连入的Socket分配一个线程。然而随着连入的Socket的数量的增加,线程的数量也在增加,这样CPU则需要不停地进行线程的切换,因此难以成为高性能的服务器程序。

  异步通信则可以把接收数据这一操作交给内核,即在内核接收数据的时候,主线程可以不用被阻塞并且继续执行其他操作,而一旦接收数据完成以后,再由内核通知主线程。而如何通知主线程是一个关键,不同的异步通信策略有着不同的通知方式。

  在这样的情况下,完成端口这一I/O模型被提出,成为目前Windows下性能最好的I/O模型之一。

  

实现原理:

  首先根据CPU数量开好线程,当有用户请求的时候,把这些请求加入一个特定的消息队列中,而事先开好的线程则会排队从这个消息队列中获取请求并作出处理。完成端口正是指这一消息队列.

  

基本流程:


主要的API:

  • 创建完成端口
HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0 );
HANDLE WINAPI CreateIoCompletionPort(
_in_ HANDLE FileHandle,
// Socket的句柄,置为INVALID_HANDLE_VALUE表示创建一个没有和任何HANDLE有关系的完成端口 _in_opt HANDLE ExistingCompletionPort,
// NULL表示新建一个完成端口 _in_ ULONG_PTR CompletionKey,
// 完成键,创建完成端口时置为0 _in_ DWORD NumberOfConcurrentThreads
// 完成端口并发线程的数量,置0表示有多少个CPU就开多少个线程
);
  • 创建监听Socket
初始化Socket库...
...
listenSoc = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
...
绑定端口,并监听...
  • 将监听的Socket绑定到完成端口上,这里同样使用HANDLE WINAPI CreateIoCompletionPort(...)这一API.
CreateIoCompletionPort(listenSoc, iocp, CompKey, 0);
HANDLE WINAPI CreateIoCompletionPort(
_in_ HANDLE FileHandle,
// 监听Socket的句柄 _in_opt HANDLE ExistingCompletionPort,
// 刚才创建的完成端口 _in_ ULONG_PTR CompletionKey,
// 完成键,我们在绑定的同时为其分配一段内存空间,以存储与这一Socket相关的信息,当网络操作完成的时候,我们可以根据这段内存空间里面的信息分辨这是哪一个Socket _in_ DWORD NumberOfConcurrentThreads
// 完成端口并发线程的数量,置0表示有多少个CPU就开多少个线程
);
  • 在监听端口上投递AcceptEX请求

    AcceptEX与传统的Accept有三个主要不同点:
  1. AcceptEX采取异步方式,可以同时投递多个请求,而Accept采取阻塞的方式,依次只能处理一个请求。
  2. AcceptEX会事先准备好Socket,当用户请求连入的时候直接使用这一新的Socket,避免临时创建Socket。
  3. AcceptEX接受连入请求的同时,我们可以附加一些数据,这样我们就可以在接受用户连入的同时,接受来自用户的第一组数据。
BOOL AcceptEx (
SOCKET sListenSocket, // 监听Socket
SOCKET sAcceptSocket, // 事先准备好给新用户的Socket
PVOID lpOutputBuffer, // 接受缓冲区
DWORD dwReceiveDataLength, // 用于存放用户第一组数据的空间大小
DWORD dwLocalAddressLength, // 本地地址的空间大小
DWORD dwRemoteAddressLength, // 客户端地址的空间大小
LPDWORD lpdwBytesReceived,
LPOVERLAPPED lpOverlapped
// 重叠结构,每一个网络操作都会对应一个重叠结构,相当于网络操作的ID
);
  • 投递接受数据请求
int WSARecv(
SOCKET s, // 接受数据的Socket
LPWSABUF lpBuffers, // 接收缓冲区
DWORD dwBufferCount, // 置为1
LPDWORD lpNumberOfBytesRecvd, // 所接收到的字节数
LPDWORD lpFlags, // 置为0
LPWSAOVERLAPPED lpOverlapped, // 这个Socket对应的重叠结构
NULL
);
  • 解析AcceptEX接收到的数据

    AcceptEX缓冲区里面保存着本地地址,客户端地址以及客户端发来的第一组数据,因此我们需要使用GetAcceptExSockAddrs()来解析这些数据.
void GetAcceptExSockaddrs(
_In_ PVOID lpOutputBuffer,
// AcceptEX中的缓冲区
_In_ DWORD dwReceiveDataLength,
// 用户第一组数据的空间大小
_In_ DWORD dwLocalAddressLength,
// 本地地址的空间大小
_In_ DWORD dwRemoteAddressLength,
// 客户端地址的空间大小
_Out_ LPSOCKADDR *LocalSockaddr,
// 本地地址
_Out_ LPINT LocalSockaddrLength,
// 实际本地地址的空间大小
_Out_ LPSOCKADDR *RemoteSockaddr,
// 客户端地址
_Out_ LPINT RemoteSockaddrLength
// 实际客户端地址的大小
);

参考: 1. 完成端口(CompletionPort)详解 - 手把手教你玩转网络编程系列之三

    2. Overlapped模型深入分析

Windows下性能最好的I/O模型——完成端口的更多相关文章

  1. windows下的套接字IO模型

    一般情况下,IO操作的行为受两种因素的影响: IO操作对象的类型(阻塞还是非阻塞) 获取IO操作结果的方式(同步还是异步). 同步就是指操作的发起和操作结果的获取由调用者完成. 异步指操作发起由调用方 ...

  2. 关于获取Windows下性能参数的总结

    Windows下特定进程或者所有进程的CPU.物理内存.虚拟内存等性能参数的获取方法小结,包括如何在MFC中以及如何使用C#语言来获取参数. VC API:GlobalMemoryStatus 获取全 ...

  3. 获取Windows下某进程监听的TCP/UDP端口

    1.在Windows下用CMD netstat命令可以获得当前进程监听端口号的信息,如netstat -ano可以看到IP.port.状态和监听的PID. 那么可以执行CMD这个进程得到监听的端口号信 ...

  4. windows下使用远程工具登录虚拟机上的Linux、访问虚拟机上的服务 、端口转发、win7 telnet登陆虚拟机

    首先要清楚virtual box如何设置端口转发: 一篇文章: 如何使用VirtualBox进行端口转发 由于默认的方式是用NAT来做虚拟机网络的,因此如果从外网想访问虚拟机的应用会比较麻烦.以前一直 ...

  5. 关于 Poco::TCPServer框架 (windows 下使用的是 select模型) 学习笔记.

    说明 为何要写这篇文章 ,之前看过阿二的梦想船的<Poco::TCPServer框架解析> http://www.cppblog.com/richbirdandy/archive/2010 ...

  6. 比较windows下的5种IO模型

    看到一个很有意思的解释: 老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系.他们的信会被邮递员投递到他们的信箱里. 这和Socket模型非常类似.下面我就以老陈接收信件为例讲解Socke ...

  7. 机器学习模型从windows下 spring上传到预发布会导致模型不可加载

    1.通过上传到redis,程序通过redis拉取模型,解决问题. 2.问题原因初步思考为windows下模型文件上传到 linux导致,待继续跟进查找.

  8. windows下的IO模型之异步选择(WSAAsyncSelect)模型

    异步选择(WSAAsyncSelect)模型是一个有用的异步I/O 模型.其核心函数是WSAAsyncSelect,该函数是非阻塞的 (关于异步io的理解详情可以看:http://www.cnblog ...

  9. 记一次虚拟化环境下Windows IO性能的解析

    前言随着云计算技术与服务的发展和进步,越来越多的客户选择将业务部署到云端.但由于引入了虚拟化层,在业务部署过程中经常会遇到IO问题,通常也不易调试.本文主要介绍利用perf.systemtap等工具, ...

随机推荐

  1. [原创]一种简单的cocos2d-x动态更新方案

    介绍一个曾经在cocos2d-x项目中使用过的动态更新方案,这个方案简单易行,针对小的项目非常有用. 这个方案有两个核心的关键词:JSON,MD5. 原理可以简单地概括为:服务端持有所有动态更新资源文 ...

  2. 【暑假】[实用数据结构]UVAlive 4329 Ping pong

    UVAlive 4329 Ping pong 题目: Ping pong Time Limit: 3000MS   Memory Limit: Unknown   64bit IO Format: % ...

  3. 安装sql server 出现挂起问题

    1.安装SQL Server的时候出现的情况.“以前的某个程序已在安装计算机上创建挂起的文件操作.运行安装之前必须重新启动计算机”的提示. 2.解决方法 关于SQL安装被挂起的修复大家可能都遇到过当S ...

  4. leetcode@ [22]Generate Parentheses (递归 + 卡特兰数)

    https://leetcode.com/problems/generate-parentheses/ Given n pairs of parentheses, write a function t ...

  5. 设置IE浏览器代理上网

    在局域网中,服务器可以直接通过IE网上冲浪,而工作站要想通过IE上网,如果是在服务器使用代理软件的情况下,其IE需要设置代理. 步骤一:启动IE浏览器,选择"工具",再" ...

  6. 第二十章、启动流程、模块管理与 Loader

    核心与核心模块 在整个启动的过程当中,是否能够成功地驱动我们主机的硬件配备, 是核心 (kernel) 的工作!而核心一般都是压缩档,因此在使用核心之前,就得要将它解压缩后, 才能加载主内存当中. 另 ...

  7. hdoj 1856 More is better【求树的节点数】

    More is better Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 327680/102400 K (Java/Others) ...

  8. [Objective-c 基础 - 2.9] 类的本质

    A.概念 类对象:类也是一个对象,是Class类型的对象 实例对象:创建的对象,有一个isa指针指向类   B.操作 获取内存中的内对象 1. 使用实例对象获取 Class c = [p class] ...

  9. CSS层叠样式选择器归纳

     常用选择器 1.1 类型选择器:用来寻找特定类型的元素  标签 { 声明 } p { color:black; } h1 { font-weight:bold; } 1.2 后代选择器: 选择一个元 ...

  10. Young不等式的一个新证明

    设 $p>0,q>0,a>0,b>0$ 且 $1/p+1/q=1$ 有 \[ab\leq \frac{a^{p}}{p}+\frac{b^{q}}{q}\] 证明:设 \[f( ...