跳到主要内容

打开导航

返回博客

MQTT 3.1.1 与 MQTT 5.0 有什么区别:用 Mqttable 逐项配置与验证

从 Mqttable 新建客户端界面逐项理解会话、配额、遗嘱、认证和用户属性,再通过发布、订阅及隔离实验解释 MQTT 5 为什么增加这些能力。

作者:
Mqttable Editorial
发布:
手绘图对比 MQTT 3.1.1 与 MQTT 5.0:两侧设备连接同一个 Broker,MQTT 5 消息附带过期、接收配额和请求响应控制。

同一个 Broker、同一个端口,把客户端的 MQTT 版本从 3.1.1 切到 5.0,连接不一定变得更快,却会多出一组能明确表达意图的设置:断线后会话保留多久、消息什么时候失效、接收方允许多少未确认消息,以及短暂断线是否应该立即通知其他设备。

本文中的 V3 主要指 MQTT 3.1.1,V5 指 MQTT 5.0。Mqttable 还提供 3.1,但它只用于明确需要旧协议的系统。应用要使用哪个版本,必须由客户端、Broker 和接入策略共同支持;TCP、TLS、WebSocket 和端口号本身不决定版本。

版本差异速查

左右滚动表格,查看其余列。

问题MQTT 3.1.1MQTT 5.0为什么需要改变
这次是否用旧会话,断线后保留多久?一个 Clean Session 开关一起控制Clean Start 与 Session Expiry 分开新建干净会话,同时允许之后恢复;保留时间可明确设定
断线期间积压的命令是否还有效?没有协议级消息过期属性Message Expiry Interval避免过时命令在设备恢复后才被执行
短暂网络抖动是否立即触发遗嘱?没有 Will Delay 属性Will Delay Interval为会话恢复留出窗口,减少短暂掉线通知
小设备如何表达接收能力?依赖本地窗口及 Broker 策略Receive Maximum、Maximum Packet Size在协议中声明未确认消息窗口和最大报文
重复发送长 Topic 是否浪费带宽?每个 PUBLISH 携带 Topic 名称Topic Alias一条连接内用短整数代替已建立映射的 Topic
失败原因从哪里看?CONNACK 返回码和 SUBACK 等有限反馈更完整的原因码、可选 Reason String、服务端 DISCONNECT区分失败位置,减少只看到连接关闭的猜测
怎样关联请求和响应?Topic 与 payload 自行约定Response Topic、Correlation Data让路由与关联信息不必塞进业务 payload
怎样控制订阅回送和保留消息?没有这些标准订阅选项No Local、RAP、RH、Subscription Identifier支持桥接、保留消息控制和订阅归属识别
能否多轮认证?用户名/密码及传输层机制Authentication Method/Data 与 AUTH支持挑战响应和连接内重新认证

这些新增能力不改变发布/订阅模型。Topic、QoS、Retain、用户名/密码和基础遗嘱,在 3.1.1 中就有。MQTT 5 标准附录 C 介绍了新增能力及部分用途;下文的设备、桥接和命令例子是工程场景分析,不是对标准制定过程的历史描述。

New Client:身份、版本与凭据

在 Connections 中选择 Broker,再打开 New Client/Add client。Broker 行显示这个客户端归属的连接端点。Host、Port、传输协议、TLS 和证书配置属于 Broker 设置,不是 MQTT 5 新属性。本文实验端点只绑定本机 loopback;真实接入请使用组织提供的端点与可信凭据。

Mqttable 中文新建客户端:恢复会话要保持同一 ID;已选择 3.1.1;心跳单位为秒;V3 使用 Clean Session。

1,恢复会话要保持同一 ID;2,已选择 3.1.1;3,心跳单位为秒;4,V3 使用 Clean Session。

  1. 恢复会话要保持同一 ID
  2. 已选择 3.1.1
  3. 心跳单位为秒
  4. V3 使用 Clean Session
Mqttable 中文新建客户端:名称不作为 MQTT 身份发送;恢复会话要保持同一 ID;凭据不等于传输加密;版本与端口分开;匹配 Listener 认证策略。

1,名称不作为 MQTT 身份发送;2,恢复会话要保持同一 ID;3,凭据不等于传输加密;4,版本与端口分开;5,匹配 Listener 认证策略。

  1. 名称不作为 MQTT 身份发送
  2. 恢复会话要保持同一 ID
  3. 凭据不等于传输加密
  4. 版本与端口分开
  5. 匹配 Listener 认证策略

Name、Client ID 与生成按钮

左右滚动表格,查看其余列。

设置含义与填写方法两个版本的关系
NameMqttable 中的显示名称,最多 255 个 UTF-8 字节。可以叫“温度采集器”,它不会作为 MQTT Client ID 发给 Broker工具功能,两版相同
Generate a new name生成新的显示名称,不会替你创建 Broker 账号或更换已经连接的设备身份工具功能
Client IDCONNECT 中的客户端身份。当前表单接受受限长度的 UTF-8 字符串,最终还受 Broker 的长度和字符策略约束两版共有;不能把工具输入上限当成所有 Broker 都支持的长度
Generate Client ID为新实验生成身份,减少撞号;需要恢复旧会话时不要重新生成工具功能

同一个 Broker 上两个连接使用同一个 Client ID,后来的连接可能接管前一个连接。会话恢复要求 Client ID 稳定,仅把 Clean Start 关掉而每次生成新 ID,仍然找不到旧会话。MQTT 5 还允许 Broker 返回 Assigned Client Identifier,见后面的能力补充;这不等于当前表单的生成按钮使用了该机制。

MQTT 版本与 Authentication Method

左右滚动表格,查看其余列。

