C++/Qt + Mqtt协议 实现客户端订阅/发布功能
目录一、UI 构建setupUI二、信号槽绑定setupConnections三、连接/断开 BrokeronConnectClicked四、 订阅主题onSubscribeClicked五、取消订阅onUnsubscribeClicked六、发布普通消息onPublishClicked七、发布保留消息onPublishRetainClicked八、连接状态变化处理onStateChanged九、连接成功处理onConnected十、断开连接处理onDisconnected十一、 接收消息处理onMessageReceived十二、消息发送确认onMessageSent十三、错误处理onErrorChanged一、UI 构建setupUIvoid MainWindow::setupUI() { // --- 中央部件和主布局 --- QWidget *central new QWidget(this); setCentralWidget(central); QVBoxLayout *mainLayout new QVBoxLayout(central); // 1. 连接配置区域 m_groupConnect new QGroupBox(tr(连接配置), this); QFormLayout *formLayout new QFormLayout(m_groupConnect); // Broker 地址默认 localhost m_editHost new QLineEdit(127.0.0.1, this); formLayout-addRow(tr(Broker 地址:), m_editHost); // 端口号MQTT 默认 1883 m_spinPort new QSpinBox(this); m_spinPort-setRange(1, 65535); m_spinPort-setValue(1883); formLayout-addRow(tr(端口:), m_spinPort); // 客户端 IDMQTT 协议要求每个客户端有唯一 ID // QUuid 生成一个全局唯一标识符避免多客户端冲突 m_editClientId new QLineEdit(QUuid::createUuid().toString(QUuid::WithoutBraces), this); formLayout-addRow(tr(客户端 ID:), m_editClientId); // 用户名和密码可选用于 Broker 认证 m_editUsername new QLineEdit(this); m_editUsername-setPlaceholderText(可选); formLayout-addRow(tr(用户名:), m_editUsername); m_editPassword new QLineEdit(this); m_editPassword-setPlaceholderText(可选); m_editPassword-setEchoMode(QLineEdit::Password); formLayout-addRow(tr(密码:), m_editPassword); // QoS 等级选择 // QoS 0: 最多一次发完不管可能丢 // QoS 1: 至少一次保证收到可能重复 // QoS 2: 恰好一次保证收到且不重复开销最大 m_comboQos new QComboBox(this); m_comboQos-addItem(QoS 0 - 最多一次, 0); m_comboQos-addItem(QoS 1 - 至少一次, 1); m_comboQos-addItem(QoS 2 - 恰好一次, 2); formLayout-addRow(tr(QoS 等级:), m_comboQos); // 连接/断开按钮 状态标签 QHBoxLayout *connBtnLayout new QHBoxLayout(); m_btnConnect new QPushButton(tr(连接), this); m_btnConnect-setMinimumWidth(100); connBtnLayout-addWidget(m_btnConnect); m_labelStatus new QLabel(tr(状态: 未连接), this); m_labelStatus-setStyleSheet(color: red; font-weight: bold;); connBtnLayout-addWidget(m_labelStatus); connBtnLayout-addStretch(); formLayout-addRow(connBtnLayout); mainLayout-addWidget(m_groupConnect); // 2. 订阅区域 m_groupSubscribe new QGroupBox(tr(订阅主题), this); QHBoxLayout *subLayout new QHBoxLayout(m_groupSubscribe); m_editSubTopic new QLineEdit(test/topic, this); m_editSubTopic-setPlaceholderText(输入主题名如 sensor/temp); subLayout-addWidget(m_editSubTopic); m_btnSubscribe new QPushButton(tr(订阅), this); subLayout-addWidget(m_btnSubscribe); m_btnUnsubscribe new QPushButton(tr(取消订阅), this); subLayout-addWidget(m_btnUnsubscribe); mainLayout-addWidget(m_groupSubscribe); // 3. 发布区域 m_groupPublish new QGroupBox(tr(发布消息), this); QHBoxLayout *pubLayout new QHBoxLayout(m_groupPublish); m_editPubTopic new QLineEdit(test/topic, this); m_editPubTopic-setPlaceholderText(发布主题); pubLayout-addWidget(m_editPubTopic, 2); m_editMessage new QLineEdit(Hello MQTT, this); m_editMessage-setPlaceholderText(消息内容); pubLayout-addWidget(m_editMessage, 3); m_btnPublish new QPushButton(tr(发布), this); pubLayout-addWidget(m_btnPublish); m_btnPubRetain new QPushButton(tr(发布(保留)), this); m_btnPubRetain-setToolTip(保留消息Broker 会保存最后一条新订阅者立即收到); pubLayout-addWidget(m_btnPubRetain); mainLayout-addWidget(m_groupPublish); // 4. 消息日志区域 QGroupBox *logGroup new QGroupBox(tr(消息日志), this); QVBoxLayout *logLayout new QVBoxLayout(logGroup); m_logText new QTextEdit(this); m_logText-setReadOnly(true); // 只读不允许用户编辑 logLayout-addWidget(m_logText); mainLayout-addWidget(logGroup, 1); // 日志区域占剩余空间 // 窗口基本设置 setWindowTitle(tr(MQTT 学习 Demo - QtMqtt)); resize(700, 650); }二、信号槽绑定setupConnectionsvoid MainWindow::setupConnections() { // --- 按钮点击信号 --- connect(m_btnConnect, QPushButton::clicked, this, MainWindow::onConnectClicked); connect(m_btnSubscribe, QPushButton::clicked, this, MainWindow::onSubscribeClicked); //订阅 connect(m_btnUnsubscribe, QPushButton::clicked, this, MainWindow::onUnsubscribeClicked); //取消订阅 connect(m_btnPublish, QPushButton::clicked, this, MainWindow::onPublishClicked); //发布普通消息 connect(m_btnPubRetain, QPushButton::clicked, this, MainWindow::onPublishRetainClicked); // 发布保留消息 // --- QMqttClient 核心信号重点学习这些 --- // 1. stateChanged: 连接状态变化时触发 // ClientState 枚举值: Disconnected0, Connecting1, Connected2 connect(m_client, QMqttClient::stateChanged, this, MainWindow::onStateChanged); // 2. connected: 成功连接到 Broker 时触发 connect(m_client, QMqttClient::connected, this, MainWindow::onConnected); // 3. disconnected: 与 Broker 断开连接时触发 connect(m_client, QMqttClient::disconnected, this, MainWindow::onDisconnected); // 4. messageReceived: 收到订阅主题的消息时触发 // 参数: QByteArray 消息内容, QMqttTopicName 主题名 connect(m_client, QMqttClient::messageReceived, this, MainWindow::onMessageReceived); // 5. messageSent: 消息成功发布到 Broker 时触发 // 参数: qint32 消息 ID connect(m_client, QMqttClient::messageSent, this, MainWindow::onMessageSent); // 6. errorChanged: 发生错误时触发 connect(m_client, QMqttClient::errorChanged, this, MainWindow::onErrorChanged); }三、连接/断开 BrokeronConnectClickedvoid MainWindow::onConnectClicked() { if (m_client-state() QMqttClient::Connected) { // 已连接 → 执行断开 m_client-disconnectFromHost(); return; } // --- 配置连接参数 --- m_client-setHostname(m_editHost-text()); m_client-setPort(m_spinPort-value()); m_client-setClientId(m_editClientId-text()); // 设置认证信息如果填了用户名和密码 if (!m_editUsername-text().isEmpty()) { m_client-setUsername(m_editUsername-text()); } if (!m_editPassword-text().isEmpty()) { m_client-setPassword(m_editPassword-text()); } // 设置 Keep Alive心跳间隔单位秒 // 客户端必须在此时间内至少发送一次消息或 PING 请求否则 Broker 会认为客户端已死并断开连接 // 默认 60 秒设为 30 秒更保险 m_client-setKeepAlive(30); /* * 什么是遗嘱消息 遗嘱消息是 MQTT 客户端在连接 Broker 时提前“立下遗嘱”指定一个主题和一条消息。如果该客户端异常断开例如网络故障、电源掉电、程序崩溃等而不是主动发送 DISCONNECT 报文 Broker 就会自动将这条遗嘱消息发布到指定的主题上通知其他订阅者。 类比一个人客户端在进入危险环境前提前写好遗书遗嘱消息交给公证人Broker并说明如果自己意外死亡异常断开就把遗书公布给指定的人订阅者。 */ // 配置遗嘱消息Last Will // 作用当客户端异常断开时Broker 自动发布这条消息通知其他客户端 m_client-setWillTopic(client/status);//设置遗嘱消息的发布主题。当客户端异常断开时Broker 会向 client/status 这个主题发布一条消息。 m_client-setWillMessage(客户端离线); m_client-setWillQoS(1); m_client-setWillRetain(true); // 标记为保留消息。Broker 会永久保存这条消息后续新订阅 client/status 主题的客户端立即收到这条离线通知而不需要等待下一次事件发生。 appendLog(正在连接到 Broker m_editHost-text() : QString::number(m_spinPort-value()) ...); // 发起连接异步操作结果通过 connected/disconnected 信号通知 m_client-connectToHost(); }四、 订阅主题onSubscribeClickedvoid MainWindow::onSubscribeClicked() { QString topic m_editSubTopic-text().trimmed(); if (topic.isEmpty()) { QMessageBox::warning(this, tr(警告), tr(请输入要订阅的主题名)); return; } // 获取用户选择的 QoS 等级 int qos m_comboQos-currentData().toInt(); // QMqttClient::subscribe() 返回 QMqttSubscription 对象 // 可通过它监控订阅状态如是否成功、QoS 协商结果等 /*MQTT 主题过滤器 可以包含通配符 单层通配符匹配任意一个层级。 #多层通配符匹配任意多个层级必须放在最后。 当你构造 QMqttTopicFilter(topic) 时如果传入的 topic 不包含通配符它就是一个精确的主题过滤器只匹配该主题本身如果包含通配符它会匹配所有符合条件的主题。*/ QMqttSubscription *sub m_client-subscribe(QMqttTopicFilter(topic), qos); // QMqttClient::subscribe()订阅方法返回订阅对象 // 可选监听订阅状态变化 connect(sub, QMqttSubscription::stateChanged, this, [this, topic](QMqttSubscription::SubscriptionState state) { if (state QMqttSubscription::Subscribed) { appendLog(✓ 订阅成功: topic); } else if (state QMqttSubscription::Unsubscribed) { appendLog(✗ 已取消订阅: topic); } }); appendLog(正在订阅主题: topic (QoS QString::number(qos) )); }五、取消订阅onUnsubscribeClickedvoid MainWindow::onUnsubscribeClicked() { QString topic m_editSubTopic-text().trimmed(); if (topic.isEmpty()) { QMessageBox::warning(this, tr(警告), tr(请输入要取消订阅的主题名)); return; } // QMqttClient::unsubscribe() 取消对指定主题的订阅 m_client-unsubscribe(QMqttTopicFilter(topic)); appendLog(正在取消订阅: topic); }六、发布普通消息onPublishClickedvoid MainWindow::onPublishClicked() { QString topic m_editPubTopic-text().trimmed(); QString message m_editMessage-text().trimmed(); if (topic.isEmpty() || message.isEmpty()) { QMessageBox::warning(this, tr(警告), tr(请输入主题和消息内容)); return; } int qos m_comboQos-currentData().toInt(); // QMqttClient::publish() 发布消息 // 参数: 主题名, 消息内容, QoS 等级, 是否保留默认false // 返回值: qint32 消息 ID可用于跟踪发布状态 qint32 msgId m_client-publish( QMqttTopicName(topic), // 主题 message.toUtf8(), // 消息体转为 UTF-8 字节 qos // QoS 等级 ); appendLog(发布消息 - 主题: topic , QoS: QString::number(qos) , ID: QString::number(msgId)); }七、发布保留消息onPublishRetainClickedvoid MainWindow::onPublishRetainClicked() { QString topic m_editPubTopic-text().trimmed(); QString message m_editMessage-text().trimmed(); if (topic.isEmpty() || message.isEmpty()) { QMessageBox::warning(this, tr(警告), tr(请输入主题和消息内容)); return; } int qos m_comboQos-currentData().toInt(); // 保留消息Broker 会保存这条消息后续订阅该主题的新客户端会立即收到 // 典型用途设备状态、配置信息等最新值 qint32 msgId m_client-publish( QMqttTopicName(topic), message.toUtf8(), qos, true // retain true标记为保留消息 ); appendLog(发布保留消息 - 主题: topic , ID: QString::number(msgId)); }八、连接状态变化处理onStateChangedvoid MainWindow::onStateChanged(QMqttClient::ClientState state) { // 根据当前状态更新 UI 显示 QString stateText; switch (state) { case QMqttClient::Disconnected: stateText 未连接; break; case QMqttClient::Connecting: stateText 正在连接...; break; case QMqttClient::Connected: stateText 已连接; break; } m_labelStatus-setText(状态: stateText); // 连接中显示黄色已连接显示绿色未连接显示红色 if (state QMqttClient::Connected) { m_labelStatus-setStyleSheet(color: green; font-weight: bold;); } else if (state QMqttClient::Connecting) { m_labelStatus-setStyleSheet(color: orange; font-weight: bold;); } else { m_labelStatus-setStyleSheet(color: red; font-weight: bold;); } }九、连接成功处理onConnectedvoid MainWindow::onConnected() { appendLog( 已连接到 Broker ); m_btnConnect-setText(断开); // 连接成功后可以发布一条上线通知 m_client-publish(QMqttTopicName(client/status), 客户端在线, 1, true); }十、断开连接处理onDisconnectedvoid MainWindow::onDisconnected() { appendLog( 与 Broker 断开连接 ); m_btnConnect-setText(连接); }十一、 接收消息处理onMessageReceivedvoid MainWindow::onMessageReceived(const QByteArray message, const QMqttTopicName topic) { // 收到消息时的核心处理逻辑 // topic.name() → 获取主题名字符串 // message → 获取消息内容QByteArray可转 QString QString timeStr QDateTime::currentDateTime().toString(HH:mm:ss); appendLog([ timeStr ] 收到消息 ← 主题: topic.name() | 内容: QString::fromUtf8(message)); }十二、消息发送确认onMessageSentvoid MainWindow::onMessageSent(qint32 id) { // 消息成功发布到 Broker 后触发 // 注意QoS 0 时可能不会触发此信号因为不需要确认 appendLog(消息发送确认 ✓ (ID: QString::number(id) )); }十三、错误处理onErrorChangedvoid MainWindow::onErrorChanged(QMqttClient::ClientError error) { // 错误处理根据错误码判断问题类型 QString errorText; switch (error) { case QMqttClient::NoError: return; // 无错误忽略 // --- 协议层错误MQTT 协议返回 --- case QMqttClient::InvalidProtocolVersion: errorText 协议版本不支持; break; case QMqttClient::IdRejected: errorText 客户端 ID 被拒绝可能太长或格式不对; break; case QMqttClient::ServerUnavailable: errorText Broker 不可用; break; case QMqttClient::BadUsernameOrPassword: errorText 用户名或密码错误; break; case QMqttClient::NotAuthorized: errorText 认证失败无权限; break; // --- 传输层错误网络问题 --- case QMqttClient::TransportInvalid: errorText 传输层错误网络断开/连接失败; break; case QMqttClient::ProtocolViolation: errorText 协议违规消息格式不正确; break; case QMqttClient::UnknownError: errorText 未知错误; break; case QMqttClient::Mqtt5SpecificError: errorText MQTT 5.0 特有错误; break; default: errorText 未知错误码: QString::number(error); break; } appendLog(⚠ 错误: errorText); }

相关新闻

千笔AI:深度学习驱动的学术写作智能辅助平台

千笔AI:深度学习驱动的学术写作智能辅助平台

1. 项目概述:学术写作的智能化革命千笔AI作为一款面向学术论文写作的智能辅助平台,正在改变传统学术研究的范式。这个平台的核心价值在于通过深度学习技术,为研究者提供从选题到发表的全程智能化支持。不同于市面上常见的论文查重或格式调整工…

2026/7/28 1:28:12 阅读更多 →
3个步骤让魔兽争霸3在现代电脑上完美运行:WarcraftHelper兼容性修复指南

3个步骤让魔兽争霸3在现代电脑上完美运行:WarcraftHelper兼容性修复指南

3个步骤让魔兽争霸3在现代电脑上完美运行:WarcraftHelper兼容性修复指南 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 你是否还在为魔兽…

2026/7/28 1:27:12 阅读更多 →
【企业微信】基于iPad协议的联系人管理接口:支持内外部联系人操作与多渠道添加功能

【企业微信】基于iPad协议的联系人管理接口:支持内外部联系人操作与多渠道添加功能

一、简要总结 本文档是企业微信 iPad 协议接口的联系人模块功能说明,共包含20 个细分操作接口,所有接口统一采用POST请求、ContentType:application/json格式,uuid 为唯一必传核心参数,服务域名以[172.0.0.1:8083](17…

2026/7/28 1:27:12 阅读更多 →

最新新闻

AI旅行助手终极指南:三步打造你的智能旅行规划师

AI旅行助手终极指南:三步打造你的智能旅行规划师

AI旅行助手终极指南:三步打造你的智能旅行规划师 【免费下载链接】ai-travel-agent AI Travel Agent 项目地址: https://gitcode.com/gh_mirrors/ai/ai-travel-agent 想要告别繁琐的旅行规划,让AI智能助手帮你搞定一切?AI旅行助手正是…

2026/7/28 1:36:15 阅读更多 →
Materialize:基于物理渲染的智能材质生成框架的技术实现与架构分析

Materialize:基于物理渲染的智能材质生成框架的技术实现与架构分析

Materialize:基于物理渲染的智能材质生成框架的技术实现与架构分析 【免费下载链接】Materialize Materialize is a program for converting images to materials for use in video games and whatnot 项目地址: https://gitcode.com/gh_mirrors/mate/Materialize…

2026/7/28 1:36:15 阅读更多 →
Python-OKX终极指南:从零构建加密货币量化交易系统

Python-OKX终极指南:从零构建加密货币量化交易系统

Python-OKX终极指南:从零构建加密货币量化交易系统 【免费下载链接】python-okx 项目地址: https://gitcode.com/GitHub_Trending/py/python-okx 在当今数字资产交易领域,数据获取与交易执行的自动化已成为量化策略成功的核心要素。python-okx作…

2026/7/28 1:36:14 阅读更多 →
光影艺术装置的技术实现与创作指南

光影艺术装置的技术实现与创作指南

1. 项目概述:光的这世上"光的这世上"这个标题乍看有些抽象,但细品之下蕴含着丰富的创作可能性。作为一名视觉艺术领域的实践者,我倾向于将其解读为一个以光为核心媒介的创意项目。光作为最基础的物理现象,却承载着无限的…

2026/7/28 1:36:14 阅读更多 →
React Native Hermes字节码逆向:从.hbc文件还原JavaScript逻辑的工程实践

React Native Hermes字节码逆向:从.hbc文件还原JavaScript逻辑的工程实践

1. 项目概述:为什么我们需要深入Hermes字节码在React Native的生态里,Hermes引擎已经从一个可选项变成了事实上的默认项,尤其是在新版本的项目中。它带来的启动性能提升和包体积优化是实实在在的。但随之而来的,是调试和逆向分析的…

2026/7/28 1:36:14 阅读更多 →
TI bq27542-G1阻抗跟踪电量计EVM:从硬件连接到精准校准的完整指南

TI bq27542-G1阻抗跟踪电量计EVM:从硬件连接到精准校准的完整指南

1. 项目概述与核心价值如果你正在开发一款依赖单节锂离子电池供电的产品,比如便携式医疗设备、高端无线耳机或者智能门锁,那么最让你头疼的问题之一,可能就是如何准确地向用户报告剩余电量。你肯定遇到过这种情况:设备明明显示还有…

2026/7/28 1:35:14 阅读更多 →

日新闻

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生 【免费下载链接】OmenSuperHub Control Omen laptop performance, fan speeds, and keyboard lighting, and unlock power limits. 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 你是否也曾为官方Om…

2026/7/28 0:00:43 阅读更多 →
RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

做 RAG 的人应该都踩过这个致命的坑:把几百页的财报、法规、技术手册扔给向量库,问一个具体问题,搜出来的全是沾边但没用的内容 —— 关键信息要么被硬切块拆碎了,要么藏在几十条结果的最下面。语义相似≠真正相关,这个…

2026/7/28 0:00:43 阅读更多 →
抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

2026年做短视频运营,从抖音上扒文案早就不是偷偷抄笔记的事了。我刚开始做内容的时候,每天刷半小时抖音,手动把爆款视频的口播敲进备忘录,一条2分钟的视频得花十来分钟,碰到语速快的还要反复回听。后来试了一圈工具&am…

2026/7/28 0:00:43 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/27 4:33:59 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/27 6:31:56 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/27 4:01:12 阅读更多 →

月新闻