发博文
正文 字体大小:

[连载]基于消息驱动的面向对象通用C/S应用框架(13)

(2009-01-19 13:20:18)
标签:

c/s应用框架

消息驱动

面向对象

cplusplus

c/s

xml

it

分类: 应用框架

2.8 半结构化消息封装器接口及其实现

        显然,对于框架层来说,它不可能预测用户的业务消息内容,因此无法提供一个转换工具,使得用户能够很方便地将一个完全结构化的业务消息转换为半结构化消息甚至直接转换为文本串消息或者二进制字节流消息。此外,所有消息都应看做业务层消息,而且何时发送以及发送什么消息完全由应用层业务逻辑决定,框架层不会发送任何消息。但是框架层可以提供将半结构化消息转换为原始消息(XML文本消息或二进制字节流消息)的工具,即半结构化消息封装器(与半结构化消息解析器对应)。虽然框架不会调用半结构化消息封装器,但是应用层需要调用它。

        正常的消息转换顺序如下图所示:

[连载]基于消息驱动的面向对象通用C/S应用框架(13)

 

图2-9 业务消息转换顺序

        当然,将半结构化消息转换为原始消息的前提是,用户在这之前已经填充好半结构化消息的消息头内容(当然这很容易),并且按照业务消息格式构造好了半结构化消息的消息体(有一定难度,但是框架层无法帮用户做这件事儿J),因为半结构化消息封装器将简单地拷贝消息体。

        半结构化消息封装器接口定义如下:

 

class HalfStructuredMessageBuilder

{

public:

    virtual void Build(HalfStructuredMessageSmartPtr psmsg,

                       ServiceLayerMessage& strMsg ) = 0;

};

 

        同样,XMLMessageBuilder和BinMessageBuilder继承自HalfStructuredMessageBuilder

[连载]基于消息驱动的面向对象通用C/S应用框架(13)

 

图2-10 半结构化消息封装器类层次结构

        显然,在一个系统中二者也是只能择其一。

2.8.1 XML消息封装器

        XMLMessageBuilder定义如下:

 

class XMLMessageBuilder : public HalfStructuredMessageBuilder

{

    DECLARE_STATIC_SINGLETON(XMLMessageBuilder)

public:

    virtual void Build(HalfStructuredMessageSmartPtr psmsg,

                       ServiceLayerMessage& strMsg);

private:

    XMLDOMSerializer   m_serializer;

};

#ifdef _USE_XML_MESSAGE_FORMAT_

typedef XMLMessageBuilder   MessageBuilderFactory;

#endif  // _USE_XML_MESSAGE_FORMAT_

 

        其实现如下所示:

 

void XMLMessageBuilder::Build(HalfStructuredMessageSmartPtr psmsg,

                              ServiceLayerMessage& strMsg)