选项实际用途使用条件及误区
5.0使用 MQTT 5 的属性、原因码和订阅选项Broker Listener 必须接受 5.0;没有一个单独的“V5 端口”
3.1.1与大量既有设备和客户端库互通CONNECT Protocol Level 为 4,不要把界面版本号当作这个字段的数值
3.1兼容明确要求旧协议的系统Protocol Level 为 3;本文不另做旧协议教程
None不发送密码Broker 可能仍按用户名、证书、网络身份或其他策略认证;不是强制让 Broker 允许匿名
Password发送 Broker 要求的用户名/密码MQTT 3.1.1 已支持;密码不会自动使明文 TCP 加密
JWT在密码字段中使用令牌JWT 由 Broker 验证,通常仍经普通 CONNECT Password 传输,不是 MQTT 5 专有机制
SCRAM-SHA-1/256/512在 MQTT 5 AUTH 交互中完成挑战响应两端必须支持同一个方法;本轮实测 SHA-256,其他两项是可选方法说明,不宣称都已实测

当前 Mqttable 在 5.0 下展示认证方法选择器。3.x 隐藏它,但仍有用户名和密码字段。协议层面,在 Broker 支持时,3.1.1 也可以在密码字段中传 JWT;不能因为 JWT 工具位于 V5 表单就称它为 V5 新功能。

MQTT 5 新增的是增强认证框架,包括方法、认证数据和 AUTH 报文。挑战响应不必直接把原始密码作为 CONNECT Password 发出,连接内也可以按协议进行重新认证。SCRAM 不替代 TLS,也不保证当前 Mqttable 有主动重新认证按钮。认证方式指南 说明了产品的凭据入口;EMQX 的 SCRAM 文档 给出了 Broker 配置要求。

认证说明区分无密码、普通密码、JWT 与 MQTT 5 SCRAM;编号指向实际规则。

密码和 JWT 不是 V5 专属功能;SCRAM 方法需要两端匹配。

  1. 按 Broker 策略选择
  2. 不发送密码
  3. 普通用户名与密码
  4. 密码栏传递令牌
  5. MQTT 5 多轮认证

Username、Password 与 Saved SecretRef

Username 和 Password 初始为空;若选择 Password 或 SCRAM,按接入策略填写。None 不发送密码,已有保存值也不能理解为仍在被发送。密码字段当前受 MQTT 二字节长度限制,最多 65,535 个字节;这不是推荐的密码长度,更不覆盖服务商自己的限制。

  • Raw password,直接填写本次凭据。显示/隐藏按钮只改变可见性,不改变认证方式、网络加密或权限。
  • Saved SecretRef,选择 Credentials 中已保存的 MQTT 密码引用。引用本身不是发给 Broker 的密码,也不是 MQTT 5 属性;连接时 Runtime 解析引用。引用不存在或不可用时应修复凭据,而不是把引用字符串填成密码。
  • Key/Value 与增删按钮,在 CONNECT User Properties 中使用;它们不是密码的额外字段,下一节单独说明。

本文只使用合成实验凭据,截图不显示密码、签名密钥或有效令牌原文。不要把生产凭据放进 User Properties、Topic、payload、截图或文章。

Mqttable 中文密码来源选择 Saved SecretRef,并选中本轮 Credentials 界面创建的实验密码引用。

引用由本地 Runtime 解析,不是 MQTT 属性或发给 Broker 的密码文本。截图不显示密码。

  1. 直接填写本次凭据
  2. 复用已保存的密码引用
  3. 选中任务专属实验凭据
  4. 认证方式与来源分开

JWT Tools:Algorithm、密钥、Claims、Encode 与 Preview

JWT Tools 是 Mqttable 的辅助工具,以下设置都不是 MQTT 报文字段。

左右滚动表格,查看其余列。

工具设置含义和使用方法
Algorithm当前提供 HS256 与 RS256,必须与 Broker 验证策略一致;不能仅凭工具生成成功判断 Broker 接受
SecretHS256 使用共享签名密钥,双方需要匹配;不是自动生成的 MQTT 密码
Private Key (PEM)RS256 使用 PEM 私钥签名,Broker 通常持有对应公钥;私钥不能上传到文章或截图
Claims 的 Key/Value定义令牌声明,增删按钮改变这份令牌,不会修改 Broker 的校验规则
iat签发时间,工具的时间提示使用当前时间;Unix 时间单位为秒
exp过期时间,当前工具默认示例为现在加 3,600 秒;它与 MQTT Session Expiry、Message Expiry 无关
Encode进入生成视图,选择算法、密钥和 Claims
Generate & Fill生成令牌并填入 Password。它不会证明 Broker 认证或发布权限成功
Preview从 Password 中解析 Header、Payload 和时间信息,不验证签名
Open in JWT Debugger打开独立调试工具。应按该工具的验证结果判断签名,不能把 Preview 解析成功称为可信令牌

用户名、Client ID、JWT 的 subject 或其他 Claim 是否必须一致,完全由 Broker 配置决定。MQTT 规范没有要求把它们都写成同一个字符串。

JWT 工具显示 HS256、遮蔽签名密钥与密码令牌、Claims、令牌过期及生成填入。

合成令牌已生成并遮蔽;这不证明 Broker 接受令牌。

  1. 匹配 Broker 的签名算法
  2. HS256 共享签名密钥
  3. 由应用约定的声明
  4. 只约束令牌有效期
  5. 填入密码,不建立连接
JWT 工具选择 RS256,PEM 私钥输入保持空白,不展示私钥或令牌原文。

切换算法不生成私钥,也不验证现有令牌。

  1. RS256 与公钥策略匹配
  2. PEM 私钥仅用于签名
  3. 解析预览不是签名验证

会话、心跳与连接属性:V5 把几个问题拆开了

Mqttable 中文新建客户端:连接时是否清理旧会话;断线后的保留秒数;心跳不等于有效期;关闭时显式发送零;请求可选的响应信息。

