MQTT协议

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是一种基于发布 / 订阅(Publish/Subscribe)模式的轻量级物联网通信协议,由 IBM 在 1999 年设计,核心目标是在低带宽、不稳定网络环境下,实现设备间高效、可靠的小数据量通信,广泛应用于物联网(IoT)、工业物联网(IIoT)、智能家居、远程监控等场景。

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是一种基于发布/订阅(Publish/Subscribe)模式的轻量级物联网通信协议,由IBM在1999年设计,核心目标是在低带宽、不稳定网络环境下,实现设备间高效、可靠的小数据量通信,广泛应用于物联网(IoT)、工业物联网(IIoT)、智能家居、远程监控等场景。

MQTT协议具有如下特性

  1. 轻量级:协议头部开销极小(固定头部仅2字节),客户端代码体积小、资源消耗低,适配单片机、传感器等算力/存储有限的设备。
  2. 发布/订阅模式:不同于传统的点对点通信,MQTT通过“主题(Topic)”实现消息解耦:
    • 发布者(Publisher):仅负责向指定主题发送消息,无需知道订阅者的存在;
    • 订阅者(Subscriber):仅关注感兴趣的主题,接收该主题下的所有消息;
    • 代理(Broker):核心中间件,负责接收发布者的消息、匹配订阅关系,并将消息推送给对应订阅者。
  3. QoS(Quality of Service)消息质量等级:支持三种等级,适配不同可靠性需求:
    • QoS 0:最多一次交付,消息发送后无需确认,可能丢失(适用于非关键数据,如温湿度实时采集);
    • QoS 1:至少一次交付,消息会被确认,确保送达但可能重复(适用于重要数据,如设备指令);
    • QoS 2:恰好一次交付,通过两次握手+消息唯一标识确保消息仅送达一次(适用于核心数据,如金融交易、设备状态变更)。
  4. 持久化与会话保持
    • 订阅持久化:订阅者可设置“持久化订阅”,即使离线,Broker也会缓存对应主题的消息,待其重连后推送;
    • 遗嘱消息(Last Will and Testament,LWT):客户端连接时可预设“遗嘱”,若异常断开(如网络中断、设备故障),Broker会自动将遗嘱消息发送到指定主题,用于设备异常告警。
  5. 低带宽适配:采用二进制编码格式,相比HTTP等文本协议,数据传输效率更高,适合窄带网络(如2G/3G、LoRa)。

核心架构

MQTT的通信架构包含三类角色:

  • 客户端(Client):可以是发布者、订阅者,或同时兼具两种身份(如智能灯既订阅“开关指令”主题,也发布“运行状态”主题);
  • 代理(Broker):核心枢纽,负责管理客户端连接、主题匹配、消息转发,主流的MQTT Broker有EMQ X、Mosquitto、AWS IoT Core等;
  • 服务器(Server):通常Broker部署在服务器端,客户端通过TCP/IP(或WebSocket)与Broker建立连接。

典型通信流程:

  1. 客户端与Broker建立TCP连接,完成身份认证(支持用户名密码、TLS/SSL加密);
  2. 订阅者向Broker发送“订阅请求”,指定感兴趣的主题(如“home/bedroom/temperature”);
  3. 发布者向Broker发送“发布请求”,指定主题并携带消息(如“25℃”);
  4. Broker根据订阅关系,将消息推送给所有订阅该主题的客户端;
  5. 客户端完成通信后,可主动断开连接,或保持长连接等待后续消息。

一、MQTT Broker、发布者、订阅者的通用部署逻辑

MQTT各角色的部署核心是Broker作为中心枢纽,发布者/订阅者作为终端客户端,部署形态随场景(家用/工业/商用)灵活调整,核心原则如下:

角色 部署位置/方式 核心要求
MQTT Broker 1. 云部署(主流):部署在公有云(阿里云IoT、腾讯云IoT、AWS IoT Core)或私有云服务器,通过公网提供服务;
2. 本地部署:家用场景常内置在智能网关、路由器或本地服务器(如树莓派),断网时仍可本地通信
稳定的网络/算力、支持高并发连接、消息持久化、权限管理(如ACL)、TLS加密
发布者/订阅者 部署在终端设备(硬件/软件),只要能运行MQTT客户端库、通过TCP/IP/WebSocket联网即可 轻量化(适配设备算力)、支持MQTT协议核心指令(连接、发布、订阅、断开)