{

    if (psmsg == NULL || psmsg->m_msgHeader == NULL)

        return;

    strMsg.erase(strMsg.begin(), strMsg.end());

 

    // 创建空的DOM树,树根为元素<msg>

    DOMDocumentPair docPair = \

        XMLDOMDocumentCreator::createDOMDocument( \

              XMLRootElement_msg::name());

    if (docPair.first == NULL || docPair.second == NULL)

        return;

    XMLElement *pRootElem = docPair.second;  // 指向根元素<msg>

    // 检查消息类型

    MessageType curMsgType(psmsg->m_msgHeader->GetMessageType());

    if (curMsgType != MESSAGE_TYPE_REQUEST &&

        curMsgType != MESSAGE_TYPE_ACK &&

        curMsgType != MESSAGE_TYPE_NOTIFY &&

        curMsgType != MESSAGE_TYPE_BROADCAST)

            return;

    //  向DOM树根添加子元素<Header>

    XMLElement *pHeaderElement = \

        pRootElem->append_element(XMLSubElement_Header::name());

    if (pHeaderElement == NULL) return;

    //  添加消息头的内容

    XMLLeaf *pLeaf = NULL;

    pLeaf = pHeaderElement->append_leaf(XMLSubElement_type::name(), \

            curMsgType.c_str());

    if (pLeaf == NULL) return;

    pLeaf = pHeaderElement->append_leaf(XMLSubElement_source::name(), \

            Convert(psmsg->m_msgHeader->GetSource()).c_str());

    if (pLeaf == NULL) return;

    pLeaf = pHeaderElement->append_leaf(XMLSubElement_dest::name(), \

            Convert(psmsg->m_msgHeader->GetDestination()).c_str());

    if (pLeaf == NULL) return;

    pLeaf = pHeaderElement->append_leaf(XMLSubElement_sn::name(), \

            Convert(psmsg->m_msgHeader->GetSn()).c_str());

    if (pLeaf == NULL) return;

    pLeaf = pHeaderElement->append_leaf(XMLSubElement_id::name(), \

            psmsg->m_msgHeader->GetMessageID().c_str());

    if (pLeaf == NULL) return;

    // 导入消息体(DOM子树)

    if (!(psmsg->m_msgBody.IsEmpty()))

        {

        XERCES_CPP_NAMESPACE::DOMNode *pNode = \

            pRootElem->import_node(psmsg->m_msgBody.GetDummyRootElement());

        if (pNode == NULL) return;

        }

    // 将整棵DOM树串化

    m_serializer.convert_to_string(pRootElem, strMsg);

}

 

        例如有一个客户端(ID=25)要发送一个XML通知消息(ID="Hello")给另一个客户端(ID=63),其消息体包含一个元素<LikeThis>,内容为一串文字。则可按照如下方法来生成该消息:

 

    HalfStructuredMessageSmartPtr pNotifyMsg(new XMLHalfStructuredMessage());

    MessageHeaderSmartPtr pMsgHeader(new MessageHeader);

    pMsgHeader->SetMessageType(MESSAGE_TYPE_NOTIFY);  // 设置消息类型

    pMsgHeader->SetSource(25);                        // 设置源端ID

    pMsgHeader->SetDestination(63);                   // 设置目的端ID

    pMsgHeader->SetSn(0);                             // 设置消息序列号

    pMsgHeader->SetMessageID("Hello");                // 设置消息ID

    pNotifyMsg->m_msgHeader = pMsgHeader;

    pNotifyMsg->m_msgBody.AppendLeafAsRoot("LikeThis", \

           "How are you! or How do you do!");         // 构造消息体

    ServiceLayerMessage strMsg;

    HalfStructuredMessageBuilder *pMsgBuilder = \

           MessageBuilderFactory::get_instance();

    pMsgBuilder->Build(pNotifyMsg, strMsg);

 

        然后就可以调用通信层提供的API将消息发送给中央服务器,中央服务器再转发给ID为63的客户端。如果是二进制消息,那么只有创建的半结构化消息对象类型、消息体的封装方法以及使用的半结构化消息封装器对象不同而已。

2.8.2 二进制消息封装器

        BinMessageBuilder定义如下:

 

class BinMessageBuilder : public HalfStructuredMessageBuilder

{

    DECLARE_STATIC_SINGLETON(BinMessageBuilder)

public:

    virtual void Build(HalfStructuredMessageSmartPtr psmsg,

                       ServiceLayerMessage& appMsg);

};

#ifdef _USE_BINARY_MESSAGE_FORMAT_

typedef BinMessageBuilder   MessageBuilderFactory;

#endif  // _USE_BINARY_MESSAGE_FORMAT_

 

        其实现如下所示(请参考图2-3所示的格式定义):

 

void BinMessageBuilder::Build(HalfStructuredMessageSmartPtr psmsg,

                              ServiceLayerMessage& appMsg)

{

    if (psmsg  == NULL || psmsg->m_msgHeader == NULL)

        return;

    appMsg.clear();

    appMsg.reserve(15 + psmsg->m_msgBody.size());

    // 拼接消息类型字段

    appMsg.push_back((BYTE)psmsg->m_msgHeader->GetMessageType());

    // 拼接消息源ID字段

    u_long source_in_netwk = \

    ::htonl((u_long)psmsg->m_msgHeader->GetSource());

    BYTE *pSource = (BYTE*)&source_in_netwk;

    appMsg.insert(appMsg.end(), pSource, pSource+4);

    // 拼接消息宿ID字段

    u_long dest_in_netwk = \

    ::htonl((u_long)psmsg->m_msgHeader->GetDestination());

    BYTE *pDest = (BYTE*)&dest_in_netwk;

    appMsg.insert(appMsg.end(), pDest, pDest+4);

    // 拼接序列号字段

    u_short sn_in_netwk = \

    ::htons((u_short)psmsg->m_msgHeader->GetSn());

    BYTE *pSn = (BYTE*)&sn_in_netwk;

    appMsg.insert(appMsg.end(), pSn, pSn+2);

    // 拼接消息ID字段

    u_long msgid_in_netwk = \

    ::htonl((u_long)psmsg->m_msgHeader->GetMessageID());

    BYTE *pMsgId = (BYTE*)&msgid_in_netwk;

    appMsg.insert(appMsg.end(), pMsgId, pMsgId+4);

    // 拷贝消息体

    appMsg.insert(appMsg.end(), psmsg->m_msgBody.begin(), \

              psmsg->m_msgBody.end());

}

        使用方法与XMLMessageBuilder类似,不再举例。

 

阅读 评论 收藏 转载 打印举报
已投稿到:
  • 评论加载中,请稍候...

       

    验证码: 请点击后输入验证码 收听验证码

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 不良信息反馈 电话:4006900000 提示音后按1键(按当地市话标准计费) 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有