1,连接时是否清理旧会话;2,断线后的保留秒数;3,心跳不等于有效期;4,关闭时显式发送零;5,请求可选的响应信息。

  1. 连接时是否清理旧会话
  2. 断线后的保留秒数
  3. 心跳不等于有效期
  4. 关闭时显式发送零
  5. 请求可选的响应信息

Clean Session 与 Clean Start:清理旧状态,不是自动重连

MQTT 3.1.1 的 Clean Session 为 1 时,清理旧会话,断线后不保留本次会话;为 0 时,尝试恢复旧会话并在断线后保留状态。状态包括订阅、未完成的 QoS 1/2 交付等;Retained Message 不属于某个客户端的会话状态。

MQTT 5 的 Clean Start 只决定连接时是否丢弃旧会话。断线后保留多久,交给 Session Expiry Interval。当前新建客户端默认开启清理开关。为理解组合,可以先看这张表:

左右滚动表格,查看其余列。

MQTT 5 设置连接时断线后
Clean Start on,Expiry 0创建干净会话丢弃本次会话,接近 V3 Clean Session on
Clean Start on,Expiry 60清理旧状态,建立新会话新会话保留 60 秒,可用于之后恢复
Clean Start off,Expiry 60有旧会话则恢复,没有则新建断线后保留 60 秒
Clean Start off,Expiry 0仍可请求恢复已有会话这次断线后立即结束会话

为什么 V5 要拆开? V3 的一个开关同时回答“现在从哪里开始”和“以后保存多久”,无法表达“这次从零开始,但断线后保留一分钟”。拆开后,客户端能指定恢复窗口,也能避免长期留下没有再上线的设备会话。

Session Expiry Interval

单位为秒,协议范围为 0 到 4,294,967,295。省略属性时默认 0;0 表示断线后结束会话,最大值表示协议上不按时间过期,仍应考虑 Broker 的资源与管理策略。当前新建客户端默认为 0,并省略该属性。

掉线后可能需要补收 QoS 1/2 消息的设备,可以使用稳定 Client ID、Clean Start off 和明确的保留秒数。实验中使用 2 秒有效期,并在超过该值的观察窗口后检查 CONNACK Session Present。订阅列表重新出现并不充分:Mqttable 能自动恢复配置中的订阅,应结合 CONNACK 和离线消息判断 Broker 是否真的恢复了会话。

Keep Alive

单位为秒,协议范围 0 到 65,535,当前产品默认 300 秒;0 关闭 MQTT Keep Alive。客户端在没有其他 MQTT 控制报文可发时,通过 PINGREQ/PINGRESP 维持活性,服务端按协议处理超时。它不是“每隔这个时间必须发送一条业务消息”,也不是订阅或消息的有效期。

Keep Alive 是两版共有能力。MQTT 5 增加 Server Keep Alive,允许 Broker 在 CONNACK 中提供应使用的值。设置越小,失联通常发现得越快,但心跳和网络唤醒更多;具体取值应结合设备功耗、链路和 Broker 策略,不存在通用最佳秒数。

Request Problem Info

协议值只有 0 和 1。属性省略时,规范默认 1。在本轮修复后的 Mqttable 中,默认未勾选会显式发送 0,勾选发送 1;旧数据未包含这个字段或为 nil 时保持省略。

1 允许 Broker 在协议允许的报文里返回 Reason String 和 User Properties,但 Broker 可以不提供。0 限制部分报文上的这些附加信息,不会关闭原因码,也不禁止规范允许的 PUBLISH、CONNACK 或 DISCONNECT 信息。不应把 Reason String 当成稳定的机器解析接口。

为什么 V5 加入它?更充分的诊断有助于排查,但附加字符串也占带宽和内存。客户端可以表达自己是否需要这些信息,而不是所有小设备都承担同样的诊断开销。它不是敏感信息过滤器。

Request Response Info

协议值为 0/1,省略默认为 0;当前默认关闭,关闭时省略属性。开启表示请求 Broker 在 CONNACK 中返回 Response Information,即使请求了,Broker 也可以不返回。

它帮助请求方获知如何构造响应 Topic,与后面 PUBLISH 的 Response Topic/Correlation Data 配合。它不自动订阅响应 Topic、不让每条发布都变成请求,也不要求 Broker 替应用生成响应。V3 中这些约定只能由应用和部署配置自行定义。

配额与 User Properties:接收方说清自己的能力

Mqttable 中文新建客户端:QoS 1/2 未确认窗口;接收方向的别名预算;整个报文的字节上限。

1,QoS 1/2 未确认窗口;2,接收方向的别名预算;3,整个报文的字节上限。

  1. QoS 1/2 未确认窗口
  2. 接收方向的别名预算
  3. 整个报文的字节上限

Receive Max:QoS 1/2 的未确认窗口

协议范围 1 到 65,535,不能为 0;省略时默认 65,535。当前产品初始为空,界面的 65535 是提示值,不代表已经发送了该属性。

客户端在 CONNECT 中声明自己愿意同时接收多少条尚未完成确认的 QoS 1/2 PUBLISH,约束 Broker 到客户端的方向。Broker 在 CONNACK 中声明的 Receive Maximum 则约束客户端到 Broker 的方向。它不限制 QoS 0,也不是每秒消息数。

V3 可以在实现里设置本地 in-flight 窗口,Broker 也有自己的策略,但双方不能通过这个标准字段互相声明能力。V5 将接收预算放到协议里,发送方才能按接收方窗口暂停新的可靠消息。实验用手动延迟 PUBACK 的辅助接收端,观察窗口为 2 时不会先收到四条,再确认一条后才释放下一条。

Maximum Packet Size:整个 MQTT 报文,不只是 payload

协议属性为正的四字节整数,0 不合法;省略时不增加额外的接收大小限制,实际仍受 MQTT 编码、实现和资源约束。当前产品初始为空;输入 0 的产品约定是省略属性,不是把非法的零写入报文。

