现在的位置: 首页 > 综合 > 正文

基于消息驱动的面向对象通用C/S应用框架(七)

2013年12月06日 ⁄ 综合 ⁄ 共 2792字 ⁄ 字号 评论关闭

2.2 消息驱动框架的基本结构

        既然是以客户端和服务器端之间的消息传递为基础,那么对于任何一端来说,首先我们要从网络上将消息接收下来,当然前提是对端先发出消息。客户端和服务器具体如何接收及发送消息,这是通信层的事情,我们将在第3章“基于Stream Socket的面向对象网络通信组件”中专门进行详细介绍,所以这里可先假定业务消息已经成功接收下来。

        通过在通信层和框架层之间定义一个接口,通信层可以将基本的网络事件主动地上报到框架层,比如消息到达、连接已经建立、连接断开等等,在通信层中将有专门的线程来负责这些通知工作。这个接口定义如下:

class NetworkObserver;

typedef SmartPtr<NetworkObserver> NetworkObserverSmartPtr;

class NetworkObserver

{

public:

    // 创建NetworkObserver单例对象

    static NetworkObserverSmartPtr CreateObserver();

    virtual ~NetworkObserver();

    // 用户业务消息到达

    virtual void OnMessage(UserMsgSmartPtr pUserMsg,

                           CommunicationUnitID unitId) = 0;

    // 客户端和服务器的连接建立

    virtual void OnLinkUp(CommunicationUnitID unitId) = 0;

    //客户端和服务器的连接断开

    virtual void OnLinkDown(CommunicationUnitID unitId) = 0;

};

        在框架层,将所有网络事件以及期望应用层来处理的本地事件都统一转换为消息,并用不同的消息类型和消息ID来区分,比如本地定时器消息等,这样消息分发就可以以一致的方式来工作。消息分发框架的基本结构和组件之间的关系如下图所示:

图2-2 消息驱动框架的基本结构

        图中的粗箭头线表明了消息的大致传递路径和流向,在传递的过程中调用消息解析器来完成消息解析。在不同的阶段将使用不同的解析器,消息被送到分发线程的消息队列之前使用半结构化消息解析器,而在把半结构化消息投递到具体的消息处理器之前使用全结构化消息解析器

        从TCP/IP的角度讲,用户业务消息就是一个格式化的字节流或者字符流,具体格式是由用户在TCP/IP之上定义的业务消息协议决定的。我们把仅仅解析完消息头的消息称为半结构化消息,而消息体全部解析完后就成为全结构化消息,相应的解析器分别称为半结构化消息解析器全结构化消息解析器。很显然,由于具有固定的消息头内容,半结构化消息解析器可以由框架层负责实现,而全结构化消息解析器是针对每一个具体的业务消息来开发的,所以交给应用层开发人员来实现。实际上应用层开发人员只需在半结构化消息的基础上完成消息体的解析即可。具体参见本章2.5节和2.9节。

        为什么要区分半结构化消息和全结构化消息呢?为什么要分两个阶段来解析呢?这是因为,框架层的消息统一分发的一个基础就是业务消息必须具有固定内容的消息头,但是消息体不能也不应该由框架层来定义,更不能假设其格式和内容,而必须由用户根据应用层业务逻辑的需要来定义。那么框架就可以负责把消息头解析出来,而把消息体的解析留给应用层开发人员来完成,因为只有应用层了解业务消息体的具体内容和结构。

        这里涉及的另一个概念是通信单元ID。由于我们在第3章还会详细介绍通信层组件的设计和实现细节,所以这里先简单介绍一下通信单元的概念,以便读者先有个基本的了解。在通信层中我们会使用“通信单元”这样一个概念和数据结构来管理客户端与服务器之间的一对连接关系,很显然在服务器端将会有若干对这样的连接关系,因此服务器端也会管理很多个通信单元,但是客户端一般来说只需要一个通信单元,而套接字(Socket)就隐藏在每个通信单元内部。这样的话,服务器要想向某个客户端发送消息,只需将消息发向对应的通信单元即可。通信单元ID就是用来唯一标识通信单元的,因此在服务器端也就用来唯一标识每个客户端,不过在客户端可以忽略它,因为客户端只有一个通信单元。所以,给服务器端接口NetworkObserver的函数传入的通信单元ID表明该事件或消息来自哪个客户端对应的通信单元(或者其Socket)。

        从上图可以看出,框架层将负责实现接口NetworkObserver,并在初始化时将实现对象注册到通信层中,此后就可以收到通信层上报的事件。但是实际上Observer对象是由通信层调用CreateObserver()来创建并注册的,而CreateObserver()函数由框架层来实现。这样做的好处是可以在框架层中替换NetworkObserver的实现,而不需要修改通信层的代码。关于接口NetworkObserver的实现,请参考本章2.4节。

        该框架中另外一个核心组件就是消息分发线程,它拥有半结构化消息队列和消息处理器列表(上图中并没有直接反映出它们之间的包含关系),因此它启动后就可以自动地向各个消息处理器分发消息。每个消息处理器都包含一个消息映射表(实际上是每个消息处理器类拥有一个消息映射表),并且在消息映射表中注册了与该消息对应的全结构化消息解析器和处理函数,所以消息分发线程可以在消息被送往具体的消息处理函数之前调用该解析器,以完成消息的完全解析。具体参见本章2.6节和2.9节。

        图中的空箭头线表示具体实现组件与接口之间的继承关系。可以看出,框架层将向应用层暴露两个接口MessageProcessorFullStructuredMessageParser,应用层应从这两个接口分别派生具体的消息处理器实现类以及针对每个业务消息的全结构化消息解析器实现类。如果需要,应用层还可以从HalfStructuredMessageBuilderHalfStructuredMessageParser分别派生自己的半结构化消息封装器类和半结构化消息解析器类,但是框架层已经提供了对XML消息格式和二进制消息格式的支持,所以基本能够满足一般应用需求。关于如何支持其他业务消息格式,请参考本章2.18节。关于消息处理器接口及其实现,请参考本章2.7节和2.11节。

        关于智能指针,实际上在作者所著的《高质量程序设计指南——C++/C语言》(第三版)一书中已经介绍过了,并且在本框架中仍将大量使用它,所以将它再次单列在本章2.16节。如果读者急需了解其实现原理和细节,可直接跳过去阅读。

抱歉!评论已关闭.