欢迎阅读此篇IOCP教程。我将先给出IOCP的定义然后给出它的实现方法,最后剖析一个Echo程序来为您拨开IOCP的谜云,除去你心中对IOCP的烦恼。OK,但我不能保证你明白IOCP的一切,但我会尽我最大的努力。以下是我会在这篇文章中提到的相关技术:
  I/O端口
  同步/异步
  堵塞/非堵塞
  服务端/客户端
  多线程程序设计
  Winsock API 2.0

  在这之前,我曾经开发过一个项目,其中一块需要网络支持,当时还考虑到了代码的可移植性,只要使用
select,connect,accept,listen,send还有recv,再加上几个#ifdef的封装以用来处理Winsock和BSD套接
字[socket]中间的不兼容性,一个网络子系统只用了几个小时很少的代码就写出来了,至今还让我很回味。那以后很长时间也就没再碰了。

  前些日子,我们策划做一个网络游戏,我主动承担下网络这一块,想想这还不是小case,心里偷着乐啊。网络游戏好啊,网络游戏为成百上千的玩家提供了
乐趣和令人着秘的游戏体验,他们在线上互相战斗或是加入队伍去战胜共同的敌人。我信心满满的准备开写我的网络,于是乎,发现过去的阻塞同步模式模式根本不
能拿到一个巨量多玩家[MMP]的架构中去,直接被否定掉了。于是乎,就有了IOCP,如果能过很轻易而举的搞掂IOCP,也就不会有这篇教程了。下面请
诸位跟随我进入正题。

什么是IOCP?
先让我们看看对IOCP的评价
I/O完成端口可能是Win32提供的最复杂的内核对象。
[Advanced Windows 3rd] Jeffrey Richter
这是[IOCP]实现高容量网络服务器的最佳方法。
[Windows Sockets2.0:Write Scalable Winsock Apps Using
Completion Ports]
Microsoft Corporation
完成端口模型提供了最好的伸缩性。这个模型非常适用来处理数百乃至上千个套接字。
[Windows网络编程2nd] Anthony Jones & Jim Ohlund
I/O completion
ports特别显得重要,因为它们是唯一适用于高负载服务器[必须同时维护许多连接线路]的一个技术。Completion
ports利用一些线程,帮助平衡由I/O请求所引起的负载。这样的架构特别适合用在SMP系统中产生的”scalable”服务器。
[Win32多线程程序设计] Jim Beveridge & Robert Wiener

看来我们完全有理由相信IOCP是大型网络架构的首选。那IOCP到底是什么呢?

  微软在Winsock2中引入了IOCP这一概念 。IOCP全称I/O Completion

Port,中文译为I/O完成端口。IOCP是一个异步I/O的API,它可以高效地将I/O事件通知给应用程序。与使用select()或是其它异步方
法不同的是,一个套接字[socket]与一个完成端口关联了起来,然后就可继续进行正常的Winsock操作了。然而,当一个事件发生的时候,此完成端
口就将被操作系统加入一个队列中。然后应用程序可以对核心层进行查询以得到此完成端口。

  这里我要对上面的一些概念略作补充,在解释[完成]两字之前,我想先简单的提一下同步和异步这两个概念,逻辑上来讲做完一件事后再去做另一件事就是同