CONNECT 中声明的是客户端能接受的整个 MQTT 控制报文大小,包含 Topic、Packet Identifier、属性和 payload。CONNACK 中同名属性声明 Broker 的接收上限。比如 payload 为 100 字节,不代表整个 PUBLISH 也是 100 字节。

为什么 V5 加入它?嵌入式设备的内存预算可能远小于协议最大报文,明确声明上限比等内存耗尽后断线更可控。注意,Broker 不应把过大的消息转发给该客户端;发布端收到 PUBACK,不等于受限订阅端收到。实验中 128 字节接收上限阻止了 512 字节 payload 的转发,前后两个小消息是成功对照。

Topic Alias Max:允许对方建立多少个别名

协议范围 0 到 65,535,省略默认为 0;当前初始为空,输入 0 也表示不允许别名。CONNECT 中声明客户端接受的 Broker → Client 别名上限;发布客户端可以使用的 Client → Broker 上限,必须读 CONNACK。

Topic Alias 是 PUBLISH 的另一个属性,取值必须从 1 开始且不超过对方接受上限。首次使用时仍需用真实 Topic 建立映射;后续可用同一连接上的别名省略重复 Topic。映射不能跨重连,也不保存到会话中。把 Topic Alias Max 设为 10,并不等于替每条 Topic 建好了映射。

V5 的用途是减少重复长 Topic 的报文开销。两端分别声明自己能保存多少映射,避免把节省带宽转变成不可控的映射内存消耗。实验同时检查两个方向,而不是只看表单中有一个整数。

User Properties:连接元数据不是全局消息标签

User Properties 是可重复的 UTF-8 Key/Value 对。Key 与 Value 使用二字节长度字段,协议允许重复 Key;当前客户端表单最多添加 100 对,普通连接实现还要求填写非空 Key 和 Value。这个数量限制属于产品,不是 MQTT 规范。

Add/Remove 增删当前 CONNECT 的元数据,比如 device-class=sensor 或 firmware=lab。这些属性的业务意义由应用或 Broker 实现约定。CONNECT 上的属性不会自动转发给订阅者,也不会自动附加到后续 PUBLISH。 要传消息元数据,应在发布面板另设 User Properties。

为什么 V5 增加它?应用可以附加扩展元数据,而不必把所有信息混进业务 payload 或编码进 Topic。标准提供承载方式,没有规定每个 Key 的含义,也没有把它变成身份或授权的可信来源。

Mqttable 中文新建客户端:Key 由业务自行约定;只属于本次 CONNECT;表单最多 100 对属性。

1,Key 由业务自行约定;2,只属于本次 CONNECT;3,表单最多 100 对属性。

  1. Key 由业务自行约定
  2. 只属于本次 CONNECT
  3. 表单最多 100 对属性

Last Will:两版共有的遗嘱,加上 V5 的时间与消息属性

遗嘱在 CONNECT 时交给 Broker 保存,不是每次点击 Publish 才设置。普通正常 DISCONNECT 不应发布遗嘱;异常断链等触发条件由协议和 Broker 行为决定。

Mqttable 中文新建客户端:遗嘱使用 Topic Name;异常离线时的内容;JSON 格式化是工具功能;遗嘱发布的 QoS;是否保存最后状态。

1,遗嘱使用 Topic Name;2,异常离线时的内容;3,JSON 格式化是工具功能;4,遗嘱发布的 QoS;5,是否保存最后状态。

  1. 遗嘱使用 Topic Name
  2. 异常离线时的内容
  3. JSON 格式化是工具功能
  4. 遗嘱发布的 QoS
  5. 是否保存最后状态

Topic、Payload、QoS、Retain 与 JSON 工具

左右滚动表格,查看其余列。

设置默认、范围与用途是否 V5 新增
Last-Will Topic初始为空,填写有效 Topic Name,不能带 + 或 #;没有 Topic 时不注册遗嘱否
Last-Will Payload初始为空,当前表单上限为 65,535 个 UTF-8 字节。比如 {"status":"offline"};空 payload 也有含义否
QoS默认 0,可选 0/1/2,约束 Broker 发布遗嘱的交付等级,仍受接收订阅及 Broker 能力限制否
Retain Last-Will Message默认关闭。开启后可让后来订阅状态 Topic 的客户端收到保存的离线状态否
Edit JSON在工具中编辑遗嘱草稿,不改变 QoS 或协议版本工具功能
Format对有效 JSON 排版,不代表 Broker 要求 payload 必须为 JSON工具功能
Open in JSON打开 JSON 工作区分析草稿,不等于已经发布遗嘱工具功能

QoS 1 可能出现重复交付;QoS 2 的协议保证不能直接写成“数据库恰好写入一次”。Retain 保存的是 Topic 的最后一条保留消息,不是保存所有历史消息,也不依赖某个订阅客户端的会话是否存在。

Will Delay、Message Expiry 与额外属性

Mqttable 中文新建客户端:发布遗嘱前等待;遗嘱发布后的有效期;描述内容类型;应用约定的响应位置;请求关联字节。

1,发布遗嘱前等待;2,遗嘱发布后的有效期;3,描述内容类型;4,应用约定的响应位置;5,请求关联字节。

  1. 发布遗嘱前等待
  2. 遗嘱发布后的有效期
  3. 描述内容类型
  4. 应用约定的响应位置
  5. 请求关联字节

左右滚动表格,查看其余列。

