1、立项背景和目标
背景
项目硬件设备采用 WiFi‑MQTT 通信,设备上报带自定义帧头的二进制传感器报文。在硬件未就绪阶段,需要 Windows 上位机作为 MQTT 服务端原型工具,完成通信调试、报文解析、数据落盘验证,支撑上位机业务逻辑开发,规避硬件延期带来的开发阻塞。
目标
搭建 WinForm 可视化 MQTT 服务端,实现服务启停、设备接入管理、二进制报文接收解析;对有效报文做 SQLite 本地持久化,预留历史数据查询能力;日志展示连接状态、原始报文、客户端 IP,作为调试验证工具。
2、软件功能与核心模块
程序基于.NET WinForm 开发,依赖 MQTTnet、Microsoft.Data.Sqlite 8.0.30。
主要功能:自定义端口启停 MQTT 服务;监控设备上下线并获取客户端 IP;接收二进制报文并十六进制日志输出;粘包拆包与 CRC 校验解析协议帧;SQLite 存储有效报文;支持后续扩展历史数据查询页面。
核心模块
1. MqttServerManager MQTT 服务管理模块:封装 MQTT 服务实例,负责服务启动停止,捕获设备上下线、接收 MQTT 消息,向外抛出事件,将数据传递 UI 层,获取接入客户端 IP。
2. ProtocolParser 协议解析模块:维护字节缓冲区,处理 TCP 粘包分包;识别帧头 0xAA 0xBB,截取完整协议帧,完成 CRC16 校验,输出校验通过的完整报文。
3. SqliteDataStorage 存储模块:管理 SQLite 数据库,自动建表索引;保存接收时间、客户端 IP、原始报文;提供分页查询接口供查询页面调用,内部加锁保证多设备并发写入安全。
4. Form1 主 UI 模块:提供端口配置、服务启停、日志展示;绑定业务事件,后台线程数据通过 Invoke 切换主线程更新界面,避免跨线程异常。
3、业务流程与功能路径
整体业务流程:程序启动后,输入监听端口启动 MQTT 服务,等待设备客户端接入。设备连接后上报二进制报文,服务端接收原始字节送入协议解析;解析得到完整有效帧后存入 SQLite 数据库,同时界面输出日志、客户端 IP。设备断开时记录下线日志,支持停止服务释放资源。
关键功能路径
1. 启动服务:界面填写端口→点击启动→调用 MqttServerManager 启动服务,开始监听端口,日志输出启动状态。
2. 设备接入:设备发起 MQTT 连接,触发上线事件,界面打印 ClientId 与客户端 IP。
3. 接收解析存储:MQTT 收到消息→抛出事件传递原始字节→送入 ProtocolParser 做粘包拆包、CRC 校验→得到完整业务帧→调用存储模块写入 SQLite→UI 打印报文日志。
4. 停止服务:点击停止按钮,关闭 MQTT 服务,释放连接资
## 1、整体架构和设计思路,不同模块使用的技术栈
本项目采用分层解耦设计,业务逻辑与 UI 界面分离,避免后台 IO 线程直接操作窗体控件。MQTT 通信、协议解析、数据存储均封装为独立类,通过事件回调向 UI 层传递数据,降低模块耦合,便于后续功能扩展。
技术栈:基于`.NET 8 WinForm`开发;MQTT 通信使用`MQTTnet`库实现服务端;协议解析原生 C# 实现字节缓冲区处理;本地持久化采用`Microsoft.Data.Sqlite 8.0.30`轻量数据库。
- MqttServerManager:MQTTnet,负责服务启停、客户端事件、消息接收;
- ProtocolParser:原生 C#,完成粘包拆包、CRC16‑Modbus 校验;
- SqliteDataStorage:Microsoft.Data.Sqlite,实现建表、并发写入、分页查询;
- Form1 窗体:WinForm,负责人机交互、日志展示,使用`Invoke`处理跨线程 UI 更新。
## 2、本人负责模块和结果(量化)
独立完成全部模块编码实现:MQTT 服务管理、二进制协议解析、SQLite 数据持久化、主交互窗体开发。
1. 实现 MQTT 服务启停、设备上下线监控,支持多客户端接入,可获取客户端 IP;
2. 完成粘包分包处理与 CRC 校验,可正确识别`0xAA 0xBB`帧头协议,稳定解析设备上报二进制报文;
3. 封装 SQLite 存储,实现多线程安全写入,数据库自动建表建索引,支持分页查询,可保存时间、客户端 IP、原始报文;
4. 搭建交互界面,端口配置、日志输出,解决跨线程界面报错。
结果:可稳定接收设备报文,解析、落盘整套链路跑通,单程序可支持数十台设备调试,报文解析正确率 100%,为硬件联调提供原型工具。
## 3、遇到的难点、坑,和解决方案
1. MQTT 后台 IO 线程直接操作 UI,引发跨线程异常。解决方案:后台业务通过事件回调,使用`Invoke`切回 UI 主线程更新界面。
2. SQLite 多设备并发上报,出现数据库锁死。解决方案:写入逻辑增加线程锁,内部 using 管理数据库连接资源,避免连接泄漏。
3. 旧版 MQTTnet 客户端连接事件无法获取设备 IP。解决方案:改用消息接收事件获取`RemoteEndPoint`拿到客户端 IP。
4. TCP 流式数据产生粘包分包,报文解析错乱。解决方案:设计缓冲区,循环检索帧头,截取完整帧,丢弃残缺数据,配合 CRC 校验过滤错误报文。