步,而同时一起做两件或两件以上事的话就是异步了。你也可以拿单线程和多线程来作比喻。但是我们一定要将同步和堵塞,异步和非堵塞区分开来,所谓的堵塞函
数诸如accept(…),当调用此函数后,此时线程将挂起,直到操作系统来通知它,”HEY兄弟,有人连进来了”,那个挂起的线程将继续进行工作,也就
符合”生产者-消费者”模型。堵塞和同步看上去有两分相似,但却是完全不同的概念。大家都知道I/O设备是个相对慢速的设备,不论打印机,调制解调器,甚
至硬盘,与CPU相比都是奇慢无比的,坐下来等I/O的完成是一件不甚明智的事情,有时候数据的流动率非常惊人,把数据从你的文件服务器中以
Ethernet速度搬走,其速度可能高达每秒一百万字节,如果你尝试从文件服务器中读取100KB,在用户的眼光来看几乎是瞬间完成,但是,要知道,你
的线程执行这个命令,已经浪费了10个一百万次CPU周期。所以说,我们一般使用另一个线程来进行I/O。重叠IO[overlapped

I/O]是Win32的一项技术,你可以要求操作系统为你传送数据,并且在传送完毕时通知你。这也就是[完成]的含义。这项技术使你的程序在I/O进行过
程中仍然能够继续处理事务。事实上,操作系统内部正是以线程来完成overlapped
I/O。你可以获得线程所有利益,而不需要付出什么痛苦的代价。

  完成端口中所谓的[端口]并不是我们在TCP/IP中所提到的端口,可以说是完全没有关系。我到现在也没想通一个I/O设备[I/O

Device]和端口[IOCP中的Port]有什么关系。估计这个端口也迷惑了不少人。IOCP只不过是用来进行读写操作,和文件I/O倒是有些类似。
既然是一个读写设备,我们所能要求它的只是在处理读与写上的高效。在文章的第三部分你会轻而易举的发现IOCP设计的真正用意。

IOCP和网络又有什么关系?

int main()
{
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    ListeningSocket = socket(AF_INET, SOCK_STREAM, 0);

bind(ListeningSocket, (SOCKADDR*)&ServerAddr,
sizeof(ServerAddr));

listen(ListeningSocket, 5);

int nlistenAddrLen = sizeof(ClientAddr);

while(TRUE)

{

NewConnection = accept(ListeningSocket,
(SOCKADDR*)&ClientAddr, &nlistenAddrLen);

HANDLE hThread = CreateThread(NULL, 0, ThreadFunc, (void*)
NewConnection, 0, &dwTreadId);

CloseHandle(hThread);

}

return 0;
}

  相信只要写过网络的朋友,应该对这样的结构在熟悉不过了。accept后线程被挂起,等待一个客户发出请求,而后创建新线程来处理请求。当新线程处理
客户请求时,起初的线程循环回去等待另一个客户请求。处理客户请求的线程处理完毕后终结。

  在上述的并发模型中,对每个客户请求都创建了一个线程。其优点在于等待请求的线程只需做很少的工作。大多数时间中,该线程在休眠[因为recv处于堵塞状态]。

  但是当并发模型应用在服务器端[基于Windows NT],Windows

NT小组注意到这些应用程序的性能没有预料的那么高。特别的,处理很多同时的客户请求意味着很多线程并发地运行在系统中。因为所有这些线程都是可运行的
[没有被挂起和等待发生什么事],Microsoft意识到NT内核花费了太多的时间来转换运行线程的上下文[Context],线程就没有得到很多
CPU时间来做它们的工作。

  大家可能也都感觉到并行模型的瓶颈在于它为每一个客户请求都创建了一个新线程。创建线程比起创建进程开销要小,但也远不是没有开销的。

  我们不妨设想一下:如果事先开好N个线程,让它们在那hold[堵塞],然后可以将所有用户的请求都投递到一个消息队列中去。然后那N个线程逐一从消
息队列中去取出消息并加以处理。就可以避免针对每一个用户请求都开线程。不仅减少了线程的资源,也提高了线程的利用率。理论上很不错,你想我等泛泛之辈都
能想出来的问题,Microsoft又怎会没有考虑到呢?!

  这个问题的解决方法就是一个称为I/O完成端口的内核对象,他首次在Windows NT3.5中被引入。

  其实我们上面的构想应该就差不多是IOCP的设计机理。其实说穿了IOCP不就是一个消息队列嘛!你说这和[端口]这两字有何联系。我的理解就是IOCP最多是应用程序和操作系统沟通的一个接口罢了。

  至于IOCP的具体设计那我也很难说得上来,毕竟我没看过实现的代码,但你完全可以进行模拟,只不过性能可能…,如果想深入理解IOCP,