注:发布者和订阅者并非“固定角色”——同一设备可同时是发布者+订阅者(如智能灯既发布状态,也订阅控制指令)。

下面以手机APP + 智能网关 + 智能灯的场景为例,说明三者的角色

二、手机APP监控家用智能灯的场景

家用智能灯的MQTT通信通常分两种架构(取决于网关是否内置Broker),以下是最常见的“云Broker+网关桥接”架构(断网时可降级为本地Broker):

核心角色定义

设备 核心角色 具体职责
智能灯 主要为发布者,次要为订阅者 1. 发布者:实时向指定Topic发布自身状态(如“home/livingroom/light/status”,消息内容:“开启/亮度80%/色温4000K”);
2. 订阅者:订阅控制指令Topic(如“home/livingroom/light/control”),接收手机APP的开关/调光指令
智能网关 网络桥接器 + 可选本地Broker 1. 桥接:智能灯多为低功耗无线设备(ZigBee/蓝牙),无法直接连公网,网关将ZigBee/蓝牙协议转换为TCP/IP,代理智能灯与云端Broker通信;
2. 本地Broker(可选):若网关内置Mosquitto等轻量Broker,断网时可在本地完成智能灯与手机(局域网)的MQTT通信
手机APP 主要为订阅者,次要为发布者 1. 订阅者:向Broker订阅智能灯状态Topic,实时接收灯的照明情况;
2. 发布者:向控制指令Topic发布操作指令(如“关闭/调亮至100%”);
3. 额外:APP需内置MQTT客户端SDK,完成与Broker的连接、认证、订阅/发布操作
云端MQTT Broker 核心消息枢纽 1. 接收智能灯(经网关)发布的状态消息;
2. 匹配订阅关系:将状态消息推送给订阅该Topic的手机APP;
3. 接收手机APP发布的控制指令,推送给智能灯(经网关);
4. 可选:持久化消息(如智能灯离线时缓存控制指令,重连后推送)

完整通信流程(云Broker架构)

  1. 智能网关接入家庭网络,与云端Broker建立长连接,并完成身份认证(设备证书/用户名密码);
  2. 智能灯通过ZigBee/蓝牙连接网关,网关为智能灯分配唯一标识,代理其完成MQTT客户端初始化;
  3. 手机APP打开后,通过移动网络/家庭WiFi连接云端Broker,订阅“home/livingroom/light/status”主题;
  4. 智能灯实时采集自身状态(如亮度、开关状态),通过网关将消息发布到上述主题;
  5. 云端Broker识别到该主题有订阅者(手机APP),将状态消息推送给手机APP,APP解析后展示照明情况;
  6. 若用户在APP操作(如关灯),APP向“home/livingroom/light/control”发布“关闭”指令;
  7. Broker将指令推送给网关,网关转换为ZigBee/蓝牙指令下发给智能灯,智能灯执行操作后,再次发布更新后的状态到状态主题,APP同步显示。

补充:本地Broker架构(断网场景)

若网关内置MQTT Broker(如小米网关、涂鸦智能网关),断网时:

  • 手机APP通过家庭WiFi连接网关内置的本地Broker;
  • 智能灯仍通过网关与本地Broker通信;
  • 所有消息转发在本地完成,无需公网,保证断网时监控/控制功能可用。

三、关键补充

  • 权限控制:Broker会为每个设备(智能灯、手机APP、网关)分配独立的身份凭证,仅允许智能灯发布状态Topic、订阅控制Topic,避免非法设备篡改指令;
  • 低功耗优化:智能灯作为MQTT客户端,会采用“长连接+心跳包”机制,既保证实时通信,又降低功耗(心跳间隔可配置,如30秒);
  • Topic设计:家用场景的Topic通常按“区域/设备类型/设备ID/消息类型”分层,如“home/bedroom/light/123/status”(123为灯的唯一ID),便于Broker精准路由。

Qos

