学习笔记——MQTT协议

MQTT协议详解笔记

一、MQTT概述

1.1 什么是MQTT

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议)是一种基于发布/订阅模式的轻量级通讯协议,构建于TCP/IP协议之上。

1.2 主要特点

  • 轻量级:协议开销小,头部最小2字节

  • 低带宽:适合网络条件差的场景

  • 简单开放:易于实现和部署

  • 可靠传输:支持三种服务质量等级

1.3 应用场景

  • 物联网(IoT):传感器数据采集

  • 移动应用:推送消息

  • M2M通信:设备间通信

  • 受限环境:低功耗、低带宽设备

1.4 协议版本

  • MQTT 3.1.1:当前标准版本(2014年发布)

  • MQTT-SN:针对非TCP/IP网络的简化版

二、MQTT核心概念

2.1 三种角色

角色 描述 功能
发布者(Publisher) 发送消息的客户端 发布消息到特定主题
代理(Broker/Server) 消息中转服务器 接收、存储、转发消息
订阅者(Subscriber) 接收消息的客户端 订阅感兴趣的主题

2.2 消息结构

复制代码
┌─────────────────────────────────┐
│       固定头(Fixed Header)      │ ← 必选,2字节最小
├─────────────────────────────────┤
│       可变头(Variable Header)    │ ← 可选,因报文类型而异
├─────────────────────────────────┤
│       有效载荷(Payload)         │ ← 可选,消息内容
└─────────────────────────────────┘

2.3 关键术语

术语 描述 备注
会话(Session) 客户端和服务端之间的状态交互 可跨越多个网络连接
客户端标识(ClientID) 客户端的唯一标识符 1-23字符,只能包含字母数字
主题(Topic) 消息的标签/分类 UTF-8字符串,区分大小写
主题过滤器(Topic Filter) 订阅时使用的表达式 支持通配符
负载(Payload) 消息的实际内容 最大256MB

三、MQTT报文类型(14种)

3.1 连接相关

报文类型 方向 描述
CONNECT 1 客户端→服务端 建立连接
CONNACK 2 服务端→客户端 连接确认

3.2 发布/订阅相关

报文类型 方向 描述
PUBLISH 3 双向 发布消息
PUBACK 4 双向 QoS 1发布确认
PUBREC 5 双向 QoS 2第一步确认
PUBREL 6 双向 QoS 2第二步确认
PUBCOMP 7 双向 QoS 2第三步确认
SUBSCRIBE 8 客户端→服务端 订阅请求
SUBACK 9 服务端→客户端 订阅确认
UNSUBSCRIBE 10 客户端→服务端 取消订阅
UNSUBACK 11 服务端→客户端 取消订阅确认

3.3 连接维护

报文类型 方向 描述
PINGREQ 12 客户端→服务端 心跳请求
PINGRESP 13 服务端→客户端 心跳响应
DISCONNECT 14 客户端→服务端 断开连接

四、服务质量(QoS)等级

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

  • 特点:不确认,可能丢失

  • 应用场景:环境传感器数据,丢失不影响

  • 传输次数:0或1次

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

  • 特点:确认机制,可能重复

  • 存储要求:发送者需持久化

  • 应用场景:重要但不严格要求唯一的数据

4.3 QoS 2:恰好一次(Exactly Once)

  • 特点:四步握手,确保唯一

  • 存储要求:双方都需持久化

  • 应用场景:计费系统,支付等关键业务

五、会话管理与连接控制

5.1 CleanSession标志

含义 会话状态
0 恢复会话 保留会话状态,重连后恢复
1 清理会话 新建会话,不保留状态

5.2 会话状态内容

客户端会话状态
  • 已发送未确认的QoS 1/2消息

  • 已接收未确认的QoS 2消息

服务端会话状态
  • 会话存在标志

  • 客户端订阅信息

  • 已发送未确认的QoS 1/2消息

  • 待发送的QoS 1/2消息

  • 已接收未确认的QoS 2消息

  • (可选)待发送的QoS 0消息

5.3 遗嘱机制(Last Will & Testament)

复制代码
// CONNECT报文中的遗嘱相关字段
Will Flag = 1;          // 启用遗嘱
Will QoS = 0/1/2;       // 遗嘱消息QoS
Will Retain = 0/1;      // 是否保留遗嘱消息
Will Topic = "topic";   // 遗嘱主题
Will Message = "msg";   // 遗嘱消息内容

触发条件

  1. 网络I/O错误或故障

  2. Keep Alive超时

  3. 客户端非正常断开

  4. 协议错误导致连接关闭

