手把手教你玩转网络编程模型之完成例程(Completion Routine)篇

  言

记得写这个系列的上一篇文章的时候已经是四年前了,准确的说是四年半以前了,翻开我尘封已久的IO模型里面的“完成例程”的实现方式及示例代码。

本文凝聚着笔者心血,如要转载,请指明原作者及出处,谢谢!不过代码写得不好,欢迎改进,而且没有版权,请随便散播、使用。OK, Let’s go ! Have fun!

本文配套的示例源码下载地址(在我的下载空间里)

http://piggyxp.download.csdn.net/

MFC代码,配有非常非常详尽的注释,功能只是简单的显示一下各个客户端发来的字符,作为教学代码,为了使得代码结构清晰明了,简化了很多地方,用于产品开发的话还需要做很多改进,有错误或者不足的地方,非常欢迎大家不吝指出。

本文假设你已经对重叠I/O的机制已有了解,否则请先参考本系列的前一篇《手把手教你玩转重叠IO模型》

目录:

1.完成例程的优点

2.完成例程的基本原理

3.关于完成例程的函数介绍

4.完成例程的实现步骤

5.实际应用中应该进一步完善的地方

一.        完成例程的优点

1.   首先需要指明的是,这里的“完成例程”(Completion Routine)并非是大家所常听到的“完成端口”(Completion Port),而是另外一种管理重叠I/O请求的方式,而至于什么是重叠I/O,简单来讲就是Windows系统内部管理I/O的一种方式,核心就是调用的ReadFile和WriteFile函数,在制定设备上执行I/O操作,不光是可用于网络通信,也可以用于其他需要的地方。

在Windows系统中,管理重叠I/O可以有三种方式:

(1) 上一篇中提到的基于事件通知的重叠I/O模型

(2) 本篇中将要讲述的基于“完成例程”的重叠I/O模型

(3) 下一篇中将要讲到的“完成端口”模型

虽然都是基于重叠I/O,但是因为前两种模型都是需要自己来管理任务的分派,所以性能上没有区别,而完成端口是创建完成端口对象使操作系统亲自来管理任务的分派,所以完成端口肯定是能获得最好的性能。

2.   如果你想要使用重叠I/O机制带来的高性能模型,又懊恼于基于事件通知的重叠模型要收到64个等待事件的限制,还有点畏惧完成端口稍显复杂的初始化过程,那么“完成例程”无疑是你最好的选择!^_^因为完成例程摆脱了事件通知的限制,可以连入任意数量客户端而不用另开线程,也就是说只用很简单的一些代码就可以利用Windows内部的I/O机制来获得网络服务器的高性能,是不是心动了呢?那就一起往下看。。。。。。。。。。

3.   而且个人感觉“完成例程”的方式比重叠I/O更好理解,因为就和我们传统的“回调函数”是一样的,也更容易使用一些,推荐!

二.        完成例程的基本原理

概括一点说,上一篇拙作中提到的那个基于事件通知的重叠I/O模型,在你投递了一个请求以后(比如WSARecv),系统在完成以后是用事件来通知你的,而在完成例程中,系统在网络操作完成以后会自动调用你提供的回调函数,区别仅此而已,是不是很简单呢?

首先这里统一几个名词,包括“重叠操作”、“重叠请求”、“投递请求”等等,这是为了配合这的重叠I/O才这么讲的,说的直白一些,也就是你在代码中发出的WSARecv()、WSASend()等等网络函数调用。

上篇文章中偷懒没画图,这次还是画个流程图来说明吧,采用完成例程的服务器端,通信流程简单的来讲是这样的:

从图中可以看到,服务器端存在一个明显的异步过程,也就是说我们把客户端连入的SOCKET与一个重叠结构绑定之后,便可以将通讯过程全权交给系统内部自己去帮我们调度处理了,我们在主线程中就可以去做其他的事情,边等候系统完成的通知就OK,这也就是完成例程高性能的原因所在。

如果还没有看明白,我们打个通俗易懂的比方,完成例程的处理过程,也就像我们告诉系统,说“我想要在网络上接收网络数据,你去帮我办一下”(投递WSARecv操作),“不过我并不知道网络数据合适到达,总之在接收到网络数据之后,你直接就调用我给你的这个函数(比如_CompletionProess),把他们保存到内存中或是显示到界面中等等,全权交给你处理了”,于是乎,系统在接收到网络数据之后,一方面系统会给我们一个通知,另外同时系统也会自动调用我们事先准备好的回调函数,就不需要我们自己操心了。