MQTT的QoS(Quality of Service,服务质量等级)是协议核心的可靠性保障机制,定义了消息从发布者经Broker(代理)到订阅者的交付规则,共分为0、1、2三个等级——等级越高,消息交付的可靠性越强,但通信开销、延迟和协议复杂度也越高,可根据业务场景灵活选择。

核心原则

QoS的保障范围是“单段链路”(发布者↔Broker、Broker↔订阅者),最终实际生效的QoS为两段链路中较低的等级(例如发布者以QoS 2发布,订阅者以QoS 1订阅,最终按QoS 1交付);且QoS仅保障“消息送达”,不保障“消息顺序”(需业务层或Broker配置实现顺序性)。

各QoS等级详细解析

1. QoS 0:最多一次(At Most Once)

  • 核心定义:消息仅发送一次,发送方(发布者/Broker)无需接收方确认,消息可能送达、也可能丢失(无重传、无确认机制)。
  • 通信流程(极简):
    • 发布者 → Broker:直接发送消息,不等待Broker的确认(PUBACK);
    • Broker → 订阅者:直接转发消息,不等待订阅者的确认。
  • 关键特点
    • 开销最小(仅一次消息传输)、延迟最低,仅依赖TCP的基础可靠性(TCP丢包则消息直接丢失);
    • 无消息重传、无重复风险,但存在丢失风险。
  • 适用场景:非关键、可容忍丢失的实时数据,例如:
    • 智能家居:智能灯的实时亮度/色温采集、温湿度传感器的秒级数据上报;
    • 工业场景:车间设备的实时转速/温度(丢一条不影响整体监控)。

2. QoS 1:至少一次(At Least Once)

  • 核心定义:确保消息至少送达一次,发送方会重传消息直到收到接收方的确认,可能出现消息重复(但绝对不会丢失)。
  • 通信流程(带确认+重传):
    阶段1:发布者 → Broker
    1. 发布者发送消息(携带唯一16位Message ID,用于标识消息);
    2. Broker收到消息后,返回PUBACK(发布确认);
    3. 若发布者超时未收到PUBACK,会重复发送该消息(直到收到确认)。
    阶段2:Broker → 订阅者
    1. Broker向订阅者转发消息(复用Message ID);
    2. 订阅者收到后返回PUBACK;
    3. 若Broker超时未收到PUBACK,会重发消息。
  • 关键特点
    • 可靠性高于QoS 0,有少量额外开销(确认包+可能的重传);
    • 存在消息重复风险(需业务层通过Message ID或唯一标识去重)。
  • 适用场景:重要、不允许丢失但可容忍重复的数据,例如:
    • 智能家居:智能门锁的开锁/关锁指令、智能灯的开关指令(重复收到仅执行一次);
    • 告警场景:设备离线告警、低电量提醒(必须送达,重复告警无影响)。

3. QoS 2:恰好一次(Exactly Once)

  • 核心定义:确保消息仅送达一次,无丢失、无重复,是MQTT最高级别的可靠性保障(开销最大)。
  • 通信流程(两次握手+唯一标识,分“发布者→Broker”和“Broker→订阅者”两段,流程一致):
    以“发布者→Broker”为例:
    1. 发布者发送消息(带Message ID),Broker收到后返回PUBREC(发布收到);
    2. 发布者收到PUBREC后,发送PUBREL(发布释放),确认“已收到Broker的接收确认”;
    3. Broker收到PUBREL后,保存消息并返回PUBCOMP(发布完成),发布者收到后终止重传;
    4. 若任意步骤超时,发送方会重传对应指令(而非整条消息),确保流程闭环。
      (Broker→订阅者的流程完全复用上述步骤,仅角色互换)
  • 关键特点
    • 完全可靠(无丢失、无重复),但通信步骤多、延迟高、开销最大;
    • 依赖Message ID和四段式握手,Broker需临时持久化消息直到流程完成。
  • 适用场景:核心、不可重复、不可丢失的数据,例如:
    • 金融场景:支付指令、交易记录上报;
    • 物联网计费:智能电表/水表的用电量/用水量上报(重复会导致计费错误);
    • 设备控制:工业机器人的动作指令(重复执行会导致设备故障)。

