铅笔

在你的害怕中坚持的越多,你就会越自信
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

文本协议篇2- 从字节流装配文本协议对象

Posted on 2020-02-06 14:43  黑色の铅笔  阅读(303)  评论(0编辑  收藏  举报

1.如何将接收缓冲区中的数据装配成协议对象

  • 上一节中提到的unserialize函数用于将一个符合协议规则的字符串装配成为一个协议对象。可是在实际工程开发过程中可能会是一个问题:接收缓冲区中的数据一定是满足协议的定义规则吗? 
    答案是:不一定

2.如果接收缓冲区中的数据不满足协议规则能否将其装配成一个协议对象

  • 接收缓冲区中的数据足够
    • 如果数据量足够是否能够装配成不只是一个对象?
    • 剩下的数据如何处理(属于下一个协议对象)?
  • 数据量不足
    • 是否达到协议最小长度(8字节)?
    • 如何处理数据量超过最小长度但不足以产生一个新对象

3.解决方案

  • 定义一个类用于接收字节流数据,并装配协议对象
  • 类中提供容器(队列)用于暂存字节流数据
  • 当容器中至少存在8个字节时开始装配
    1. 首先装配协议中的类型(type)和数据区长度(length)
    2. 根据数据区长度从容器中取数据装配协议数据(data)
    3. 当协议数据装配完成后,创建协议对象并返回,否则,返回NULL

4.代码

目的:实现将字符串装配成一个完整的协议对象。 
main函数测试目标 将完整的协议字符串分两次写到内存中,通过 
TxtMsgAssembler类完成对协议的装配 
代码: 
txtmsgassembler.h

 1 #ifndef TXTMSGASSEMBLER_H
 2 #define TXTMSGASSEMBLER_H
 3 
 4 #include <QObject>
 5 #include <QQueue>
 6 #include <QSharedPointer>
 7 #include "TextMessage.h"
 8 
 9 class TxtMsgAssembler : public QObject
10 {
11     Q_OBJECT
12     QQueue<char> m_queue;//存储字节流
13     QString m_type;
14     int m_length;
15     QString m_data;
16 
17 
18 public:
19     explicit TxtMsgAssembler(QObject *parent = nullptr);
20 
21     QSharedPointer<TextMessage> assemble();//从内部容器中获取字节数据,尝试装配字节对象
22     QSharedPointer<TextMessage> assemble(const char* data,int length);
23     void prepare(const char* data, int len);//将数据转存到内部容器,用于后续协议对象装配
24     void reset();
25     void clear();
26     bool makeTypeAndLength();
27 
28     TextMessage*  makeMessage();
29     QString fetch(int n);
30 signals:
31 
32 public slots:
33 };
34 
35 #endif // TXTMSGASSEMBLER_H

txtmsgassembler.cpp

  1 #include "txtmsgassembler.h"
  2 
  3 TxtMsgAssembler::TxtMsgAssembler(QObject *parent) : QObject(parent)
  4 {
  5 
  6 }
  7 
  8 QSharedPointer<TextMessage> TxtMsgAssembler::assemble(const char *data, int length)
  9 {
 10     prepare(data,length);
 11     return assemble();
 12 }
 13 
 14 QSharedPointer<TextMessage> TxtMsgAssembler::assemble()
 15 {
 16     TextMessage* ret=NULL;
 17 
 18     bool tryMakeMsg=false;
 19     if(m_type=="")
 20     {
 21        tryMakeMsg=makeTypeAndLength();
 22     }
 23     else
 24     {
 25         tryMakeMsg=true;
 26     }
 27 
 28    if(tryMakeMsg)
 29    {
 30        ret = makeMessage();
 31     }
 32 
 33    if(ret!=NULL)
 34     {
 35         clear();
 36     }
 37 
 38     return QSharedPointer<TextMessage>(ret);
 39 }
 40 
 41 void TxtMsgAssembler::prepare(const char* data, int len)
 42 {
 43     if(data!=NULL)
 44     {
 45         for(int i=0;i<len;i++)
 46         {
 47             m_queue.enqueue(data[i]);
 48         }
 49     }
 50 }
 51 
 52 void TxtMsgAssembler::reset()
 53 {
 54     clear();
 55     m_queue.clear();
 56 }
 57 
 58 void TxtMsgAssembler::clear()
 59 {
 60     m_type="";
 61     m_length=0;
 62     m_data="";
 63 }
 64 
 65 bool TxtMsgAssembler::makeTypeAndLength()
 66 {
 67     bool ret=(m_queue.length()>=8);
 68 
 69     if(ret)
 70     {
 71         m_type=fetch(4);
 72         QString len =fetch(4).trimmed();
 73         m_length=len.toInt(&ret,16);
 74 
 75         if(!ret)
 76        {
 77             clear();
 78         }
 79     }
 80     return ret;
 81 }
 82 
 83 
 84 
 85 
 86 TextMessage* TxtMsgAssembler::makeMessage()
 87 {
 88     TextMessage* ret=NULL;
 89 
 90     if(m_type!="")
 91     {
 92         int needlen=m_length-m_data.length();
 93 
 94         int n=(needlen<=m_queue.length())?needlen:m_queue.length();
 95         m_data+=fetch(n);
 96 
 97         if(m_length==m_data.length())
 98         {
 99             ret=new  TextMessage(m_type,m_data);
100         }
101 
102     }
103     return ret;
104 }
105 
106 QString TxtMsgAssembler::fetch(int n)
107 {
108     QString ret;
109 
110     for(int i=0;i<n;++i)
111     {
112         ret+=m_queue.dequeue();
113     }
114 
115     return ret;
116 }

main.cpp

 1 #include <QCoreApplication>
 2 #include <QDebug>
 3 #include "textmessage.h"
 4 #include "txtmsgassembler.h"
 5 
 6 int main(int argc, char *argv[])
 7 {
 8     QCoreApplication a(argc, argv);
 9 
10     TextMessage tm("AB","1234567890");
11     QString message=tm.serialize();
12     qDebug()<<message;
13 
14     QString s1=message.mid(0,5);
15     QString s2=message.mid(5);
16 
17     QSharedPointer<TextMessage> tmt;
18     TxtMsgAssembler as;
19 
20     tmt=as.assemble(s1.toStdString().c_str(),s1.length());
21     if(tmt!=NULL)
22     {
23         qDebug()<<"assembel sucessed.";
24         qDebug()<<tmt->type();
25         qDebug()<<tmt->length();
26         qDebug()<<tmt->data();
27     }
28 
29     tmt=as.assemble(s2.toStdString().c_str(),s2.length());
30     if(tmt!=NULL)
31     {
32         qDebug()<<"assembel sucessed.";
33         qDebug()<<tmt->type();
34         qDebug()<<tmt->length();
35         qDebug()<<tmt->data();
36     }
37     return a.exec();
38 }

说明:textmessage.h参考上一节同名文件

效果:

 

 

 

5.小结

  • 从连续字节流装配协议对象是应用自定义协议的基础
  • 装配类TxtMsgAssembler用于解析自定义协议
  • 装配类的实现的关键是如何处理字节数据不够的情况
  • 自定义协议类和装配类能够有效的解决数据粘粘的问题