看到这里,各位应该已经对完成例程的体系结构有了比价清晰的了解了吧,下面各位喝点咖啡转转脖子休息休息,然后就进入到下面的具体实现部分了。

一.        完成例程的函数介绍

这个部分将要介绍在完成例程模型中会使用到的关键函数,内容比较枯燥,大家要做好心理准备。不过在实际应用以前,很多东西肯定也不会理解得太深刻,可以先泛泛的了解一下,以后再回头复习这里的知识就可以了。

厄。。。。。。仔细审查了一下代码,发现其实这里也没有什么新函数好介绍了,大部分都是使用重叠模型那一章里介绍的一样的函数,需要查看的朋友请看这里《手把手教你玩转重叠IO模型》

,这里就不再重复了:

这里只补充一个知识点,就是咱们完成例程方式和前面的事件通知方式最大的不同之处就在于,我们需要提供一个回调函数供系统收到网络数据后自动调用,回调函数的参数定义应该遵照如下的函数原型:

1. 完成例程回调函数原型及传递方式

函数应该是这样定义的,函数名字随便起,但是参数类型不能错

[cpp] view plaincopy

Void CALLBACK _CompletionRoutineFunc(

DWORD dwError, // 标志咱们投递的重叠操作,比如WSARecv,完成的状态是什么

DWORD cbTransferred, // 指明了在重叠操作期间,实际传输的字节量是多大

LPWSAOVERLAPPED lpOverlapped, // 参数指明传递到最初的IO调用内的一个重叠  结构

DWORD dwFlags  // 返回操作结束时可能用的标志(一般没用));

还有一点需要重点提一下的是,因为我们需要给系统提供一个如上面定义的那样的回调函数,以便系统在完成了网络操作后自动调用,这里就需要提一下究竟是如何把这个函数与系统内部绑定的呢?如下所示,在WSARecv函数中是这样绑定的

[cpp] view plaincopy

int WSARecv(

SOCKET s,                      // 当然是投递这个操作的套接字

LPWSABUF lpBuffers,          // 接收缓冲区,与Recv函数不同

// 这里需要一个由WSABUF结构构成的数组

DWORD dwBufferCount,        // 数组中WSABUF结构的数量,设置为1即可

LPDWORD lpNumberOfBytesRecvd,  // 如果接收操作立即完成,这里会返回函数调用

// 所接收到的字节数

LPDWORD lpFlags,             // 说来话长了,我们这里设置为0 即可

LPWSAOVERLAPPED lpOverlapped,  // “绑定”的重叠结构

LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine

// 我们的完成例程函数的指针

);

其他参数我们可以先不用先细看,只看最后一个,看到了吗?直接在

[cpp] view plaincopy

举个例子:(变量的定义顺序和上面的说明的顺序是对应的,下同)

SOCKET s;

WSABUF DataBuf;           // 定义WSABUF结构的缓冲区

// 初始化一下DataBuf

#define DATA_BUFSIZE 4096

char buffer[DATA_BUFSIZE];

ZeroMemory(buffer, DATA_BUFSIZE);

DataBuf.len = DATA_BUFSIZE;

DataBuf.buf = buffer;

DWORD dwBufferCount = 1, dwRecvBytes = 0, Flags = 0;

// 建立需要的重叠结构,每个连入的SOCKET上的每一个重叠操作都得绑定一个

WSAOVERLAPPED AcceptOverlapped ;// 如果要处理多个操作,这里当然需要一个

// WSAOVERLAPPED数组

ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));

// 作了这么多工作,终于可以使用WSARecv来把我们的完成例程函数绑定上了

// 当然,假设我们的_CompletionRoutine函数已经定义好了

WSARecv(s, &DataBuf, dwBufferCount, &dwRecvBytes,

&Flags, &AcceptOverlapped, _CompletionRoutine);