QoS关键补充说明

  1. Message ID的作用:QoS 1/2的消息均携带16位Message ID(范围0~65535),用于标识消息、实现重传和去重,同一客户端的Message ID会循环使用。
  2. 持久化机制:QoS 1/2的消息会被Broker持久化存储,直到确认送达订阅者,避免Broker重启后消息丢失;QoS 0的消息仅在内存中暂存,重启即丢失。
  3. 开销对比(从低到高):QoS 0 < QoS 1 < QoS 2,实际选型需平衡“可靠性需求”和“设备/网络开销”(例如低功耗传感器优先选QoS 0,核心指令选QoS 1/2)。

代码示例

一、整体方案说明

本次示例基于家用网关内置MQTT Broker(用轻量的Mosquitto模拟),实现:

  • 智能灯(Publisher):周期性发布自身状态(开关/亮度),同时订阅控制指令;
  • 网关(Broker):Mosquitto服务,负责消息路由;
  • 手机APP(Subscriber):订阅智能灯状态、接收消息,也可发布控制指令。

环境准备

  1. 安装依赖:pip install paho-mqtt(MQTT客户端库);
  2. 安装Mosquitto(模拟网关Broker):
    • Windows:下载安装包,启动服务 net start mosquitto
    • Linux:sudo apt install mosquitto && sudo systemctl start mosquitto
    • 验证:默认监听1883端口,telnet localhost 1883 可连通。

二、代码实现

1. 智能灯(Publisher):light_publisher.py

核心逻辑:发布状态Topic(home/light/status),订阅控制Topic(home/light/control),模拟灯的状态上报和指令接收。

import paho.mqtt.client as mqtt
import time
import random

# MQTT Broker配置(网关地址,本地模拟则为localhost)
BROKER_HOST = "localhost"
BROKER_PORT = 1883
CLIENT_ID = "smart_light_001"  # 智能灯唯一标识

# 灯的初始状态
light_status = {
    "switch": "on",
    "brightness": 80
}

# 连接Broker成功的回调
def on_connect(client, userdata, flags, rc):
    print(f"智能灯连接网关Broker成功,返回码:{rc}")
    # 订阅控制指令Topic(QoS 1,确保指令不丢失)
    client.subscribe("home/light/control", qos=1)

# 接收控制指令的回调(订阅者逻辑)
def on_message(client, userdata, msg):
    cmd = msg.payload.decode("utf-8")
    print(f"\n收到控制指令:{cmd}")
    # 模拟执行指令
    if cmd == "off":
        light_status["switch"] = "off"
        light_status["brightness"] = 0
    elif cmd == "on":
        light_status["switch"] = "on"
        light_status["brightness"] = 80
    elif cmd.startswith("brightness:"):
        light_status["brightness"] = int(cmd.split(":")[1])
    print(f"灯状态更新:{light_status}")

# 初始化客户端
client = mqtt.Client(client_id=CLIENT_ID, clean_session=False)
client.on_connect = on_connect
client.on_message = on_message

# 连接Broker
client.connect(BROKER_HOST, BROKER_PORT, keepalive=60)

# 启动客户端循环(处理网络事件)
client.loop_start()

# 周期性发布灯的状态(QoS 1,确保状态送达)
try:
    while True:
        status_msg = f"{light_status['switch']},{light_status['brightness']}"
        client.publish("home/light/status", payload=status_msg, qos=1)
        print(f"发布灯状态:{status_msg}")
        time.sleep(5)  # 每5秒发布一次
except KeyboardInterrupt:
    print("\n智能灯断开连接")
    client.loop_stop()
    client.disconnect()

2. 手机APP(Subscriber):phone_subscriber.py

核心逻辑:订阅状态Topic接收灯的状态,手动输入指令发布控制消息。

import paho.mqtt.client as mqtt
import time

# MQTT Broker配置
BROKER_HOST = "localhost"
BROKER_PORT = 1883
CLIENT_ID = "phone_app_001"  # 手机APP唯一标识

# 连接Broker成功的回调
def on_connect(client, userdata, flags, rc):
    print(f"手机APP连接网关Broker成功,返回码:{rc}")
    # 订阅灯状态Topic(QoS 1)
    client.subscribe("home/light/status", qos=1)

