再探完成端口

 
  快速入门
       有关完成端口的概念,以及相关的一些API就不讨论了,因为网上介绍这些东西的文章太多了。这里关注的是如何组织代码。首先我们来看《WINDOWS网络编程技术》第八章中有关完成端口的例子。代码可以在网上找到,这里就不贴了。这个不到300行的程序可以让我们迅速入门。
 
查看程序,我们可以看到使用完成端口的服务器的一般步骤:
1.         创建一个完成端口。第四个参数保持为0,指定在完成端口上,每个处理器一次只允许执行一个工作者线程。
2.         判断系统内到底安装了多少个处理器。
3.         创建工作者线程,根据步骤2 )得到的处理器信息,在完成端口上,为已完成的I / O请求提供服务。在这个简单的例子中,我们为每个处理器都只创建两个工作者线程。
4.         准备好一个监听套接字,在端口5150上监听进入的连接请求。
5.         使用 accept 函数,接受进入的连接请求。
6.         创建一个数据结构,这个数据结构通常称之为完成键,同时在结构中存入接受的套接字句柄,以及其他在处理 Socket IO 时需要的信息,以备后用
7.         调用 CreateIoCompletionPort ,将自 accept 返回的新套接字句柄同完成端口关联到一起。通过完成键( CompletionKey )参数,将 6 中的数据结构传递给 CreateIoCompletionPort
8.         开始在已接受的连接上进行 I/O 操作。在此,我们希望通过重叠 I/O 机制,在新建的套接字上投递一个或多个异步 WSARecv WSASend 请求。这些 I/O 请求完成后,一个工作者线程会为 I/O 请求提供服务,同时继续处理未来的 I/O 请求,稍后便会在步骤 3 ) 指定的工作者例程中,体验到这一点。
9.         重复步骤5 ) ~ 8 ),直至服务器中止。
 
对于程序的分析
很明显,5-8步是关键。而在工作者线程中,如何处理IO请求也是关键。其中有两个数据结构很重要,一个就是完成键结构,另一个就是重叠IO结构。这两个结构都是指针的指针,这种晦涩的设计是为了使结构具有更好的扩展性。个人觉得理解怎么使用这两个结构就理解了完成端口编码的核心。当然,要学习操作完成端口的实现机制还是需要了解windows内核是如何维护完成队列之类更深层的东西,这些内容不在本文讨论范围之内。
 
我们可以把完成键结构看作是服务主线程accept连接时和工作者线程交互的平台。一般我们在客户端连接时会将客户端Socket句柄放入这个结构中。除此以外,所有在一次Socket IO中无法保存的数据结构都可以扔到该结构中,让它可以在多次Socket IO中重复使用。例如客户端要向服务器传文件,那么可以在完成键中添加服务器上打开的文件句柄,每次Socket IO就可以使用这个句柄读写数据。
 
我们可以把重叠IO看作是一个可以扩展的Buffer类型。这个Buffer可以根据具体的需求进行改变,它可以携带写入、写入完成、写出、写出完成的标志,也可以携带具体接受或发送的数据,等等。
 
在实际应用中,我们可以将以上两个结构设计成CompletionKey和Buffer两个类,但是要保证代表重叠IO的Buffer类指向的数据结构头部就是一个重叠结构。
 
解决了这两个结构的问题之后还有另一个问题,就是如何进行工作者线程的编程。完成端口的特点是一次数据收到之后程序会得到通知。那么怎么判断我收到的数据是完整的,可以进行下一步处理的呢?通常我们会根据已收到的数据流特征来判断它是否是完整的,对于完整的数据我们就可以进行下一步处理,而对于不完整的数据我们需要等待下次的数据,并且把它和以前的数据拼接起来。这就需要我们实现一个高效的Buffer结构来满足这些需求。当然,这些功能在Windows网络编程技术一书中给出的例子并没有实现。
 
 
  实例分析
 
       经同事指点,我在CodeProject上找到了一个比较符合实际需求的完成端口的例子。它提供了三种类型的服务器,一是简单的EchoServer,另一个是基于自定义包结构的服务器,还有一个就是基于命令的服务器。这三个服务器只是在处理数据的逻辑上有些差异,整体架构都是一样的。
 
基于包结构的服务器和基于命令的服务器的优劣
在游戏服务器中,我们一般有自己的协议,这个协议可以基于包结构,也可以基于命令结构,可以根据需要灵活选择。通常认为包结构设计的好的话服务器和客户端只需要一个连接,而基于命令的服务器如果需要传数据,那么最好使用另一个连接,否则将会无法判断收到的是数据还是命令。但是无论使用哪一种,一个好的解析器是必要的。它可以帮助你高效地解析包内容或者是命令内容。
 
服务器的组织结构
       服务器的主体部分是一个类库工程,main函数所在工程只是继承与类库工程中的CSocketServer类来实现一些逻辑。类库工程对一些windows内核对象进行了包装,例如事件,线程等等。比较重要的部分就是CSocketServer类,Socket类,以及CIOBuffer类的实现。CSocketServer类的协作图如下:

 

 

to be continued..

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值