Jeffrey Ritchter的Advanced Windows
3rd其中第13章和第14张有很多宝贵的内容,你可以拿来窥视一下系统是如何完成这一切的。

实现方法

Microsoft为IOCP提供了相应的API函数,主要的就两个,我们逐一的来看一下:

HANDLE
CreateIoCompletionPort (

HANDLE
FileHandle,             
// handle to file

HANDLE
ExistingCompletionPort,  // handle to I/O completion
port

ULONG_PTR
CompletionKey,

// completion key

DWORD
NumberOfConcurrentThreads // number of threads to execute
concurrently
);

在讨论各参数之前,首先要注意该函数实际用于两个截然不同的目的:
1.用于创建一个完成端口对象
2.将一个句柄[HANDLE]和完成端口关联到一起

  在创建一个完成一个端口的时候,我们只需要填写一下NumberOfConcurrentThreads这个参数就可以了。它告诉系统一个完成端口上
同时允许运行的线程最大数。在默认情况下,所开线程数和CPU数量相同,但经验给我们一个公式:
  线程数 = CPU数 * 2 + 2

要使完成端口有用,你必须把它同一个或多个设备相关联。这也是调用CreateIoCompletionPort完成的。你要向该函数传递一个已有的完成
端口的句柄,我们既然要处理网络事件,那也就是将客户的socket作为HANDLE传进去。和一个完成键[对你有意义的一个32位值,也就是一个指针,
操作系统并不关心你传什么]。每当你向端口关联一个设备时,系统向该完成端口的设备列表中加入一条信息纪录。

另一个API就是

BOOL GetQueuedCompletionStatus(

HANDLE CompletionPort,

// handle to completion port

LPDWORD lpNumberOfBytes,

// bytes transferred

PULONG_PTR lpCompletionKey,   // file completion
key

LPOVERLAPPED *lpOverlapped,   // buffer

DWORD dwMilliseconds

 

// optional timeout value
);

第一个参数指出了线程要监视哪一个完成端口。很多服务应用程序只是使用一个I/O完成端口,所有的I/O请求完成以后的通知都将发给该端口。简单的
说,GetQueuedCompletionStatus使调用线程挂起,直到指定的端口的I/O完成队列中出现了一项或直到超时。同I/O完成端口相关
联的第3个数据结构是使线程得到完成I/O项中的信息:传输的字节数,完成键和OVERLAPPED结构的地址。该信息是通过传递给
GetQueuedCompletionSatatus的
lpdwNumberOfBytesTransferred,lpdwCompletionKey和lpOverlapped参数返回给线程的。

根据到目前为止已经讲到的东西,首先来构建一个frame。下面为您说明了如何使用完成端口来开发一个echo服务器。大致如下:
  1.初始化Winsock
  2.创建一个完成端口
  3.根据服务器线程数创建一定量的线程数
  4.准备好一个socket进行bind然后listen
  5.进入循环accept等待客户请求
  6.创建一个数据结构容纳socket和其他相关信息
  7.将连进来的socket同完成端口相关联
  8.投递一个准备接受的请求
以后就不断的重复5至8的过程
那好,我们用具体的代码来展示一下细节的操作。

  WOW,程序的代码若是贴在此处,实在大煞风景,又不能CTRL+V还不能F7,如果大家需要源代码可以发信给我o_nono@163.net

  至此文章也该告一段落了,我带着您做了一趟旋风般的旅游,游览了所谓的完成端口。