# 接收灯状态的回调
def on_message(client, userdata, msg):
    status = msg.payload.decode("utf-8")
    switch, brightness = status.split(",")
    print(f"\n【灯状态更新】开关:{switch},亮度:{brightness}%")

# 初始化客户端
client = mqtt.Client(client_id=CLIENT_ID, clean_session=False)
client.on_connect = on_connect
client.on_message = on_message

# 连接Broker
client.connect(BROKER_HOST, BROKER_PORT, keepalive=60)

# 启动客户端循环
client.loop_start()

# 手动输入控制指令
try:
    while True:
        print("\n===== 控制指令 =====")
        print("输入 off/on 控制开关,输入 brightness:数值 调节亮度(如brightness:50)")
        cmd = input("请输入指令(回车发送):")
        if cmd:
            # 发布控制指令(QoS 1)
            client.publish("home/light/control", payload=cmd, qos=1)
            print(f"已发送指令:{cmd}")
        time.sleep(0.5)
except KeyboardInterrupt:
    print("\n手机APP断开连接")
    client.loop_stop()
    client.disconnect()

3. 网关Broker:启动Mosquitto

无需代码,直接启动Mosquitto服务(模拟网关内置Broker):

# Windows
mosquitto.exe -c mosquitto.conf  # 或直接net start mosquitto
# Linux
sudo systemctl start mosquitto

默认配置即可,无需额外修改(家用网关内置Broker会默认开启1883端口、基础权限)。

三、交互时序图(Mermaid语法)

以下是智能灯发布状态 + 手机APP接收状态 + 手机发布控制指令 + 智能灯执行 的完整时序(以QoS 1为例,保证消息至少一次送达):

sequenceDiagram participant 智能灯(Light) participant 网关(Broker) participant 手机APP(Phone) %% 第一步:连接Broker Note over 智能灯(Light),网关(Broker): 阶段1:建立连接 智能灯(Light)->>网关(Broker): CONNECT(客户端ID:smart_light_001,QoS=1) 网关(Broker)-->>智能灯(Light): CONNACK(连接确认) 智能灯(Light)->>网关(Broker): SUBSCRIBE(订阅home/light/control,QoS=1) 网关(Broker)-->>智能灯(Light): SUBACK(订阅确认) 手机APP(Phone)->>网关(Broker): CONNECT(客户端ID:phone_app_001,QoS=1) 网关(Broker)-->>手机APP(Phone): CONNACK(连接确认) 手机APP(Phone)->>网关(Broker): SUBSCRIBE(订阅home/light/status,QoS=1) 网关(Broker)-->>手机APP(Phone): SUBACK(订阅确认) %% 第二步:智能灯发布状态 Note over 智能灯(Light),网关(Broker): 阶段2:发布灯状态 智能灯(Light)->>网关(Broker): PUBLISH(Topic:home/light/status,消息:on,80,QoS=1,MsgID=1) 网关(Broker)-->>智能灯(Light): PUBACK(确认接收,MsgID=1) 网关(Broker)->>手机APP(Phone): PUBLISH(转发状态消息,MsgID=1) 手机APP(Phone)-->>网关(Broker): PUBACK(确认接收,MsgID=1) %% 第三步:手机发布控制指令 Note over 手机APP(Phone),网关(Broker): 阶段3:发布控制指令 手机APP(Phone)->>网关(Broker): PUBLISH(Topic:home/light/control,消息:brightness:50,QoS=1,MsgID=2) 网关(Broker)-->>手机APP(Phone): PUBACK(确认接收,MsgID=2) 网关(Broker)->>智能灯(Light): PUBLISH(转发控制指令,MsgID=2) 智能灯(Light)-->>网关(Broker): PUBACK(确认接收,MsgID=2) %% 第四步:智能灯更新状态并重新发布 Note over 智能灯(Light),网关(Broker): 阶段4:更新并发布新状态 智能灯(Light)->>网关(Broker): PUBLISH(Topic:home/light/status,消息:on,50,QoS=1,MsgID=3) 网关(Broker)-->>智能灯(Light): PUBACK(确认接收,MsgID=3) 网关(Broker)->>手机APP(Phone): PUBLISH(转发新状态,MsgID=3) 手机APP(Phone)-->>网关(Broker): PUBACK(确认接收,MsgID=3) %% 断开连接(可选) Note over 智能灯(Light),网关(Broker): 阶段5:断开连接(手动触发) 智能灯(Light)->>网关(Broker): DISCONNECT 手机APP(Phone)->>网关(Broker): DISCONNECT

