一、 MQTT 简介
MQTT (消息队列遥测传输,Message Queuing Telemetry Transport) 是OASIS和ISO制定的物联网消息协议,主要有3.1.1和5.0版规范,新项目普遍使用 MQTT 5.0。
- MQTT5 特点
- 基于TCP
- 发布/订阅模式,核心是 MQTT 客户端 (Client) 和 MQTT代理(Broker)
- 支持服务质量 (QoS) 等级、持久会话、会话管理、用户属性等
二、数据包格式

- Packet type:15 种控制报文类型,可分为连接、发布、订阅三类
- ACK:确认消息
- REC、REL、COMP:保证交付,收到、释放、完成

-
Flag: 标志位,PUBLISH报文标识
- Bit 3:DUP,是否重传报文。
- Bit 2,1:QoS,服务质量等级。
- Bit 0:Retain,是否保留消息。
-
Remaining Length:从第2个字节开始,
可变头(Variable header) + 消息体(payload)的长度
三、发布/订阅

- 主题Topic:消息路由通道,类似于 URL 路径,使用/分层,订阅时可使用+或#通配符
text
sensor/1/temperature
sensor/2/temperature
sensor/+/temperature //单层通配符
sensor/# //多层通配符
- QoS
- QoS 0:消息最多传送一次。如果当前客户端不可用,它将丢失这条消息。
- QoS 1:消息至少传送一次。
- QoS 2:消息只传送一次。
四、开发实践
代码示例使用Mosquitto,当然Client也可以选Paho、Broker选EMQX、NanoMQ、HiveMQ等。
Broker (代理)
- 下载 mosquitto broker安装,安装时勾选"Service"组件
- 安装目录下打开mosquitto.conf,添加监听配置
text
listener 1883 0.0.0.0
allow_anonymous true
- 打开Windows服务,重启服务
- 安装目录下,切到命令行,运行测试
bash
mosquitto_sub -h localhost -t "test/topic" -v
# 运行下一句pub后收到: test/topic Hello MQTT
mosquitto_pub -h localhost -t "test/topic" -m "Hello MQTT3"
Client (发布者/订阅者)
- cmake编译mosquitto及所需依赖如cJson等,可选的如需要持久化添加SQLite依赖
- 创建C++项目,设置头文件包含目录和库目录,设置附加依赖项mosquitto.lib libcjson.lib...
- Client代码编译成功后,拷贝dll到exe目录,先运行订阅者,再运行发布者,否则消息后丢失
Publisher 发布者
cpp
#include <iostream>
#include "mosquitto.h"
void on_connect(struct mosquitto* mosq, void* obj, int reason_code)
{
printf("on_connect: %s\n", mosquitto_connack_string(reason_code));
if (reason_code != 0)
mosquitto_disconnect(mosq);
}
void on_publish(struct mosquitto* mosq, void* obj, int mid)
{
printf("Message with mid %d has been published.\n", mid);
}
int main()
{
mosquitto_lib_init();
struct mosquitto* mosq = mosquitto_new(NULL, true, NULL);
// 设置回调函数
mosquitto_connect_callback_set(mosq, on_connect); // 收到CONNACK触发
mosquitto_publish_callback_set(mosq, on_publish); // PUBLISH成功触发(QoS0) (QoS1:PUBACK , QoS2:PUBCOMP)
mosquitto_connect(mosq, "127.0.0.1", 1883, 60);
mosquitto_loop_start(mosq);
//推送消息
char payload[20] = {0};
snprintf(payload, sizeof(payload), "temperature is %d", 26);
mosquitto_publish(mosq, NULL, "example/temperature", (int)strlen(payload), payload, 2, false);
getchar();
mosquitto_lib_cleanup();
return 0;
}
Subscriber 订阅者
cpp
#include <iostream>
#include "mosquitto.h"
void on_connect(struct mosquitto* mosq, void* obj, int reason_code)
{
printf("on_connect: %s\n", mosquitto_connack_string(reason_code));
if (reason_code != 0)
mosquitto_disconnect(mosq);
//订阅,写在此处重连时能恢复
mosquitto_subscribe(mosq, NULL, "example/temperature", 1);
}
// obj:mosquitto_new时的用户数据;mid:mosquitto_subscribe时的消息id;
// qos_count:Topic主题数量;granted_qos:每个订阅请求最终授予的 QoS
void on_subscribe(struct mosquitto* mosq, void* obj, int mid, int qos_count, const int* granted_qos)
{
bool have_any_subscription = false;
for (int i = 0; i < qos_count; i++) {
printf("on_subscribe: %d:granted qos = %d\n", i, granted_qos[i]);
if (granted_qos[i] <= 2) {
have_any_subscription = true;//qos 0,1,2表示订阅成功,128表示订阅失败
}
}
if (have_any_subscription == false) {
fprintf(stderr, "Error: All subscriptions rejected.\n");
mosquitto_disconnect(mosq);
}
}
void on_message(struct mosquitto* mosq, void* obj, const struct mosquitto_message* msg)
{
printf("%s %d %s\n", msg->topic, msg->qos, (char*)msg->payload);
}
int main()
{
mosquitto_lib_init();
struct mosquitto* mosq = mosquitto_new(NULL, true, NULL);
// 设置回调函数
mosquitto_connect_callback_set(mosq, on_connect); // 收到CONNACK触发
mosquitto_subscribe_callback_set(mosq, on_subscribe); // 收到SUBACK触发,即订阅成功
mosquitto_message_callback_set(mosq, on_message); // 收到消息触发
mosquitto_connect(mosq, "127.0.0.1", 1883, 60);
mosquitto_loop_start(mosq);
getchar();
mosquitto_lib_cleanup();
return 0;
}