理解I/O Completion Port的更多相关文章

  1. [转载]理解 I/O Completion Port (IOCP完成端口)

    原文:理解 I/O Completion Port (IOCP完成端口)欢迎阅读此篇IOCP教程.我将先给出IOCP的定义然后给出它的实现方法,最后剖析一个Echo程序来为您拨开IOCP的谜云,除去你 ...

  2. [转]理解I/O Completion Port

    原文:http://dev.gameres.com/Program/Control/IOCP.htm 另附上:http://stackoverflow.com/questions/5283032/i- ...

  3. 理解I/O Completion Port(完成端口)

    欢迎阅读此篇IOCP教程.我将先给出IOCP的定义然后给出它的实现方法,最后剖析一个Echo程序来为您拨开IOCP的谜云,除去你心中对IOCP的烦恼.OK,但我不能保证你明白IOCP的一切,但我会尽我 ...

  4. 完成端口(Completion Port)详解(转)

    手把手叫你玩转网络编程系列之三    完成端口(Completion Port)详解                                                           ...

  5. (转载)完成端口(Completion Port, I/OCP)详解

    http://www.cnblogs.com/lancidie/archive/2011/12/19/2293773.html 手把手叫你玩转网络编程系列之三    完成端口(Completion P ...

  6. 转:完成端口(Completion Port)详解

    手把手叫你玩转网络编程系列之三    完成端口(Completion Port)详解                                                           ...

  7. 服务器开发基础-Tcp/Ip网络模型—完成端口(Completion Port)模型

    本文对于初学网络编程的极为友好,文中所有代码全部基于C语言实现,文中见解仅限于作者对于完成端口的初步认识,由于作者才疏学浅,出现的错误和纰漏,麻烦您一定要指出来,咱们共同进步.谢谢!!! 完成端口(c ...

  8. IOCP Input/Output Completion Port IO完成端口

    I/O completion ports provide an efficient threading model for processing multiple asynchronous I/O r ...

  9. Window I/O 完成端口 (Windows I/O Completion Port (IOCP))

    相关对象 IO EndPoint, 所有支持重叠IO(overlapped IO)的设备,比如文件,Winsock,管道等. IOCP, IO完成端口内核对象,可以使用API CreateIoComp ...

随机推荐

  1. 初步认识 Web Service

     Web Service初步认识   Web Service:不是框架,不是一种技术,而是一种跨平台,跨语言的规范. 作用:异构平台之间的交互,解决了不同平台,不同语言所编写的应用之间的相互调用.(远 ...

  2. 享元模式(咖啡屋)【java与模式】

    package com.javapatterns.flyweight.coffeeshop; public class Flavor extends Order { private String fl ...

  3. Codevs 1507 酒厂选址

    1507 酒厂选址 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 传送门 题目描述 Description Abstinence(戒酒)岛的居民们酷爱一种无酒精啤酒 ...

  4. 【CF493E】【数学】Vasya and Polynomial

    Vasya is studying in the last class of school and soon he will take exams. He decided to study polyn ...

  5. 通过shell脚本实现代码自动化部署

    通过shell脚本实现代码自动化部署 一.传统部署方式及优缺点 1.传统部署方式 (1)纯手工scp (2)纯手工登录git pull.svn update (3)纯手工xftp往上拉 (4)开发给打 ...

  6. linux 下串口的配置

    串口最基本的设置包括波特率 校验位 和停止位. 主要是设置个结构体的成员值, #include <termios.h> struct termio { unsigned short c_i ...

  7. jquery ajax 的data 存表单的值

    jsp <body> <form action="" method="post" id="formid">  < ...

  8. 诡异的XmlSerializer属性字段Specified

    自动生成代码时,往往会为一个字段假设为 * , 生成另一个bool型字段: *Specified: 如: [Serializable] public class A { [XmlElement] pu ...

  9. 点击listview 的列头对其item进行自动排序

    若要自定义排序顺序,必须编写一个实现 IComparer 接口的类,并将 ListViewItemSorter 属性设置为该类的一个对象.当设置 ListViewItemSorter 属性值时,将自动 ...

  10. 数据库MySQL与xls文件的互导

    最近的一个项目需要将xls表导入到MySQL数据库中和将MySQL数据表导出到xls表中,在网上搜了很多资料,经过多次尝试终于实现了功能,废话不多说,在这粘贴出代码,希望可以帮到需要的朋友. 一.将. ...