四、运行说明

  1. 先启动Mosquitto(网关Broker);
  2. 运行light_publisher.py(智能灯上线,开始发布状态);
  3. 运行phone_subscriber.py(手机APP上线,接收状态,可输入指令控制);
  4. 效果:
    • 手机APP实时接收灯的状态;
    • 输入off,智能灯关闭,状态更新为off,0并发布,手机同步接收;
    • 输入brightness:50,智能灯亮度改为50,状态同步到手机。

五、关键细节说明

  1. QoS 1的核心体现:每个PUBLISH消息都有PUBACK确认,确保消息至少送达一次;
  2. 角色复用:智能灯既是Publisher(发布状态)也是Subscriber(订阅控制),手机同理;
  3. 网关Broker的作用
    • 验证客户端连接身份(示例简化了认证,实际网关会有设备白名单/密钥);
    • 路由消息(状态消息从智能灯→Broker→手机,控制指令从手机→Broker→智能灯);
    • 缓存消息(若智能灯离线,Broker会缓存控制指令,重连后推送)。

报文详解

一、MQTT报文基础结构(先铺垫核心规则)

MQTT协议基于二进制报文通信,所有交互报文由三部分组成,这是解析所有报文的基础:

报文部分 作用 核心字段
固定头(必选) 标识报文类型、QoS、重传标志(Dup)、保留标志(Retain),占2字节起步 第1字节:报文类型(4位) + Dup(1位) + QoS(2位) + Retain(1位)
第2字节:剩余长度(后续字节数,可变)
可变头(可选) 随报文类型变化,存储协议版本、Topic、消息ID(MsgID)等核心元数据 如CONNECT含协议名/版本,PUBLISH含Topic+MsgID(QoS≥1时)
有效载荷(可选) 实际数据,如客户端ID、消息内容、订阅Topic列表等 如PUBLISH的有效载荷是“on,80”,CONNECT的有效载荷是客户端ID+密码

关键编码规则

  • 报文类型编码(4位):CONNECT=1、CONNACK=2、PUBLISH=3、PUBACK=4、SUBSCRIBE=8、SUBACK=9、DISCONNECT=14;
  • 剩余长度:采用“可变长度编码”(1~4字节),表示“可变头+有效载荷”的总字节数;
  • MsgID:仅QoS≥1的PUBLISH/PUBACK/SUBSCRIBE等报文携带(16位无符号整数,0~65535),用于标识消息、实现重传/确认。

二、完整交互报文详解(基于代码场景:智能灯+网关Broker+手机APP)

以下所有报文均对应代码中的实际交互(Broker地址localhost:1883,智能灯ClientID=smart_light_001,手机ClientID=phone_app_001,QoS=1),按“连接→订阅→发布/接收→断开”流程拆解。

阶段1:客户端连接Broker(CONNECT/CONNACK)

1.1 智能灯→Broker:CONNECT报文(发起连接)
# 代码对应:client.connect(BROKER_HOST, BROKER_PORT, keepalive=60)

报文二进制解析(示例)

部分 二进制(十六进制) 字段解析
固定头 0x10 0x27 0x10 = 报文类型(0001=CONNECT) + Dup(0) + QoS(00) + Retain(0);
0x27 = 剩余长度(39字节,即后续可变头+有效载荷共39字节)
可变头 0x00 0x04 4D 51 54 54 0x04 0x02 0x00 0x3C 0x00 0x04 4D515454:协议名长度(4) + “MQTT”;
0x04:MQTT版本(4=MQTT 3.1.1);
0x02:连接标志(清理会话=0+遗嘱=0+用户名=0+密码=0+保活时间=1);
0x00 0x3C:保活时间(60秒)
有效载荷 0x00 0x0B 73 6D 61 72 74 5F 6C 69 67 68 74 5F 30 30 31 0x00 0x0B:客户端ID长度(11);
736D6172745F6C696768745F303031:“smart_light_001”(ASCII编码)