V5 设置默认或未设置的语义如何使用,为什么增加
Will Delay Interval秒,0 到 4,294,967,295;默认 0,产品零值省略给短暂断线留恢复窗口。等延迟到期或会话先结束时发布,以较早者为准;恢复同一会话才能抑制待发遗嘱
Message Expiry Interval秒,协议范围 0 到 4,294,967,295;缺失表示没有该协议过期时间。当前产品 0 会省略,不能用它表达协议中的“立即过期”限制遗嘱发布之后的消息有效期,避免离线状态长期残留;不是从断链时开始算的 Will Delay
Content Type初始为空,填写双方约定的 MIME 类型,如 application/json;不会自动转换 payload告诉接收应用如何解释内容,减少外部约定;声明 JSON 不等于已校验 JSON
Response Topic初始为空,合法 Topic Name,不允许通配符在需要请求/响应语义的应用中说明响应位置;不会自动发响应或建立订阅
Correlation Data初始为空,协议为二进制数据,当前文本输入会按字节发送,例如 request-7,并非自动 Base64 解码将响应与原请求关联;不是 Client ID,也不是用于排序或去重的全局唯一标识

V5 规范还允许遗嘱 Payload Format Indicator 与遗嘱 User Properties,但当前 New Client 遗嘱区域没有对应的独立设置控件。不要把发布面板的 UTF-8 开关指成遗嘱开关,也不要把 CONNECT User Properties 当成遗嘱元数据。

Will Delay 与 Session Expiry 需要一起考虑。若 Expiry 为 0,会话在断链后立即结束,不能只设置十秒 Will Delay 就承诺十秒通知缓冲。实验为会话设置大于延迟的有效期,再分别观察延迟内恢复与没有恢复的结果。

Test Connection、Cancel 与 Connect:这三个结果不能混用

  • Test Connection,临时连接测试当前 MQTT 版本和凭据,不保存新客户端,也不建立长期连接。当前测试不注册遗嘱、不发布、不订阅、不自动重连。
  • Cancel,退出本次未保存表单,不会撤销 Broker 上此前发生的连接或测试影响。
  • Connect,保存客户端配置并启动真实连接。Connected 只证明连接建立,还需检查订阅确认和实际消息收发。

Test Connection 使用真实 Client ID,可能接管同 ID 的既有连接,触发其遗嘱或影响会话及离线队列。测试通过不证明发布、订阅、遗嘱或所有配额设置都符合预期。测试 Broker 连通性的入口与测试 Client 认证的入口也不是一回事。

发布消息:把 V5 元数据放到具体的 PUBLISH 上

Mqttable 中文发布窗口使用 MQTT 3.1.1 客户端,展示 Topic、正文、QoS 1 与 Retain 设置。

发送客户端、Topic、Payload、QoS 与 Retain 都是两版共有能力。这里是未发送的配置示例。

  1. 本次发送所用连接
  2. Topic Name 不含通配符
  3. 应用内容,不强制 JSON
  4. 协议交付级别
  5. 保存 Topic 的最后状态

Client 选择本次发送连接;Topic 必须是 Topic Name,不能带订阅通配符。Payload 是应用内容,MQTT 本身不要求 JSON。QoS 选 0/1/2,Retain 决定是否更新 Topic 的保留消息。这些都是 3.1.1 已有能力。

Mqttable 中文 V5 发布属性:application/json、30 秒消息过期、UTF-8 与消息级用户属性。

这些属性属于当前 PUBLISH,不继承 CONNECT 的用户属性。UTF-8 声明不等于 JSON 校验。

  1. 描述内容类型
  2. 消息有效期,单位秒
  3. 声明 UTF-8,不校验 JSON
  4. 仅属于本条消息的元数据
Mqttable 中文 V5 发布窗口设置响应 Topic、request-7 关联数据和当前连接内的 Topic Alias 1。

先订阅响应 Topic,再发送请求。关联数据由响应方按约定带回,别名要遵守对端上限。

  1. 先订阅这个响应位置
  2. 用相同字节关联请求
  3. 仅在当前连接内有效
  4. 已选择 V5 发送客户端

左右滚动表格,查看其余列。

发布属性取值和未设置行为V5 的用途与边界
Content Type可选 UTF-8 字符串,如 application/json描述内容类型,不自动解码、转换或验证业务结构
Payload Format Indicator0 或未设置为未指定字节,1 表示 UTF-8 字符数据;当前开关显示 UTF-8提供编码信息,不等于 JSON 格式,也不是选择 Base64 的工具输入模式
Message Expiry秒,四字节整数;缺失表示没有该协议 TTL,协议的 0 表示立即过期限制尚未开始向订阅者转发的消息和保留消息的寿命;不是接收应用已经开始处理后的取消指令
Response Topic可选有效 Topic Name,不含通配符请求方说明响应位置;发送请求前应先完成该 Topic 的订阅
Correlation Data可选二进制数据,当前文本输入按字节发送响应方按应用约定带回原关联字节,区别多个同时进行的请求
Topic Alias1 到对端 CONNACK 声明的上限,0 非法;缺失则正常使用 Topic仅在当前连接内建立和复用映射。工具仍需要遵循自己的 Topic 输入要求,不能据协议示例断言所有 UI 都能提交空 Topic
User Properties可重复 Key/Value,当前发布表单最多 100 对由 Broker 随消息转发的应用元数据;与 CONNECT 上的同名属性是两份配置

为什么不继续把元数据放在 JSON 里? V3 完全可以实现 TTL、请求 ID 和内容类型,只是每个应用需要约定 payload 格式,Broker 一般也无法依赖这些约定执行协议级过期和路由处理。V5 给出标准承载字段,同时保留业务 payload 的自由。

Message Expiry 不替代接收端的业务时间校验。Topic Alias 也不保证所有连接都会自动节省带宽:应查看实际 PUBLISH 是否建立并复用了映射。

订阅消息:控制哪些消息回来,以及怎样回来

Mqttable 中文 MQTT 3.1.1 订阅界面显示 Topic Filter、订阅客户端与请求 QoS,不包含 V5 订阅选项。

V3 标准订阅只有过滤器和 QoS 等共有字段,告警监控是另外的工具功能。

  1. 过滤器可以使用通配符
  2. 明确订阅属于哪个客户端
  3. 最终获准级别要看 SUBACK
