3.2-上位机与下位机的“私有协议”通信构架设计
一、前言
在上一章中已经介绍了协议设计和封包设计。那么怎样把这些设计优美的落实为代码的形式呢。使用几个函数就可以实现,但是不够优美和实用,因此本章主要介绍一个协议层构架。使用构架的优点如下:
- 所有协议命令整合到一个类中方便管理和修改
- 构架可以方便协议的扩展
- 构架实现了协议层和逻辑层的解耦
- 构架实现了通信层和协议层的解耦
- 构架实现了通信线程和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 CheckResponse(quint8 * return_pkg);//用于检测本指令对应的“反馈指令”是否和本指令匹配 - WorkerManager:
QList<Work*> work_list_;//用于记录待发送指令的列表
void AddWork(QByteArray &pkg);//给列表增加一条待发送指令
void CleanWorkList();//清空指令列表
void FinishAWork();//删除列表首位的指令,表示这条指令发送完成
Work* GetCurrentWork();//获取列表首位的那一条指令
bool IsHasWork();//查询列表中是否有指令待发 - ProtocolManager:
通信层对象,用于收发数据
Thread用于从发送队列获取待发送数据并发送
Timer 用于检测发送是否超时
通信层接收数据回调函数 {
从接收的数组中分离出一个完整的数据包
拆掉包头,包尾,并验证CRC是否正确
验证一条命了处理完成
如果收到的反馈包中含有数据,反馈数据到UI层
接收下位机主动反馈数据
}
五、框架对于流式数据的处理说明
前一章提到了,像UDP通信这样的有消息边界的通信方式,一个UDP封包中可以只包含一个自定义协议封包,因此极大的方便了编程,并且可以稳定使用。如果是像TCP或串口这样的无消息边界的通信方式中,想要实现自定义通信封包则相对复杂。因为会出现多个自定义封包的的“粘包”的现象。如下图所示:
首先要说明本框架是支持,无消息边界情况下的自定义数据封包传输。下面结合代码详细说明一下本框架在这方面的设计思想。
从上图可以得出,如果框架可以处理“无消息边界的通信方式”,那么一定可以处理“有消息边界的通信方式”,反之则不行。也就可以理解为“有消息边界通信方式”是”无消息边界通信方式“的子集。因此,在框架设计中,只要处理好”无消息边界的通信方式“,就可以应对所有通信方式了。以下为本框架识别并拆分“自定义协议封包”的代码:
//通信接收数据回调函数
void ProtocolManager::onRecv(QByteArray *data, QString ip, quint16 port) {//把接收到的数据,依单个byte处理,并不区分有没有消息边界,都以“流式通信”处理。//(前提是,无论通信协议是否有消息边界,都使用使用自定义协议)for(int a = 0; a < data->length(); a ++) {Process(static_cast<quint8>(data->at(a)));}
}
void ProtocolManager::Process(quint8 b) {if (package_on_ == true) //如果找到包头则开始此封包的后续识别{if (pos > CACHE_LENGTH) {return;}cache[pos++] = b;//识别包尾if (pos > 4 &&cache[pos - 4] == tail[0] && //a pkg is finishedcache[pos - 3] == tail[1] &&cache[pos - 2] == tail[2] &&cache[pos - 1] == tail[3]) {package_on_ = false;quint8* pkg = &cache[1];int pkg_len = pos - 5;//进行CRC校验if (Crc::CheckCRC(pkg, pkg_len) == true) {ProcessMetaPkg(pkg, pkg_len-2);//-2 : remove crc byte} else {CleanWorkList();emit onConnectionError("crc check error");}}//识别包头} else if (package_on_ == false && b == head[0]) { //start a pkgpackage_on_ = true;pos = 0;cache[pos++] = b;}
}
//此函数用于处理封包中的内容,根据不同指令做不同解析,详见源代码
void ProtocolManager::ProcessMetaPkg(quint8 *pkg, int len) {}
3.2-上位机与下位机的“私有协议”通信构架设计相关推荐
- 上下位机通讯协议_上位机与下位机的区别通讯
上位机是指可以直接发出操控命令的计算机,一般是PC/host computer/master computer/upper computer,屏幕上显示各种信号变化(液压,水位,温度等).下位机是直接 ...
- 上位机和下位机的概念,理解如何实现PC从PLC中读取数据?
市面上的PLC有上百种, 西门子的, 三菱的, 欧姆龙的等等. 上位机和下位机的理解: 上位机是指可以直接发出操控命令的计算机,一般是PC/host computer/master computer/ ...
- 打开单片机世界的大门——上位机控制下位机实例详解
上位机控制下位机实例详解 一.基本概念 上位机与下位机 串口 数据表达 二.下位机程序 三.上位机程序 四.总结 一.基本概念 在开始讲解前,先来看几个基本概念,如果是有基础的大佬,请直接跳到下一节. ...
- 超详细Klipper 上位机与下位机配置
(适用多数Mega2560芯片打印机主板,本文使用香橙派ZERO2作为上位机) 上位机:ZERO2 下位机:打印机主板 下载镜像系统 首先,去Armbian官网下载Buster系统镜像:Armbian ...
- 计算机基础-工控机、上位机、下位机、stm32、单片机
工控机 定义:(Industrial Personal Computer,IPC)即工业控制计算机,主要用于工业生产上. 性能:采用全钢机箱,抗震性能好,抗电磁干扰,抗冲击. 结构:包括CPU.io外 ...
- 什么是上位机、下位机
上位机 上位机是指可以直接发出操控命令的计算机, 一般是PC/host computer/master computer/upper computer, 屏幕上显示各种信号变化(液压,水位,温度等). ...
- java实现上位机与下位机串口通信
串口通信是在工程应用中很常见.在上位机与下位机通讯过程中常通过有线的串口进行通信,在低速传输模式下串口通信得到广泛使用.在说个之前先来简单解释一下上位机与下位机的概念. 上位机与下位机 通常上位机指的 ...
- QT5实现串口收发数据(上位机与下位机通信)
最近帮老师做一个应用程序,通过上位机与下位机进行串口通信,最后实现实时绘图,通过几天努力,成功实现蓝牙串口通信. 参考博客1 注意:代码中一些与串口无关代码,可以忽略掉 一.QT5串口基础知识 1. ...
- 【CNC——第6篇】PMAC上位机编程基础篇(上位机和下位机如何通信)
拓展链接: PAMC官网:DELTA TAU. 官网手册:手册大全 PMAC官网: PCOMM32PRO用户手册 PMAC 的内部变量 内部变量分为四种,I 变量为电机等常用基本控制变量,P 变量为全 ...
最新文章
- PicGo github配置
- HMM算例 python 有代码
- 一款炫酷Loading动画--载入成功
- SAP Fiori里的Adapt UI按钮,神出鬼没的奥秘
- 3ds Max 2018 在安装后无法启动或出现不稳定
- CSU-1982 小M的移动硬盘
- 第十一届蓝桥杯省赛C++组试题 第5题
- 指针与数组的关系---初始化
- pytorch---模型加载与保存(5)使用在不同模型参数下的热启动模式
- C# 调用系统API 内核 简单样例
- qq象棋辅助 android,QQ象棋自动下棋
- qqxml代码-班级作业xml卡片代码班级作业
- popwindow 加个边框_PopupWindow仿微信浮层弹出框效果
- python 接入百度地图数据包下载_Python爬虫-利用百度地图API接口爬取数据并保存至MySQL数据库...
- centos7 安装/卸载wps 无法启动 字体缺失(亲测有效)
- ERD Online 4.0.5 在线数据库建模、元数据管理(免费、私有部署)
- mysql查询是第几条记录_MySQL查询第几行到第几行记录
- 更改SQL Server数据库名、数据库文件名的方法
- 计算机网络(各章节精华版)
- 注塑成型(注射模塑成型)
热门文章
- 网易im 服务器消息格式,机器人消息体模板说明-IM即时通讯-网易云信开发文档...
- Python fractions模块 —— 分数相关函数
- 快牛智能凉经(数据挖掘日常实习)
- 算法---逆向旋转矩阵法求解矩阵绕圈走
- 高德地图上画图!和3D绘制区域
- 基于STM32设计物联网在线智能称重系统(OneNet)_2022
- 【Java编程练习】司机肇事后逃跑,现场三人半瞎系列
- HOS Develop Notes-开启ssh服务
- LeetCode第9题 回文数(Palindrome Number)
- 什么样的公司才需要办理ICP经营许可证?