核心含义:智能灯向Broker发起连接,声明使用MQTT 3.1.1协议、保活时间60秒、客户端ID为smart_light_001(代码中clean_session=False,对应连接标志的“清理会话位=0”,Broker会保留订阅关系)。

1.2 Broker→智能灯:CONNACK报文(连接确认)

报文二进制解析(示例)

部分 二进制(十六进制) 字段解析
固定头 0x20 0x02 0x20 = 报文类型(0010=CONNACK) + Dup(0) + QoS(00) + Retain(0);
0x02 = 剩余长度(2字节)
可变头 0x00 0x00 0x00:连接确认标志(无会话保留);
0x00:返回码(0=连接成功,其他值为失败,如1=协议版本不支持)
有效载荷 -

核心含义:Broker确认智能灯连接成功(返回码0),手机APP连接Broker的CONNACK报文结构完全一致,仅客户端ID不同。

阶段2:客户端订阅Topic(SUBSCRIBE/SUBACK)

2.1 智能灯→Broker:SUBSCRIBE报文(订阅控制指令Topic)
# 代码对应:client.subscribe("home/light/control", qos=1)

报文二进制解析(示例)

部分 二进制(十六进制) 字段解析
固定头 0x82 0x13 0x82 = 报文类型(1000=SUBSCRIBE) + Dup(0) + QoS(01=1) + Retain(0);
0x13 = 剩余长度(19字节)
可变头 0x00 0x01 0x00 0x01:MsgID(1,SUBSCRIBE必须带MsgID,用于匹配SUBACK)
有效载荷 0x00 0x10 68 6F 6D 65 2F 6C 69 67 68 74 2F 63 6F 6E 74 72 6F 6C 0x01 0x00 0x10:Topic长度(16);
686F6D652F6C696768742F636F6E74726F6C:“home/light/control”;
0x01:订阅QoS(1)
2.2 Broker→智能灯:SUBACK报文(订阅确认)

报文二进制解析(示例)

部分 二进制(十六进制) 字段解析
固定头 0x90 0x03 0x90 = 报文类型(1001=SUBACK) + Dup(0) + QoS(00) + Retain(0);
0x03 = 剩余长度(3字节)
可变头 0x00 0x01 0x00 0x01:MsgID(与SUBSCRIBE的MsgID一致,1)
有效载荷 0x01 0x01:订阅结果(Broker确认的QoS等级,1=与请求一致)

核心含义:Broker确认智能灯成功订阅home/light/control(QoS=1);手机APP订阅home/light/status的SUBSCRIBE/SUBACK报文结构完全一致,仅Topic和MsgID不同。

阶段3:发布/接收消息(PUBLISH/PUBACK,QoS=1)

3.1 智能灯→Broker:发布灯状态PUBLISH报文
# 代码对应:client.publish("home/light/status", payload="on,80", qos=1)

报文二进制解析(示例)

部分 二进制(十六进制) 字段解析
固定头 0x32 0x0F 0x32 = 报文类型(0011=PUBLISH) + Dup(0) + QoS(01=1) + Retain(0);
0x0F = 剩余长度(15字节)
可变头 0x00 0x0E 68 6F 6D 65 2F 6C 69 67 68 74 2F 73 74 61 74 75 73 0x00 0x02 0x00 0x0E:Topic长度(14);
686F6D652F6C696768742F737461747573:“home/light/status”;
0x00 0x02:MsgID(2,QoS≥1必须带)
有效载荷 6F 6E 2C 38 30 6F6E2C3830:“on,80”(ASCII编码)
3.2 Broker→智能灯:PUBACK报文(确认接收状态消息)

报文二进制解析(示例)

部分 二进制(十六进制) 字段解析
固定头 0x40 0x02 0x40 = 报文类型(0100=PUBACK) + Dup(0) + QoS(00) + Retain(0);
0x02 = 剩余长度(2字节)
可变头 0x00 0x02 0x00 0x02:MsgID(与PUBLISH的MsgID一致,2)
有效载荷 -
3.3 Broker→手机APP:转发状态PUBLISH报文

