一、前言
在上一章中已经介绍了协议设计和封包设计。那么怎样把这些设计优美的落实为代码的形式呢。使用几个函数就可以实现,但是不够优美和实用,因此本章主要介绍一个协议层构架。使用构架的优点如下:
- 所有协议命令整合到一个类中方便管理和修改
- 构架可以方便协议的扩展
- 构架实现了协议层和逻辑层的解耦
- 构架实现了通信层和协议层的解耦
- 构架实现了通信线程和UI线程的分割
二、通信构架的需求分析(框架具有的功能)
前言只是简单介绍了构架的优点,这根据编程中遇到的实际问题来列举出对协议框架的需求。需求如下:
- 完成基本数据通信,即能发送数据,能接收数据
- 要求可以确认数据是否被下位机正确收到,即数据收到验证机制(依靠协议设计)
- 要求有失败重传机制,多次重传仍然失败报错机制(一般重试3次)
- 要求支持通信超时检测和通信断开检测机制
- 要求与UI逻辑层使用不同线程,要求不能占用UI线程来发送数据,接收数据也不用UI线程
- 要求和通信层解耦,从而保证构架支持多种通信协议,比如,串口,网络UDP,网络Tcp,蓝牙等
- 要求支持下位机主动反馈机制(依靠协议设计支持,定义下位机主动给上位机发送指令的指令类型)
- 要求系统的鲁棒性高,即基础数据层出现数据意外丢失一部分的情况下,不会造成协议处理构架崩溃,而是能检测出来这个意外,把不完整的数据包丢掉的功能。
- 要求有报错回调函数,在解析封包的时候,遇到任何问题都可以通过回调函数反馈给逻辑层
三、对一般通信过程简单描述
一般上位机与下位机通信协议示意图:
四、正式开始构架设计
- 框架示意图:
- 框架中类的的简单功能说明
-
CRC
用CRC校验的类,包括生成RCR码和校验CRC码。 -
Command
用于储存协议定制的命令,以及完成封包的组装。整个软件用到的命令都储存在这个类中形成独特的命令层。本类以函数的形式向对功能逻辑层提供调用接口。方便后期随时对命令做出修改,并能保证不影响现有的功能逻辑代码。 -
Worker
“工人类”也可以叫做“任务类”,主要用于储存待发送的命令。类中包括待发送的实际16进制格式指令、命令重发次数、和当前指令的反馈指令识别函数。 -
WorkerManager
主要由Worker为子项的List列表组成。对外提供对list列表操作的一些函数。主要功能是用于管理待发送指令的的列表。此设计的主要目的是实现UI线程和发送线程的分割。避免使用UI线程直接调用通信层的发送函数发送指令而导致UI线程的卡顿。 -
ProtocolManager
此类以单实例模式设计。是实现协议框架的主要处理类。此类中集合了通信层的实例、一个线程用于循环从待发送指令列表里提取指令并发送、一个定时器用于判断指令发送以及等待反馈指令是否超时、完成对接收封包的拆解和校验工作。
- 框架中使用到的类的成员函数介绍
- CRC:
void GetCRC(quint8 *data, int len, quint16 &crc);//生成一个16位的CRC码
bool CheckCRC(quint8 *data, int len);//校验一个CRC码 - Command:
//给指令数据计算CRC并加入包头和包尾,完成指令的封包
QByteArray* Pack(QByteArray &data);
//一条指令封包的实例
inline static QByteArray* GetTemperature() {
QByteArray data;
data.append(0x01); //指令类型码
data.append(0x01); //指令码
return Pack(data);
} - Worker:
int failed_times; //用于记录此条指令发送失败的重试次数
QByteArray data;//用于记录指令封包的数据
bool CheckRes