MSDN这么个好帮手,而且在讲后面的完成例程和完成端口的时候我还会讲到一些<FONT color="blue" face="""> ^_^

手把手教你玩转网络编程模型之完成例程(Completion Routine)篇(上)-转相关推荐

  1. 手把手教你玩转网络编程模型之完成例程(Completion Routine)篇(下)-转

    续 手把手教你玩转网络编程模型之完成例程(Completion Routine)篇(上) 四.         完成例程的实现步骤 基础知识方面需要知道的就是这么多,下面我们配合代码,来一步步的讲解如 ...

  2. 手把手教你玩转网络编程模型之完成例程(Completion Routine)

    前  言 记得写这个系列的上一篇文章的时候已经是四年前了,准确的说是四年半以前了,翻开我尘封已久的IO模型里面的"完成例程"的实现方式及示例代码. 本文凝聚着笔者心血,如要转载,请 ...

  3. 完毕port(CompletionPort)具体解释 - 手把手教你玩转网络编程系列之三

       手把手叫你玩转网络编程系列之三    完毕port(Completion Port)具体解释                                                    ...

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

    手把手叫你玩转网络编程系列之三 完成端口(Completion Port)详解 ----- By PiggyXP(小猪) 前 言 本系列里完成端口的代码在两年前就已经写好了,但是由于许久没有写东西了, ...

  5. 手把手教你玩转SOCKET模型:完成例程(Completion Routine)篇

    本文假设你已经对重叠I/O的机制已有了解,否则请先参考本系列的前一篇<手把手教你玩转SOCKET模型之重叠I/O篇>: 目录: 1.完成例程的优点 2.完成例程的基本原理 3.关于完成例程 ...

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

    完成端口(Completion Port)详解 本系列里完成端口的代码在两年前就已经写好了,但是由于许久没有写东西了,不知该如何提笔,所以这篇文档总是在酝酿之中--酝酿了两年之后,终于决定开始动笔了, ...

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

    2019独角兽企业重金招聘Python工程师标准>>> 前 言 本系列里完成端口的代码在两年前就已经写好了,但是由于许久没有写东西了,不知该如何提笔,所以这篇文档总是在酝酿之中--酝 ...

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

    [第六步]当收到Accept通知时 _DoAccept() 在用户收到AcceptEx的完成通知时,需要后续代码并不多,但却是逻辑最为混乱,最容易出错的地方,这也是很多用户为什么宁愿用效率低下的acc ...

  9. TCP网络编程模型从入门到实战基础篇,单服务器单个用户非并发版本

    文章目录 前言 一.网络编程实践的必备基础知识 二.系统调用方法刨析 1.socket 2.bind 3.listen 4.accept 5.connect 三.实现一个简单的功能, 服务器将单客户端 ...

最新文章

  1. 浅析网站优化知识自学从哪些方面开始起步
  2. python工资等级分类程序-php项目中用python来预测薪资(工资)
  3. JAVA项目实训struts2_Java Web项目搭建过程记录(struts2)
  4. flask 路由 php文件,Flask 请求处理流程(一):WSGI 和 路由
  5. OSChina 周日乱弹 —— 我叫张一条
  6. 吉林大学计算机英语成绩,吉林大学复试出结果,初试分差84分,复试成绩却相差无几!...
  7. 5-10transformation中部分函数使用
  8. oracle 进入gdsctl,oracle的分析函数over 及开窗函数[转]
  9. NHibernate.3.0.Cookbook第三章第8节的翻译
  10. sql azure 语法_Azure Data Studio中SQL Server架构比较扩展
  11. win10鼠标右键拓展(使用vs Code打开)
  12. vue全家桶+element-UI
  13. 企业微信使用扫一扫案例(含代码和具体步骤)
  14. 接口耗时优化与cpu飙高解决
  15. 高德地图上绘制城市名字和带涟漪的点标记
  16. CVE-2020-1472NetLogon权限提升漏洞
  17. 拉马努金,天才之超越
  18. PS抠字、PS修改图片文字
  19. JPA 7. Spring 整合 JPA
  20. iOS Charles IM抓包

热门文章

  1. DataGrid分页;指定列的总和和平均值;显示鼠标背景色;弹出式窗口;
  2. python的os,commands,subprocess启动进程调用的几种方法
  3. 基础总结篇之三:Activity的task相关
  4. Android FrameWork——Activity启动过程详解
  5. Spring MVC学习笔记——SiteMesh的使用(转)
  6. hadoop mapreduce开发实践之本地文件分发by streaming
  7. $_SERVER[HTTP_HOST]和$_SERVER[SERVER_NAME]
  8. 关于spring MVC机制,示例解读
  9. LVS+Heartbeat+Ipvsadm+Ldirectord安装 (一)
  10. 西塘游(2007-08-14)