Broker收到智能灯的状态消息后,向订阅了home/light/status的手机APP转发PUBLISH报文,结构与3.1完全一致(仅Dup/MsgID可能重新分配,示例MsgID=3):

部分 二进制(十六进制) 字段解析
固定头 0x32 0x0F 0x32 = PUBLISH + Dup(0) + QoS(1) + Retain(0);
0x0F = 剩余长度15
可变头 0x00 0x0E + Topic + 0x00 0x03 Topic=home/light/status,MsgID=3
有效载荷 6F 6E 2C 38 30 “on,80”
3.4 手机APP→Broker:PUBACK报文(确认接收状态)

与3.2结构一致,仅MsgID=3:

部分 二进制(十六进制) 字段解析
固定头 0x40 0x02 PUBACK + 剩余长度2
可变头 0x00 0x03 MsgID=3(匹配转发的PUBLISH)
3.5 手机APP→Broker:发布控制指令PUBLISH报文(如“brightness:50”)
# 代码对应:client.publish("home/light/control", payload="brightness:50", qos=1)

报文二进制解析(示例)

部分 二进制(十六进制) 字段解析
固定头 0x32 0x16 0x32 = PUBLISH + Dup(0) + QoS(1) + Retain(0);
0x16 = 剩余长度22
可变头 0x00 0x10 + Topic + 0x00 0x04 Topic=home/light/control,MsgID=4
有效载荷 62 72 69 67 68 74 6E 65 73 73 3A 35 30 “brightness:50”(ASCII编码)
3.6 Broker→智能灯:转发控制指令PUBLISH报文

结构与3.5一致(MsgID=5),智能灯接收后返回PUBACK(MsgID=5),完成指令交付。

阶段4:断开连接(DISCONNECT)

# 代码对应:client.disconnect()
4.1 智能灯→Broker:DISCONNECT报文

报文二进制解析(示例)

部分 二进制(十六进制) 字段解析
固定头 0xE0 0x00 0xE0 = 报文类型(1110=DISCONNECT) + Dup(0) + QoS(00) + Retain(0);
0x00 = 剩余长度0(无可变头/有效载荷)
可变头 -
有效载荷 -

核心含义:智能灯主动向Broker发送断开连接指令,Broker收到后释放连接资源;手机APP的DISCONNECT报文结构完全一致。

三、关键报文规则补充(结合代码场景)

  1. QoS=1的核心保障:所有PUBLISH报文均带MsgID,且必须收到对应PUBACK才会终止重传(代码中若Broker未返回PUBACK,智能灯会超时重传,此时Dup标志置1);
  2. Retain标志:代码中publish未设置retain=True,所以固定头的Retain位为0(若置1,Broker会保留该Topic的最后一条消息,新订阅者上线直接收到);
  3. 剩余长度编码:若“可变头+有效载荷”超过127字节,剩余长度会用多字节编码(如128字节=0x80 0x01),代码中消息内容短,仅用1字节即可;
  4. Dup标志:仅重传时置1(如智能灯发布消息后超时未收到PUBACK,重传PUBLISH时Dup=1,MsgID不变)。

四、报文交互核心逻辑总结

智能灯→Broker:CONNECT → Broker→智能灯:CONNACK
智能灯→Broker:SUBSCRIBE → Broker→智能灯:SUBACK
手机APP→Broker:CONNECT → Broker→手机APP:CONNACK
手机APP→Broker:SUBSCRIBE → Broker→手机APP:SUBACK
智能灯→Broker:PUBLISH(状态) → Broker→智能灯:PUBACK
Broker→手机APP:PUBLISH(状态) → 手机APP→Broker:PUBACK
手机APP→Broker:PUBLISH(指令) → Broker→手机APP:PUBACK
Broker→智能灯:PUBLISH(指令) → 智能灯→Broker:PUBACK
智能灯→Broker:DISCONNECT
手机APP→Broker:DISCONNECT

所有报文均遵循“固定头+可变头+有效载荷”结构,QoS=1通过“PUBLISH+PUBACK”两次交互保证消息至少送达一次,这也是代码中能稳定收发消息的核心原因。

posted @ 2025-11-30 14:16  Asp1rant  阅读(0)  评论(0)    收藏  举报