Mqttable 中文 MQTT 5 订阅界面展示 QoS 1、No Local、RAP、RH 三种选项与订阅标识 7。

逐项改变设置再比较结果;订阅标识说明匹配哪份订阅,不是消息唯一 ID。编号 4 定位 RH 选项并解释 RH 1 的用途,不表示截图已选中 RH 1;图中高亮仍为 RH 0,实际 RH 行为见下文实验。

  1. 申请的最高交付级别
  2. 抑制这个客户端自身回送
  3. 保留发布时的 Retain 位
  4. 新订阅才回送旧状态
  5. 识别消息匹配哪份订阅

Client 选择订阅连接,Topic Filter 可使用 + 匹配一层、# 匹配后续层级;QoS 是期望的最高交付级别,最终看 SUBACK。V5 中的以下选项不能套用到 3.1.1 的标准 SUBSCRIBE 格式。

左右滚动表格,查看其余列。

设置默认和有效值为什么 V5 增加它
No Local默认关闭,协议位为 0/1;开启后不把这个客户端发布的消息回送给它减少桥接或发布兼订阅客户端的自身回送;不禁止其他客户端向它发消息,也不是禁用本地网络
Retain as Published (RAP)默认关闭。开启保留发布时的 Retain 标记;关闭时正常实时转发使用相应协议规则桥接可以区分保留发布与普通发布;它不决定是否读取历史保留消息
Retain Handling (RH)默认 0;0 每次订阅发送匹配的保留消息,1 仅新建订阅时发送,2 不因订阅发送保留消息避免重复订阅时重新处理旧状态;2 不阻止之后的实时消息
Subscription Identifier可选,范围 1 到 268,435,455,0 非法;缺失时不要求此标识接收方从 PUBLISH 中知道消息匹配了哪份订阅,尤其适用于重叠过滤器;不是 Client ID 或每条消息的唯一 ID

RH 1 的“新建”指 Broker 已有订阅状态,不是用户认为这个客户端刚打开。若某项工具操作先取消再重新订阅,不能把它当成“对既有订阅的更新”;实验使用辅助客户端直接重复发送同一过滤器的 SUBSCRIBE 来区别 RH 0/1/2。

共享订阅:标准化了已有 Broker 的扩展能力

过滤器写成 $share/组名/TopicFilter,比如 $share/version-workers/versions/shared。同一组中,Broker 将一条匹配消息交给其中一个订阅者,适用于消费者工作分担。它不承诺严格轮询、均匀分配或业务处理恰好一次。组名和 Topic Filter 必须满足共享订阅的格式要求,共享订阅不能设置 No Local 为 1。

Mqttable 中文共享订阅使用 version-workers 组名和 versions/shared 过滤器,No Local 关闭。

同组分担工作,不保证均匀分配;部分 Broker 在 V3 下也提供共享订阅扩展。

  1. 组名与过滤器分别有意义
  2. 共享订阅不能开启此项
  3. 组内的一个参与者

MQTT 5 把共享订阅纳入标准,但部分 Broker 在 3.1.1 下已有此扩展。迁移文案不应写成“V3 完全不能做负载分担”;需要区分协议标准与具体 Broker 的兼容能力。Broker 可以在 CONNACK 中声明 Shared Subscription Available,不能忽略它。

用真实报文验证:配置、成功与协议推断分开

本轮使用任务专属 Mosquitto、EMQX 和包含修复的 Mqttable Web Runtime,均只开放到本机。辅助协议客户端负责那些不能由 UI 精确控制的步骤,比如暂缓 PUBACK 和重复订阅;它们的结果不冒充 Mqttable 按钮执行结果。抓包只包含本轮实验端口。截图中的 22932 是本轮记录 CONNECT 的 loopback 代理,转发到 Mosquitto 的 22931;读者直接使用 22931 即可,这个记录代理不是 MQTT 5 的要求。

从零复现的最小环境

已安装 Mosquitto 的 macOS/Linux 用户,可以建立一个独立实验目录。下面命令不覆盖已有配置;端口 22931 必须空闲,遇到占用应停下来选择另一个 loopback 端口。

bash
LAB_DIR="$(mktemp -d)"
printf '%s\n' "$LAB_DIR"
cat > "$LAB_DIR/mosquitto.conf" <<'CONF'
listener 22931 127.0.0.1
allow_anonymous true
persistence false
CONF
mosquitto -c "$LAB_DIR/mosquitto.conf"

只在本机实验中允许匿名,不把该配置用于公网。Mqttable 创建 Broker mqtt://127.0.0.1:22931;再建立两个不同 Client ID,分别选择 3.1.1 和 5.0。正常消息闭环先用 QoS 1、Retain off,订阅确认之后再发布。

会话恢复与消息过期

  1. V3 客户端关闭 Clean Session,V5 客户端关闭 Clean Start 并设置正的 Session Expiry;使用固定 Client ID。
  2. 在线订阅 versions/session,正常断开订阅者,再由另一个连接发布 QoS 1 消息。
  3. 在有效期内重新连接,检查 CONNACK Session Present 与补收消息。不要只检查本地订阅列表。
  4. V5 再断开,超过 Session Expiry 后恢复,确认旧会话不再存在。
  5. 在可恢复会话中测试 Message Expiry:断线后发送短 TTL 消息,超过 TTL 再恢复;随后发一条新消息作成功对照。

当前这份源码的 Trace 记录包含 ack_flags,详情面板却只读取另一种 Session Present 字段名,因此可能不显示它。缺少界面行不能当成 0;本轮通过实际报文和 Trace 原始记录的最低位验证恢复,下面的 CONNACK 截图只说明连接接受与接收配额。

Mqttable 检查真实 MQTT 5 CONNACK:成功码 0x00、主题别名上限 10、最大报文 65536 与接收窗口 20。

