首页 / 资讯中心 / 文章详情

qtmqtt.zip 实战:Qt 接入 MQTT 的构建与避坑指南

qtmqtt.zip 实战:Qt 接入 MQTT 的构建与避坑指南 ★ FEATURED ARTICLE
简介qtmqtt.zip是一份面向Qt开发者的MQTT协议集成库源码包适合需要在应用中接入物联网消息通信的C/Qt工程师。压缩包共含174个文件大小约2.61MB除src与include下的C源码和头文件外还包含qmake工程配置、pro项目文件、examples示例、tests测试用例以及编译好的lib库文件代码组织清晰便于按模块查阅。目前已有634人学习下载。该资源不仅提供完整的MQTT客户端实现还覆盖连接属性、消息属性、遗嘱消息、用户属性等扩展特性并附有topic过滤器、订阅/取消订阅等类的定义。通过配套示例可快速掌握连接服务器、发布订阅主题及处理消息的完整流程。对希望深入理解Qt MQTT内部机制、需要据此二次开发或集成到现有项目的开发者而言这份包含源码、构建配置、库文件与测试的完整包有很高参考价值。1. qtmqtt.zip 是什么Qt 项目接入 MQTT 最直接的入口如果你在找 qtmqtt.zip 这个压缩包大概率已经踩过一次编译环境的坑。它不是一个普通的应用安装包而是 Qt 官方 MQTT 模块的源码分发文件里面装的是 QtMqtt 库的完整源码、示例和工程文件。MQTT 是物联网场景下最常用的消息协议而 QtMqtt 就是让 Qt 程序通过这套协议完成订阅、发布和遗嘱消息等操作的官方实现。你解压后需要自己构建库、配置工程才能把它用起来这也是大量新手拿到包之后卡住的地方。这个包适合谁做物联网上位机、搞智能设备配网调试、写跨平台监控面板的 Qt 开发者都绕不开它。它解决的典型问题是你的设备端用 MQTT 上报数据但桌面端或移动端用 Qt 写界面时找不到一个原生好用的客户端库只能拿 WebSocket 转发或自己拼报文格式代码越写越脏。QtMqtt 帮你把这些协议细节封装成现成的类订阅、发布、掉线重连都有回调接口省掉的往往是一两个星期的手工协议调试时间。这篇文章会按我自己的实操顺序写先讲选型差异再讲构建然后给最小可运行示例最后把编译报错和运行时翻车的常见坑一次性说透。2. 拿到压缩包先别急着解压先分清 QtMqtt 与 qmqtt 的选型差异很多人在搜索 qtmqtt 时其实搜到的是两个完全不同的库一个是 Qt 官方技术预览版模块 QtMqtt另一个是第三方维护的 qmqtt 客户端库。两者名字相似但设计思路、 API 风格、维护状态和使用方式差异很大。如果你在项目里用错了库后期换成本非常高所以这一步值得先停下来看清楚。2.1 QtMqtt 与 qmqtt同一协议下两套 API 风格QtMqtt 是 Qt 官方仓库里的模块类名以 QMqtt 开头比如 QMqttClient、QMqttTopicSubscription、QMqttSubscription。API 风格和 Qt 自身保持一致信号槽的命名规范跟 QNetworkAccessManager 这类类很像如果你长期写 Qt上手几乎没有学习成本。这个模块长期处于技术预览状态但 Qt 5.15 到 Qt 6 的各个版本里都能找到对应分支稳定性在持续改善。qmqtt 则是第三方开发者维护的一套 Qt 封装命名上用的是 QMQTT全大写核心类是 QMQTT::Client风格偏轻量接口比官方模块少一些编译依赖也更简单——直接把它当普通源码加到工程里编就行。它比较适合只需要 publish/subscribe 两个基本动作的小型工具类项目。如果你拿到的是 qtmqtt.zip 且里面带有大量第三方版权声明文件那大概率是这一套。我的建议是新项目一律优先用官方 QtMqtt理由是它在 Qt 6.2 之后加入了 qmake 和 CMake 的官方支持逻辑SSL 配置也整合进 QSslSocket 体系后续升级 Qt 版本时不容易出现接口断层。qmqtt 虽然省事但遇到要上 TLS、遗嘱消息、QoS2 这类扩展场景时它的封装深度不够你依然要自己读协议细节去补洞。2.2 版本匹配Qt 版本与 QtMqtt 源码分支必须对齐这是解压前最容易被忽略的一个点。QtMqtt 不是独立于 Qt 存在的一个包它的构建依赖你本机 Qt 的 qmake 和头文件路径。你可以从仓库里拉取源码然后让它的构建系统对接现有 Qt 环境但这里有一个硬性前提QtMqtt 的版本分支必须和你的 Qt 大版本匹配。比如你的机器装的是 Qt 5.15.2那就应该用 5.15 分支或对应的发布标签如果你装了 Qt 6.5就去切 6.5 分支。混用版本最典型的结果是编译时候报一堆 undefined reference或者 moc 生成的代码和 Qt 头文件版本不匹配。由于 qtmqtt.zip 这种压缩包形式自带一个工程目录快照你收到手的文件不一定来自最新分支先确认包内 .pro 文件或者 CMakeLists.txt 里的版本要求再决定是否继续使用能省下大量排错时间。另外一个差异是构建模式。Qt 5 时代 QtMqtt 可以通过 qmake 一键构建Qt 6 里官方推荐用 CMakeqtbase 通过自带的 qt_add_module 方式管理子模块构建。如果压缩包内只有 .pro 文件这个包大概率是 Qt 5 时代的产物。你可以直接拿它在 Qt 5 环境构建但拿到 Qt 6 环境就要自己创建 CMakeLists.txt 去编译同一批源码并非完全不能用只是要把路径和依赖手工梳理一遍。建议下载前先确认来源分支历史包供参考不必死守。3. 从源码构建 QtMqtt 模块qmake 与 CMake 两条路的完整步骤构建 QtMqtt 模块是绝大多数读者拿到 qtmqtt.zip 之后要做的第一件事。这里整理两条常见路径Qt 5 时代的 qmake 构建流程以及 Qt 6 环境下的 CMake 构建流程。按步骤执行五分钟内能看到编译产物。以下命令都基于解压后目录结构为根目录展开。3.1 先看目录确认压缩包里有哪些文件解压后先别急着敲命令用下面的命令扫一眼顶层目录unzip qtmqtt.zip -d /work/qtmqtt-src cd /work/qtmqtt-src ls -la正常解压后你会看到 src、tests、examples 之类的目录以及项目根级别的 .pro 或 CMakeLists.txt 文件。src 里的 mqtt 子目录存放核心源码examples 里会有一两个简单 demo。先确认 sees 目录下是否有 .pro 文件还是 CMakeLists.txt这决定了你走哪条构建路径。如果只有 .pro说明是经典 qmake 工程适合 Qt 5 环境使用。如果既有 .pro 又有 CMakeLists.txt说明源码版本较新两种构建方式均可。特别提醒不要直接在解压目录里编译后就算完强烈建议建一个单独的构建目录让中间文件与源码分离后续重新构建时不需要清理源码目录。3.2 用 qmake 构建 QtMqttQt 5 路径Qt 5 环境下构建 QtMqtt 模块最常见做法是直接用 qmake 扫描整个工程并安装到 Qt 安装目录cd /work/qtmqtt-src /opt/Qt/5.15.2/gcc_64/bin/qmake make -j8 make install命令解释qmake 会读取根目录下的 .pro 文件自动递归处理 src/mqtt 子工程生成对应 Makefilemake 执行编译链接make install 会把头文件和库文件复制到当前 Qt 安装目录的 include 与 lib 路径下。多数发行包里没有预编译好的 QtMqtt 库必须走这一步让 Qt 环境认识它。为什么建议 make install因为 Qt 的 include 扫描路径默认指向安装目录如果你不安装模块你自己项目里写#include QMqttClient时会直接报找不到文件。make install 之后任何基于这个 Qt 版本的新项目都能直接拉起模块这是最省事的方式。如果你的 Qt 安装在系统目录且当前用户没有写权限记得在 make install 前切到 sudo否则复制文件那一步会失败。3.3 用 CMake 构建 QtMqttQt 6 路径Qt 6 的环境通常就是一套 CMake 生态。这个包如果自带 CMakeLists.txt构建命令和普通 Qt 项目一致mkdir build cd build cmake .. -DCMAKE_PREFIX_PATH/opt/Qt/6.5.0/gcc_64 cmake --build . -j8 cmake --install .这里的-DCMAKE_PREFIX_PATH告诉 CMake 到哪里找 Qt 的配置包非常重要。如果你的机器上装了多个 Qt 版本不指定这个路径CMake 极有可能找到错误的 Qt 版本随后报一堆难以理解的 Qt 6 与 Qt 5 混用错误。构建完成后QtMqtt 的库文件会出现在构建目录的 lib 或 bin 子目录中install 后才会复制到指定前缀。我一般不在系统目录做 install而是 install 到项目私有目录/opt/Qt之外的~/thirdparty/qtmqtt这样每个项目可以自己指向前缀路径避免升级 Qt 时模块版本错乱。CMake 路径找库的逻辑也简单靠find_package(Qt5Mqtt)或find_package(Qt6Mqtt)实现库本身不安装find 不中后续写 CMakeLists 时就会报Qt6Mqtt not found。3.4 构建后验证模块可用的最小命令构建完之后别急着开 IDE先用一条命令验证模块是否注册成功/opt/Qt/6.5.0/gcc_64/bin/qmake -query QT_INSTALL_HEADERS ls /opt/Qt/6.5.0/gcc_64/include/QtMqtt ls /opt/Qt/6.5.0/gcc_64/lib | grep Mqtt如果 include 目录下能看到 QtMqtt 头文件lib 目录下能看到 libQt6Mqtt.so 或相关文件说明模块安装成功。此时你可以开始写自己的第一个连接程序而不是继续反复重新编译这个模块。注意Qt 6 里部分平台还会生成 Qt6Mqtt.dll 或 .dylib运行你自己的程序时如果出现找不到该库的错误记得把库所在路径加入动态链接路径Linux 可以临时用export LD_LIBRARY_PATH/opt/Qt/6.5.0/gcc_64/libmacOS 上则优先用 install_name_tool 修正依赖。跨平台部署时这个问题会在另一台机器上成为主要烦恼。4. 把 MQTT 客户端用起来最小订阅发布示例与关键参数构建是主菜前的小菜真正能让项目跑起来的是把客户端类接入自己的工程。这里给一套可以直接抄走的 CMake 工程结构和 main.cpp覆盖连接、订阅、发布三个高频操作包含每个关键参数的作用与调整方式。你也可以用 qmake 复刻同样的逻辑但 CMake 在 Qt 6 环境下更通用。4.1 最小 CMakeLists.txt工程如何找到 QtMqtt项目根目录里新建一个 CMakeLists.txt核心是把 QtMqtt 组件加入链接cmake_minimum_required(VERSION 3.16) project(qtmqtt_demo LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Qt6 REQUIRED COMPONENTS Core Network Mqtt) qt_standard_project_setup() qt_add_executable(qtmqtt_demo main.cpp) target_link_libraries(qtmqtt_demo PRIVATE Qt6::Core Qt6::Network Qt6::Mqtt)find_package(Qt6 ... Mqtt)是你上一章构建的模块在工程里的入口缺少它代码里 include 的头文件无法解析到库符号。如果用的是 Qt 5把 Qt6 改成 Qt5 即可其它逻辑一致。注意qt_standard_project_setup()不是必须的但它能自动处理 C 标准设置和资源扫描规则省去你手动 set 一堆属性。多人协作的项目里建议统一指定 CMAKE_PREFIX_PATH 指向你安装 QtMqtt 的前缀路径例如/opt/Qt/6.5.0/gcc_64否则别人拉代码后可能因为找不到模块而卡住。4.2 连接、订阅、发布三板斧核心代码与参数说明下面这个例子展示的是最常用的客户端流程连接 broker订阅主题收到消息后打印同时主动向另一个主题发布一条消息。注释写得很细放在哪里都容易懂。#include QCoreApplication #include QMqttClient #include QDebug #include QTimer int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QMqttClient client; client.setHostname(127.0.0.1); client.setPort(1883); client.setClientId(qs_demo_001); client.setUsername(guest); client.setPassword(guest); QObject::connect(client, QMqttClient::stateChanged, [](QMqttClient::ClientState state) { if (state QMqttClient::Connected) { qDebug() connected, subscribing...; auto sub client.subscribe(QMqttTopicFilter(dev/sensor/#), 1); if (sub) { QObject::connect(sub.data(), QMqttSubscription::messageReceived, [](const QMqttMessage msg) { qDebug() topic: msg.topic().filter() payload: msg.payload(); }); } client.publish(QMqttTopicName(dev/cmd/set), hello from qt, 1, false); } else if (state QMqttClient::Disconnected) { qDebug() disconnected; } }); QObject::connect(client, QMqttClient::errorChanged, [](QMqttClient::ClientError err) { qDebug() error: err; }); client.connectToHost(); return app.exec(); }核心参数逐一说明setHostname和setPort指定 broker 的地址与端口。默认 1883 是明文 TCP如果使用 8883 端口则要配合 SSL 配置这在后面的进阶章节会提到。setClientId客户端标识。新老 session 的区分依赖这个 ID不同进程不能共用同一 ID否则 broker 会强制踢掉旧连接。setUsername与setPassword仅当 broker 开启认证时才需要。匿名连接可以直接调setUsername()但部分 broker 配置下会被拒绝。subscribe(QMqttTopicFilter(dev/sensor/#), 1)第二个参数是 QoS 等级0 表示尽力送达1 表示至少一次2 表示恰好一次。传感器上报场景一般选 1避免重复。publish(topic, payload, qos, retain)最后一个 retain 参数表示是否保留消息置 true 后 broker 会为后续订阅者缓存最后一次消息选 false 则不做缓存。这里有一个需要特别指出的问题subscribe返回的 QMqttSubscription 是异步生效的你立刻拿不到任何消息需要在信号回调里等待 broker 的 SUBACK 返回。connect 之后立刻 publish 其实有竞态风险不过大多数本地 broker 响应非常快实测问题不大。如果想严格一点就在QMqttSubscription::stateChanged里等 SubscriptionGranted 事件再发消息。4.3 让客户端带上遗嘱消息掉线时给其他设备一个信号设备端的断线往往意味着故障MQTT 的遗嘱消息机制正好适合把这种状态通知给其他参与者。在 QtMqtt 里设置遗嘱不需要额外打协议补丁直接设置相关属性然后连接client.setWillTopic(QMqttTopicName(dev/status)); client.setWillMessage(offline); client.setWillQoS(1); client.setWillRetain(true); client.connectToHost();遗嘱消息是 broker 在检测到异常断线时替客户端发布的所谓异常断线包括网络断开、进程崩溃、心跳超时。注意它只在连接建立时生效你先设置再连接即可。QoS 设为 1 是想确保这条状态变化至少被一个订阅者收到retain 设为 true 则能让新上线的订阅者立刻看到当前设备是离线状态。设置遗嘱后还有一个必须留意的点最终你在代码里正常disconnectFromHost()关机时broker 不会发布遗嘱这是协议规定的行为差异。因此你需要设计两套通知逻辑正常退出时显式发布一条 online/offline 消息异常断线依赖遗嘱。忽略这个细节会让监控端在设备正常重启时出现短暂误报。4.4 心跳与超时为什么连接会在安静久了之后突然消失实际跑项目时会发现一个现象MQTT 客户端连着没事但隔了十来分钟就莫名断开过几秒又自动恢复。这背后是 broker 两端都在做“静默超时”只是 QtMqtt 默认心跳时间较长你的网络环境里中间设备交换机、路由器、LVS把空闲连接当成无用连接回收了。解决方式是调短 keepalive 间隔client.setKeepAlive(10);单位是秒表示客户端每隔 10 秒向 broker 发送一次 PINGREQ。这个值建议设置为 10 到 30 之间如果设置过长NAT 设备容易半开连接。如果实在不想让客户端额外发心跳包可以启用 TCP 层的 SO_KEEPALIVE但 Qt 没有直接暴露这个选项需要你自己通过 socket 句柄设置。在容器和跨网络环境下显式缩短 keepalive 是性价比最高的方案。5. 常见避坑编译报错、连接失败、消息丢失的排查清单从拿到 qtmqtt.zip 开始到真正实现稳定通信中间有几个高频卡点。我梳理了五条最典型的踩坑记录每条按现象、原因、解决的顺序写基本覆盖大多数人的翻车现场。5.1 编译报错找不到 QMqttClient 头文件现象自己工程里 include 编译时报错提示 No such file or directory或者 CMake 提示find_package(Qt6Mqtt) 找不到包。原因模块还没被安装到 Qt 环境的 include 路径或者安装的 Qt 前缀和你构建时指定的 CMake 路径不一致。Qt 头文件搜索依赖安装目录源码目录再怎么能访问编译器也找不到。解决回到第 3 章的构建流程确认是否正确执行了 make install如果已经安装检查 CMake 里的 CMAKE_PREFIX_PATH 是否指对了 Qt 安装根目录。用一个宏在 CMake 里打印Qt6Mqtt_DIR变量指向哪里一目了然。另一个常见原因是同时装了 Qt 5 与 Qt 6find_package 找到了 Qt5 的构建文件但代码里却用 Qt6 模块头文件此时需要明确指定查找路径。5.2 连接建立后立刻收到 Disconnected现象connectToHost() 调用后stateChanged 信号显示 Connected可 100 到 500 毫秒内就变成 Disconnected日志里看不到 error 或只显示 UnknownError。原因最常见的是 broker 的 allow_anonymous 为 false而你并未配置用户名密码broker 在 CONNACK 阶段直接拒绝另一个原因是 clientId 与其他在线客户端重复broker 踢掉了旧连接后新连接又因为同一标识逻辑反复互踢。解决先用端口为 1883 的本机 broker 排除网络因素写死client.setUsername(guest); client.setPassword(guest);并在 broker 端为这个账号授权同时确保 clientId 进程唯一可加入时间戳或随机数后缀比如qs_ QRandomGenerator。注意不要所有服务端实例共用同一 clientId否则在负载均衡场景下会出现互相挤下线的问题。5.3 消息偶尔丢失QoS 0 与订阅时机是元凶现象订阅某个主题后部分消息没有被回调收到或一开始能收到过一会儿就收不到重新订阅后又能收到。原因QoS 0 的消息本就允许丢弃broker 在内部队列溢出时会优先丢弃 QoS 0 消息还有一种可能是 subscribe 还没完成就发出了 publish导致第一条消息发到了未订阅状态。另外如果使用持久会话但没有正确设置 session 过期时间broker 可能不再把消息推给“看起来已经失联”的会话。解决生产级程序至少把 QoS 提到 1订阅动作建议放在客户端状态变为 Connected 之后并确认 QMqttSubscription 的 state 是 SubscriptionGranted 再发消息。若你要做本地缓存或报表采集建议再加一层数据库落库或消息队列缓冲靠网络协议层去保证完全不丢不现实。5.4 SSL 连接失败报 handshake 错误但看不出问题现象连接 8883 端口时QtMqtt 日志显示 sslErrors甚至直接握手失败本地用 openssl s_client 测试明明是通的。原因QtMqtt 默认启用证书校验而你使用的 broker 证书是自签名或不在系统信任链内。Qt 的 SSL 模块运行时依赖 OpenSSL 库如果当前环境下缺少正确版本的 OpenSSL 运行时也会导致握手这一步直接失败。解决如果是内部测试环境可以临时关闭证书校验来实现联调client.setSSLConfiguration(QSslConfiguration::defaultConfiguration()); client.ignoreSslErrors();生产环境不要这样做。正确做法是把你的 CA 证书导入系统信任库或者通过 QSslConfiguration 手动 setCaCertificates 加载自签 CA 证书。同时检查 OpenSSL 版本Qt 6.5 要求 OpenSSL 1.1.1 以上Windows 上经常遇到旧版 OpenSSL 库文件残留下载对应版本重新覆盖即可。5.5 回调不触发事件循环、线程与队列关系混乱现象publish 后在另一个类里连接的槽函数或者 lambda 不执行断线重连后订阅不再生效。原因QtMqtt 的信号靠事件循环派发。如果是在无 QEventLoop 的线程里调用连接或整个进程没有执行app.exec()回调永远不会触发。另一个坑是订阅成功后如果断线过QtMqtt 可能不会自动恢复旧的订阅关系如果你依赖重连逻辑必须重新走一遍订阅流程。解决确保所有 MQTT 操作都在拥有事件循环的线程里进行最常见的做法是直接在 GUI 主线程里操作。重连后不要只调用 connectToHost要把订阅操作从 Connected 信号里重新执行一次如果订阅本身依赖临时参数那就做成函数并在重连时重建订阅对象。最省事的方式是给客户端设置自动重连并订阅QMqttClient::connected信号每次都重建需要保留的订阅。6. 进阶在 QML 里直接写 MQTT 客户端以及顺手的联调技巧如果 QtMqtt 只在 C 层使用它的能力扩展还只是开了一半。Qt 官方这个模块为 QML 暴露了一批快速上手类型你可以在 qml 文件里直接声明一个 MqttClient 对象省掉 C 暴露接口的中间层非常适合作原型验证和物联网面板类界面。最后一个部分分享 QML 接入的关键写法以及一个让排查效率翻倍的本地联调思路。6.1 QML 里建立一条 MQTT 连接MqttClient 的基本配置使用时先在 QML 文件头导入 QtMqtt 模块然后声明一个 MqttClient 对象配置 hostname、port 和 topic 过滤器import QtQuick 2.15 import QtMqtt 2.0 Item { id: root property string receivedData: MqttClient { id: client hostname: brokerAddr.text port: parseInt(brokerPort.text) username: guest password: guest clientId: qml_client_ Math.random().toString(16).slice(2) onStateChanged: { if (state MqttClient.Connected) { console.log(qml mqtt connected) var sub subscribe(topicFilter.text, 1) sub.messageReceived.connect(function(message) { receivedData message.payload.toString() }) } } } function sendCommand(cmd) { client.publish(topicCmd.text, cmd, 1, false) } }这组属性与 C 接口一一对应理解 C 参数后可以直接迁移。注意 QML 里 MqttClient 的subscribe调用时机同样是建议在state MqttClient.Connected之后进行不然订阅可能丢失clientId使用随机数是为了避免两个 QML 实例同时存在时互踢。QML 类型里topicFilter和topicCmd是界面上的输入框对象实际项目可以换成固定字符串。6.2 主机上没有 broker 时的快速联调容器化 mosquitto写代码时最怕环境起不来。本地调试 MQTT 不需要完整的服务器集群起一个轻量级 broker 就能满足 80% 的场景一般直接用 mosquitto 就行docker run -d --name mqtt-broker -p 1883:1883 -p 9001:9001 eclipse-mosquitto:2这个容器默认配置允许本地匿名访问非常适合联调。启动后你可以在命令行里订阅主题验证发布是否生效mosquitto_sub -h 127.0.0.1 -t dev/sensor/# -q 1 mosquitto_pub -h 127.0.0.1 -t dev/cmd/set -m manual test -q 1如果你不需要容器直接本机安装 mosquitto 也可以但配置文件的显式监听写法和版本有绑定关系容器化能规避这些系统差异。联调时我通常在mosquitto_sub窗口里观察消息一边另开一个终端向 broker 发测试消息确认 Qt 端日志输出正常后再关掉命令行工具排除是 broker 端还是客户端端的问题。6.3 联调里值得养成的两个习惯第一个习惯是始终打开 QtMqtt 客户端的状态日志不要只在 UI 上放一个连接指示器。把stateChanged和errorChanged信号都输出到调试窗断线时你能直接看到是Disconnected还是Connecting配合时间戳能判断是否由心跳引起。第二个习惯是保留一个极简的纯命令行 MQTT 工具串在测试流程里用来验证 broker 本身是否正常当你的 Qt 客户端收发异常时先拿命令行工具确认 broker 没问题再去查代码定位会快很多。我在一些模拟项目里还养成了一个习惯把 MQTT 收到的每条原始报文都先转成十六进制日志打印出来。数据不经过任何解析的原始形态能帮你快速区分编码问题和协议字段问题。收到消息后先打 payload再走到 UI 渲染中间不夹带格式转换和业务逻辑一旦后续界面显示异常就可以确认问题发生在展示层而不是数据链路层。这个习惯帮我在一次开发中省掉了整整一下午的查错时间——你能想象某个设备把 Data 字段写成 UCS-2 编码时字符串直接变问号还找不到回溯点的场景。如果你在项目里从零接入 MQTT我建议先把最小示例跑通确认你能在 mosquitto_sub 里看到 Qt 发的报文然后再进入业务逻辑后续再逐步加入 TLS、遗嘱、离线消息这些能力时每一步都有清晰验证点不会造成一次改动后好几天不知道问题在哪。希望这套基于 qtmqtt.zip 的完整路径能帮你在自己的项目里少绕几个弯子。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站