5.4 心跳机制(Keep Alive)

  • 作用:维持长连接,检测对方存活

  • 超时:服务端在1.5×KeepAlive时间内未收到报文则断开

  • 范围:0-65535秒(0表示关闭)

  • 最大值:18小时12分15秒

六、主题与订阅机制

6.1 主题命名规则

规则 说明
字符集 UTF-8编码,不能包含空字符
长度 最长65535字节
层级 使用"/"分隔,支持任意层级
区分大小写 "A"和"a"是不同的主题
允许空格 可以包含空格字符

6.2 通配符

6.2.1 单层通配符(+)
复制代码
有效:
  sport/tennis/+    匹配 sport/tennis/player1, sport/tennis/player2
  +/tennis/player1  匹配 any/tennis/player1
  +                 匹配单层级主题
  
无效:
  sport+            通配符必须占据整个层级
6.2.2 多层通配符(#)
复制代码
有效:
  sport/tennis/#    匹配 sport/tennis, sport/tennis/player1, sport/tennis/player1/score
  sport/#           匹配 sport 及所有子层级
  #                 匹配所有主题(应谨慎使用)
  
无效:
  sport/tennis#     通配符前必须有"/"
  sport/#/ranking   通配符必须是最后字符
6.2.3 特殊主题($开头)
复制代码
规则:
  - $开头的主题不能匹配通配符(#或+)开头的过滤器
  - 订阅"#"不会收到$开头的消息
  - 订阅"$SYS/#"可接收系统主题
  - $SYS/ 通常用于服务器监控和管理

6.3 订阅与QoS降级

复制代码
// SUBSCRIBE报文结构
主题过滤器1 + QoS请求1
主题过滤器2 + QoS请求2
...
主题过滤器N + QoS请求N

// SUBACK返回码
0x00 - 最大QoS 0授予成功
0x01 - 最大QoS 1授予成功
0x02 - 最大QoS 2授予成功
0x80 - 订阅失败

QoS降级规则

  • 服务端可能授予比请求低的QoS等级

  • 实际消息QoS = min(发布QoS, 授予QoS)

  • 高QoS消息降级后可能重复

七、报文标识与重传机制

7.1 报文标识符(Packet Identifier)

报文类型 是否需要Packet ID 说明
PUBLISH (QoS>0) 非零16位整数
PUBACK/PUBREC 与对应PUBLISH相同
PUBREL/PUBCOMP 与对应PUBLISH相同
SUBSCRIBE/SUBACK 匹配对应请求
UNSUBSCRIBE/UNSUBACK 匹配对应请求

7.2 重传规则

  1. 重传条件:在相同Session内等待确认超时

  2. DUP标志:重传时设置为1

  3. Packet ID重用:确认完成后可重用

  4. 顺序保证:必须按原始顺序重传

7.3 消息排序

复制代码
QoS 1可能顺序:1, 2, 3, 2, 3, 4  # 可能重复导致乱序
QoS 2保证顺序:1, 2, 3, 4         # 严格保证有序无重复

控制措施:
  - 设置传输窗口(in-flight window)=1可避免QoS 1乱序
  - QoS 2天然保证有序

八、保留消息(Retain)

8.1 Retain标志作用

Retain值 服务端行为 订阅者影响
0 不存储,不替换现有保留消息 仅当前订阅者收到
1 存储消息和QoS,替换旧消息 新订阅者立即收到

8.2 保留消息特性

  1. 单主题单消息:每个主题最多一个保留消息

  2. 零字节清理:发送零字节保留消息可清除

  3. 存储限制:服务端内存紧张时可丢弃

  4. 遗嘱保留:Will Retain标志同理

九、安全机制

9.1 传输层安全

端口 协议 加密 说明
1883 TCP 标准MQTT端口
8883 TLS 推荐生产环境使用
8080 WebSocket WebSocket连接
8081 WebSocket+TLS 安全WebSocket

9.2 认证方式

9.2.1 用户名/密码认证
复制代码
// CONNECT报文中的认证字段
UserName Flag = 1;      // 启用用户名
Password Flag = 1;      // 启用密码
UserName = "user";      // UTF-8字符串
Password = "pass";      // 二进制数据
9.2.2 其他认证方式
  • TLS客户端证书:双向认证

  • 外部系统:LDAP、OAuth、操作系统认证

  • 应用层认证:通过消息传递凭证

9.3 安全风险与防护

风险类型 防护措施
中间人攻击 TLS加密,VPN隧道
重放攻击 时间戳,序列号,TLS
拒绝服务 频率限制,黑名单
数据泄露 静态数据加密
身份伪造 强身份验证

9.4 异常行为检测

复制代码
监控指标:
  - 频繁连接/认证请求
  - 异常断开连接
  - 主题扫描行为
  - 发送无订阅者消息
  - 连接后无数据传输

应对策略:
  - 动态黑名单(IP/ClientID)
  - 连接速率限制
  - 异常断开报警

十、协议实现要点

10.1 网络传输要求

要求 说明
有序 报文必须按发送顺序到达
可靠 不丢失,不损坏
双向 客户端↔服务端双向通信
字节流 基于字节流而非消息

支持协议:TCP、TLS、WebSocket(不支持UDP)

10.2 连接处理流程

10.3 CONNACK返回码

返回码 描述 处理方式
0x00 连接已接受 正常继续
0x01 不支持的协议版本 断开连接
0x02 标识符不合格 断开连接
0x03 服务端不可用 断开连接
0x04 用户名或密码错误 断开连接
0x05 未授权 断开连接

十一、最佳实践建议

11.1 客户端设计

  1. ClientID管理:固定ClientID以支持会话恢复

  2. CleanSession选择:移动设备用1,关键业务用0

  3. KeepAlive设置:根据网络状况调整(建议1-5分钟)

  4. 遗嘱机制:重要设备启用,用于异常检测

11.2 服务端设计

  1. 主题规划:使用层级结构,避免过度使用通配符

  2. 保留消息:谨慎使用,定期清理

  3. 安全策略:强制TLS,实施认证授权

  4. 监控告警:监控连接数、消息速率、异常行为

11.3 消息设计

  1. QoS选择:数据重要性 vs 性能开销

  2. 主题设计:语义清晰,避免歧义

  3. 负载格式:使用JSON等标准格式

  4. 消息大小:控制单消息大小,避免阻塞

11.4 错误处理

复制代码
// 协议违规处理原则
if (收到非法报文) {
    关闭网络连接;  // 根据规范必须断开
    记录日志;
    告警通知;
}

// 瞬时错误处理
if (处理报文时发生瞬时错误) {
    关闭连接;     // 如缓冲区满等
    避免影响其他客户端;
}

十二、常见问题与解决方案

12.1 连接问题

问题 可能原因 解决方案
频繁重连 KeepAlive设置过短 适当延长KeepAlive
连接被拒绝 ClientID冲突 使用唯一ClientID
认证失败 凭证错误 检查用户名密码

12.2 消息问题

问题 可能原因 解决方案
消息丢失 QoS=0网络差 升级为QoS 1或2
消息重复 QoS=1网络不稳定 应用层去重,或使用QoS 2
接收延迟 服务端负载高 优化服务端,负载均衡

12.3 性能问题

问题 可能原因 解决方案
高CPU使用 频繁发布订阅 合并消息,减少频率
内存增长 保留消息积累 定期清理,限制数量
网络拥塞 消息体过大 压缩,分片

附录:协议一致性要求速查表

项目 客户端要求 服务端要求
报文格式 必须符合规范 必须符合规范
QoS支持 必须支持所有QoS等级 必须支持所有QoS等级
主题匹配 必须支持通配符 必须按规范匹配
会话管理 必须支持CleanSession 必须支持会话状态
安全传输 推荐支持TLS 必须支持有序可靠传输
错误处理 必须关闭非法连接 必须关闭非法连接
相关推荐
vx-程序开发4 分钟前
springboot在线装修管理系统-计算机毕业设计源码56278
java·c语言·spring boot·python·spring·django·php
Dxy12393102167 分钟前
js如何把字符串转数字
开发语言·前端·javascript
幽络源小助理11 分钟前
网页软件库源码(带1153条资源)-含详细搭建教程
php
_饭团25 分钟前
字符串函数全解析:12 种核心函数的使用与底层模拟实现
c语言·开发语言·学习·考研·面试·蓝桥杯
Larry_Yanan26 分钟前
Qt网络开发之基于 QWebEngine 实现简易内嵌浏览器
linux·开发语言·网络·c++·笔记·qt·学习
2401_8318249632 分钟前
嵌入式C++驱动开发
开发语言·c++·算法
qingcyb39 分钟前
重复 id 对应的多个对象
开发语言·python
li星野1 小时前
[特殊字符] 模拟试卷一:C++核心与系统基础(90分钟)答案版
开发语言·c++·算法
天下无贼!1 小时前
【Python】2026版——FastAPI 框架快速搭建后端服务
开发语言·前端·后端·python·aigc·fastapi
Irissgwe1 小时前
c++特殊类设计
java·开发语言·c++