CONNACK 配额描述 Broker 的接收能力。Session Present 使用真实报文标志验证,不把详情缺行当成零。

  1. 仅表示连接被接受
  2. 零是成功,不是错误
  3. Broker 接受的别名上限
  4. Broker 的接收配额

实验结果和实际等待窗口见后面的验证表。断线、过期和负向结果必须连同时间及成功对照阅读;一张没有消息的截图不能证明消息过期功能正常。

不写客户端代码也能复现消息过期

下面命令已在本轮 Mosquitto 2.1.2 实测。第一次订阅建立会话并在一秒后按预期超时退出,退出码 27 不是认证错误;其他非零退出必须停止排查。发布命令的 TTL 为两秒,等待五秒后恢复同一客户端,不应收到 stale-command。

bash
# Keep a session for 30 seconds; exit 27 is the expected one-second timeout.
mosquitto_sub -h 127.0.0.1 -p 22931 -V mqttv5 \
  -i article-cli-expiry -c -x 30 -q 1 -t versions/cli-expiry -W 1 -v \
  || [ "$?" -eq 27 ]
mosquitto_pub -h 127.0.0.1 -p 22931 -V mqttv5 -q 1 \
  -t versions/cli-expiry -m stale-command \
  -D PUBLISH message-expiry-interval 2
sleep 5
mosquitto_sub -h 127.0.0.1 -p 22931 -V mqttv5 \
  -i article-cli-expiry -c -x 30 -q 1 -t versions/cli-expiry -W 1 -v \
  || [ "$?" -eq 27 ]

成功对照:保持下面订阅运行,在另一个终端发 fresh-command,应收到新命令。不要把一秒窗口内没有消息,单独当作过期证明。

bash
mosquitto_sub -h 127.0.0.1 -p 22931 -V mqttv5 \
  -i article-cli-expiry -c -x 30 -q 1 -t versions/cli-expiry -C 1 -W 4 -v
bash
mosquitto_pub -h 127.0.0.1 -p 22931 -V mqttv5 -q 1 \
  -t versions/cli-expiry -m fresh-command \
  -D PUBLISH message-expiry-interval 2

遗嘱延迟与请求/响应

遗嘱实验使用独立观察者订阅 versions/will/#,被观察连接设置正的 Session Expiry 与更短的 Will Delay。正常 Disconnect 应不发送遗嘱;异常断链后等待超过延迟应看到遗嘱;在延迟内用相同 Client ID、Clean Start off 恢复同一会话,应抑制待发遗嘱。给保留遗嘱加 Message Expiry 后,还要用后来订阅者检查它没有长期保留,并发新消息验证路径仍可用。

请求/响应先由请求方订阅 versions/reply,请求 PUBLISH 设置 Response Topic 和 Correlation Data request-7。响应方订阅 versions/request,收到后向指定 Topic 发响应,带回相同关联字节。Broker 不替应用执行业务函数,PUBACK 也不是业务响应。

Mqttable 收到真实响应 21.5,附带 request-7 关联数据、订阅标识 7 与接收方向的主题别名 1。

收到的 PUBLISH 才是接收证据;PUBACK 本身不能证明订阅者收到。关联字节与原请求保持一致。

  1. 真实收到的业务响应
  2. 带回同一 request-7 字节
  3. 匹配订阅标识 7
  4. 接收方向的连接内别名

配额、别名与订阅选项

配额实验把辅助接收端的 Receive Maximum 设为 2 并暂缓 PUBACK,观察发送窗口,再确认一条释放额度。最大包实验把接收上限设为 128 字节,先发小消息,再发 512 字节 payload,最后再发小消息;不要把发送方 PUBACK 当作订阅方收到了大消息。

别名实验在两个方向分别看 CONNECT/CONNACK 上限,再检查第一次携带 Topic 的映射与后续 PUBLISH。重连后必须重新建立映射。

订阅选项逐项改变,其他条件保持一致:No Local 比较自己与另一个客户端发布;RAP 比较同一条实时保留发布的 Retain 位;RH 比较初次与重复 SUBSCRIBE;Subscription Identifier 检查收到的 PUBLISH。共享订阅只要求组内总接收数和消息身份正确,不强行要求两个订阅者各收到一半。

本轮验证记录

左右滚动表格,查看其余列。

场景本轮结果证据边界
会话恢复V3 / V5 的 Session Present 均为 true,补收离线 QoS 1 消息辅助连接;固定 Client ID
会话过期V5 有效期 2 秒,断线 5 秒后 Session Present 为 false未把自动订阅当恢复证据
积压与保留消息过期两秒 TTL 的旧消息未补收,新消息成功作为对照观察窗口超过 TTL
遗嘱发布与抑制正常断开无遗嘱;异常断链后发布;延迟内恢复同一会话抑制遗嘱Will Delay 1 / 2 秒,Session Expiry 20 秒
遗嘱消息过期已看到临时遗嘱,超过两秒 TTL 的后来订阅者未收到旧状态新状态消息成功对照
请求与响应响应 21.5 带回 request-7 关联字节辅助闭环和托管 Mqttable 闭环分别验证
接收窗口窗口 2:确认前只收到 2 条;确认 1 条后新增 1 条辅助接收端暂缓 PUBACK
最大包大小128 字节上限阻止 512 字节 payload 的转发,小消息前后均成功发送 PUBACK 不当作接收证据
双向主题别名Broker 接收上限 10,客户端接收上限 2,映射复用成功按连接分别检查方向
No Local / RAP / RH自身回送被抑制;RAP 位不同;RH 新建和重复订阅结果符合设置保持成功对照,RH 直接重复 SUBSCRIBE
订阅标识与共享订阅收到标识 7;六条任务在两位同组消费者间无重复分担不宣称均匀或业务恰好一次
服务端断开同 ID 接管收到 MQTT 5 DISCONNECT 0x8E真实 Broker 返回值
SCRAM-SHA-256托管 Mqttable 客户端 Connected,真实 AUTH / AUTH / CONNACK 0x00Credentials + SecretRef + Compact MCP;不是 Raw password 提交验收
命令行过期复现退出码 27 为预期超时;旧命令未收到,fresh-command 收到文章列出的 CLI 参数已实测

