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

手把手教你玩转网络编程模型之完成例程(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的一种方式,核心就是调用的ReadFileWriteFile函数,在制定设备上执行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. 完成例程回调函数原型及传递方式

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

 

  1. Void CALLBACK _CompletionRoutineFunc(  
  2.   DWORD dwError, // 标志咱们投递的重叠操作,比如WSARecv,完成的状态是什么   
  3.   DWORD cbTransferred, // 指明了在重叠操作期间,实际传输的字节量是多大   
  4.   LPWSAOVERLAPPED lpOverlapped, // 参数指明传递到最初的IO调用内的一个重叠  结构   
  5.   DWORD dwFlags  // 返回操作结束时可能用的标志(一般没用));  

      

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

  1. int WSARecv(  
  2.             SOCKET s,                      // 当然是投递这个操作的套接字   
  3.             LPWSABUF lpBuffers,          // 接收缓冲区,与Recv函数不同   
  4. // 这里需要一个由WSABUF结构构成的数组   
  5.  DWORD dwBufferCount,        // 数组中WSABUF结构的数量,设置为1即可   
  6.    LPDWORD lpNumberOfBytesRecvd,  // 如果接收操作立即完成,这里会返回函数调用   
  7. // 所接收到的字节数   
  8.   LPDWORD lpFlags,             // 说来话长了,我们这里设置为0 即可   
  9.   LPWSAOVERLAPPED lpOverlapped,  // “绑定”的重叠结构   
  10.   LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine  
  11.                                   // 我们的完成例程函数的指针   
  12. );  

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

  1. 举个例子:(变量的定义顺序和上面的说明的顺序是对应的,下同)  
  2. SOCKET s;  
  3. WSABUF DataBuf;           // 定义WSABUF结构的缓冲区   
  4. // 初始化一下DataBuf   
  5. #define DATA_BUFSIZE 4096   
  6. char buffer[DATA_BUFSIZE];  
  7. ZeroMemory(buffer, DATA_BUFSIZE);  
  8. DataBuf.len = DATA_BUFSIZE;  
  9. DataBuf.buf = buffer;  
  10. DWORD dwBufferCount = 1, dwRecvBytes = 0, Flags = 0;  
  11. // 建立需要的重叠结构,每个连入的SOCKET上的每一个重叠操作都得绑定一个   
  12. WSAOVERLAPPED AcceptOverlapped ;// 如果要处理多个操作,这里当然需要一个   
  13. // WSAOVERLAPPED数组   
  14. ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));  
  15.   
  16. // 作了这么多工作,终于可以使用WSARecv来把我们的完成例程函数绑定上了   
  17. // 当然,假设我们的_CompletionRoutine函数已经定义好了   
  18. WSARecv(s, &DataBuf, dwBufferCount, &dwRecvBytes,   
  19. &Flags, &AcceptOverlapped, _CompletionRoutine);  

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值