SCRAM 与拒绝原因

SCRAM-SHA-256 实验使用单独的 EMQX 6.0.0,建立 scram:built_in_database 认证器和合成用户,再在 Mqttable 中选相同方法。 本轮连接使用 Credentials 界面保存的测试密码引用,再经 Compact MCP 创建客户端;不把 Raw password 表单提交视为已验收。必须观察真实 AUTH/CONNACK,不能用“选择了 SCRAM”当作认证成功。该实测不证明 SHA-1/SHA-512 或连接内重新认证已被验证。

Mqttable 中文新建客户端选择 MQTT 5 与 SCRAM-SHA-256,通过实验 SecretRef 使用密码而不显示密码原文。

认证算法必须匹配 Broker。配置不等于成功,真实 AUTH 和 CONNACK 链路提供本轮验证结果。

  1. 增强认证需要 MQTT 5
  2. 算法与 Broker 策略匹配
  3. 本轮专用的合成用户名
  4. 由 Runtime 解析密码引用
Mqttable 的真实 SCRAM-SHA-256 Trace 显示 CONNECT、收到和发出的 AUTH,以及成功的 CONNACK,不展示认证数据。

按时间从 CONNECT、Broker 挑战、客户端响应读到 CONNACK。连接认证通过不代表消息权限已验收。

  1. 客户端开始连接
  2. Broker 发起认证挑战
  3. 客户端返回认证响应
  4. Broker 接受连接

连接拒绝则分别看 MQTT 3.1.1 CONNACK 返回码和 MQTT 5 原因码;具体数值由 Broker 的实际策略决定。同一 Client ID 接管,在 MQTT 5 中还可看到服务端 DISCONNECT 0x8E。Reason String 可选,Broker 不返回时不要补造文字;错误码速查 可用于定位含义。

不在 New Client 中配置的 MQTT 5 能力

左右滚动表格,查看其余列。

能力它解决的问题在本文中的边界
原因码与 Reason String发布确认、订阅、断开等更明确地说明结果;原因字符串用于诊断原因码不是全部都是错误;诊断字符串不是稳定 API
服务端 DISCONNECTBroker 主动说明连接为什么结束展示真实接管报文,不模拟没有发生的拒绝原因
CONNACK 的 Maximum QoS、Retain/Wildcard/Subscription Identifier/Shared Subscription Available提前告诉客户端服务端可选能力,避免尝试不支持的操作是 Broker 返回属性,不是 New Client 的一组开关;未返回的可选属性不伪造
Server Keep AliveBroker 告知它希望客户端使用的心跳值不是表单中另一个客户端输入框
Assigned Client IdentifierBroker 分配身份后把它返回给客户端,支持后续使用同一身份不宣称当前生成 Client ID 按钮采用服务端分配
Server Reference在相应 CONNACK/DISCONNECT 原因中提示替代服务器属性不等于当前产品会自动迁移连接;本轮不宣称重定向实测
AUTH 与重新认证支持多轮认证,并允许在连接内重新认证本轮验证连接期 SCRAM,不宣称 UI 提供主动重新认证功能

从 3.1.1 迁移到 5.0,先改哪些设置?

先确认 Broker 与设备 SDK 均支持 V5,然后选择一个非生产 Client ID 做消息闭环。两版不能仅通过把协议版本号改掉就保留所有行为假设。

  • 需要 V3 Clean Session on 的效果,V5 使用 Clean Start on、Session Expiry 0。
  • 需要恢复离线会话,保持 Client ID,设 Clean Start off 和明确的正有效期。4,294,967,295 表示协议上不过期,不等于 Broker 会跨所有故障永远保存。
  • 业务命令有时效,先定义 Message Expiry 与应用时间校验;设备状态通知会抖动,再考虑 Will Delay 与足够的 Session Expiry。
  • 设备内存或吞吐有限,再配置 Receive Maximum 和 Maximum Packet Size。它们有方向性,别把客户端的接收预算当成发布速率。
  • 请求/响应、桥接和重叠订阅有需要时,再引入 Response Topic、Correlation Data、No Local、RAP、RH 和 Subscription Identifier。

选择 3.1.1 不意味着系统不可靠;选择 5.0 也不会自动修复鉴权、断线、重复处理或业务过期。用配置表达需求,再用 CONNECT/CONNACK、SUBACK、PUBLISH 和真实接收结果检查它。

常见问题

换成 V5 就必须换端口吗? 不必。Listener 决定是否接受对应版本;端口和 MQTT 协议版本是不同层的配置。

QoS 2、Retain、JWT 都是 V5 新功能吗? 不是。QoS 2 与 Retain 在 V3 就有;JWT 可以作为普通 MQTT 密码凭据使用。V5 新增的是标准属性、增强认证框架和相关行为。

Session Expiry、Message Expiry、Will Delay、JWT exp 能互相替代吗? 不能。它们分别约束断线会话、消息有效期、遗嘱发布时机和令牌有效期。

我勾选了 Request Response Info,为什么 CONNACK 没有对应信息? 规范允许 Broker 不提供;响应 Topic 和关联数据仍由双方应用约定。

订阅列表恢复了,是否就证明会话恢复? 不充分。客户端可能重新订阅;检查 Session Present 和离线消息,区分本地配置与 Broker 会话。

后续可以继续阅读连接配置、发布与订阅、Trace 和 PCAP 分析。TLS、告警和定时消息各有自己的配置边界,本文不把它们并入 MQTT 版本差异。