MQTT 3.1.1 与 MQTT 5.0 有什么区别:用 Mqttable 逐项配置与验证
从 Mqttable 新建客户端界面逐项理解会话、配额、遗嘱、认证和用户属性,再通过发布、订阅及隔离实验解释 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.1 | MQTT 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 介绍了新增能力及部分用途;下文的设备、桥接和命令例子是工程场景分析,不是对标准制定过程的历史描述。
在 Connections 中选择 Broker,再打开 New Client/Add client。Broker 行显示这个客户端归属的连接端点。Host、Port、传输协议、TLS 和证书配置属于 Broker 设置,不是 MQTT 5 新属性。本文实验端点只绑定本机 loopback;真实接入请使用组织提供的端点与可信凭据。

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

1,名称不作为 MQTT 身份发送;2,恢复会话要保持同一 ID;3,凭据不等于传输加密;4,版本与端口分开;5,匹配 Listener 认证策略。
- 名称不作为 MQTT 身份发送
- 恢复会话要保持同一 ID
- 凭据不等于传输加密
- 版本与端口分开
- 匹配 Listener 认证策略
左右滚动表格,查看其余列。
| 设置 | 含义与填写方法 | 两个版本的关系 |
|---|---|---|
| Name | Mqttable 中的显示名称,最多 255 个 UTF-8 字节。可以叫“温度采集器”,它不会作为 MQTT Client ID 发给 Broker | 工具功能,两版相同 |
| Generate a new name | 生成新的显示名称,不会替你创建 Broker 账号或更换已经连接的设备身份 | 工具功能 |
| Client ID | CONNECT 中的客户端身份。当前表单接受受限长度的 UTF-8 字符串,最终还受 Broker 的长度和字符策略约束 | 两版共有;不能把工具输入上限当成所有 Broker 都支持的长度 |
| Generate Client ID | 为新实验生成身份,减少撞号;需要恢复旧会话时不要重新生成 | 工具功能 |
同一个 Broker 上两个连接使用同一个 Client ID,后来的连接可能接管前一个连接。会话恢复要求 Client ID 稳定,仅把 Clean Start 关掉而每次生成新 ID,仍然找不到旧会话。MQTT 5 还允许 Broker 返回 Assigned Client Identifier,见后面的能力补充;这不等于当前表单的生成按钮使用了该机制。
左右滚动表格,查看其余列。
| 选项 | 实际用途 | 使用条件及误区 |
|---|---|---|
| 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 不是 V5 专属功能;SCRAM 方法需要两端匹配。
- 按 Broker 策略选择
- 不发送密码
- 普通用户名与密码
- 密码栏传递令牌
- MQTT 5 多轮认证
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、截图或文章。

引用由本地 Runtime 解析,不是 MQTT 属性或发给 Broker 的密码文本。截图不显示密码。
- 直接填写本次凭据
- 复用已保存的密码引用
- 选中任务专属实验凭据
- 认证方式与来源分开
JWT Tools 是 Mqttable 的辅助工具,以下设置都不是 MQTT 报文字段。
左右滚动表格,查看其余列。
| 工具设置 | 含义和使用方法 |
|---|---|
| Algorithm | 当前提供 HS256 与 RS256,必须与 Broker 验证策略一致;不能仅凭工具生成成功判断 Broker 接受 |
| Secret | HS256 使用共享签名密钥,双方需要匹配;不是自动生成的 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 规范没有要求把它们都写成同一个字符串。

合成令牌已生成并遮蔽;这不证明 Broker 接受令牌。
- 匹配 Broker 的签名算法
- HS256 共享签名密钥
- 由应用约定的声明
- 只约束令牌有效期
- 填入密码,不建立连接

切换算法不生成私钥,也不验证现有令牌。
- RS256 与公钥策略匹配
- PEM 私钥仅用于签名
- 解析预览不是签名验证

1,连接时是否清理旧会话;2,断线后的保留秒数;3,心跳不等于有效期;4,关闭时显式发送零;5,请求可选的响应信息。
- 连接时是否清理旧会话
- 断线后的保留秒数
- 心跳不等于有效期
- 关闭时显式发送零
- 请求可选的响应信息
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 的一个开关同时回答“现在从哪里开始”和“以后保存多久”,无法表达“这次从零开始,但断线后保留一分钟”。拆开后,客户端能指定恢复窗口,也能避免长期留下没有再上线的设备会话。
单位为秒,协议范围为 0 到 4,294,967,295。省略属性时默认 0;0 表示断线后结束会话,最大值表示协议上不按时间过期,仍应考虑 Broker 的资源与管理策略。当前新建客户端默认为 0,并省略该属性。
掉线后可能需要补收 QoS 1/2 消息的设备,可以使用稳定 Client ID、Clean Start off 和明确的保留秒数。实验中使用 2 秒有效期,并在超过该值的观察窗口后检查 CONNACK Session Present。订阅列表重新出现并不充分:Mqttable 能自动恢复配置中的订阅,应结合 CONNACK 和离线消息判断 Broker 是否真的恢复了会话。
单位为秒,协议范围 0 到 65,535,当前产品默认 300 秒;0 关闭 MQTT Keep Alive。客户端在没有其他 MQTT 控制报文可发时,通过 PINGREQ/PINGRESP 维持活性,服务端按协议处理超时。它不是“每隔这个时间必须发送一条业务消息”,也不是订阅或消息的有效期。
Keep Alive 是两版共有能力。MQTT 5 增加 Server Keep Alive,允许 Broker 在 CONNACK 中提供应使用的值。设置越小,失联通常发现得越快,但心跳和网络唤醒更多;具体取值应结合设备功耗、链路和 Broker 策略,不存在通用最佳秒数。
协议值只有 0 和 1。属性省略时,规范默认 1。在本轮修复后的 Mqttable 中,默认未勾选会显式发送 0,勾选发送 1;旧数据未包含这个字段或为 nil 时保持省略。
1 允许 Broker 在协议允许的报文里返回 Reason String 和 User Properties,但 Broker 可以不提供。0 限制部分报文上的这些附加信息,不会关闭原因码,也不禁止规范允许的 PUBLISH、CONNACK 或 DISCONNECT 信息。不应把 Reason String 当成稳定的机器解析接口。
为什么 V5 加入它?更充分的诊断有助于排查,但附加字符串也占带宽和内存。客户端可以表达自己是否需要这些信息,而不是所有小设备都承担同样的诊断开销。它不是敏感信息过滤器。
协议值为 0/1,省略默认为 0;当前默认关闭,关闭时省略属性。开启表示请求 Broker 在 CONNACK 中返回 Response Information,即使请求了,Broker 也可以不返回。
它帮助请求方获知如何构造响应 Topic,与后面 PUBLISH 的 Response Topic/Correlation Data 配合。它不自动订阅响应 Topic、不让每条发布都变成请求,也不要求 Broker 替应用生成响应。V3 中这些约定只能由应用和部署配置自行定义。

1,QoS 1/2 未确认窗口;2,接收方向的别名预算;3,整个报文的字节上限。
- 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 时不会先收到四条,再确认一条后才释放下一条。
协议属性为正的四字节整数,0 不合法;省略时不增加额外的接收大小限制,实际仍受 MQTT 编码、实现和资源约束。当前产品初始为空;输入 0 的产品约定是省略属性,不是把非法的零写入报文。
CONNECT 中声明的是客户端能接受的整个 MQTT 控制报文大小,包含 Topic、Packet Identifier、属性和 payload。CONNACK 中同名属性声明 Broker 的接收上限。比如 payload 为 100 字节,不代表整个 PUBLISH 也是 100 字节。
为什么 V5 加入它?嵌入式设备的内存预算可能远小于协议最大报文,明确声明上限比等内存耗尽后断线更可控。注意,Broker 不应把过大的消息转发给该客户端;发布端收到 PUBACK,不等于受限订阅端收到。实验中 128 字节接收上限阻止了 512 字节 payload 的转发,前后两个小消息是成功对照。
协议范围 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 是可重复的 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 的含义,也没有把它变成身份或授权的可信来源。

1,Key 由业务自行约定;2,只属于本次 CONNECT;3,表单最多 100 对属性。
- Key 由业务自行约定
- 只属于本次 CONNECT
- 表单最多 100 对属性
遗嘱在 CONNECT 时交给 Broker 保存,不是每次点击 Publish 才设置。普通正常 DISCONNECT 不应发布遗嘱;异常断链等触发条件由协议和 Broker 行为决定。

1,遗嘱使用 Topic Name;2,异常离线时的内容;3,JSON 格式化是工具功能;4,遗嘱发布的 QoS;5,是否保存最后状态。
- 遗嘱使用 Topic Name
- 异常离线时的内容
- JSON 格式化是工具功能
- 遗嘱发布的 QoS
- 是否保存最后状态
左右滚动表格,查看其余列。
| 设置 | 默认、范围与用途 | 是否 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 的最后一条保留消息,不是保存所有历史消息,也不依赖某个订阅客户端的会话是否存在。

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,临时连接测试当前 MQTT 版本和凭据,不保存新客户端,也不建立长期连接。当前测试不注册遗嘱、不发布、不订阅、不自动重连。
- Cancel,退出本次未保存表单,不会撤销 Broker 上此前发生的连接或测试影响。
- Connect,保存客户端配置并启动真实连接。Connected 只证明连接建立,还需检查订阅确认和实际消息收发。
Test Connection 使用真实 Client ID,可能接管同 ID 的既有连接,触发其遗嘱或影响会话及离线队列。测试通过不证明发布、订阅、遗嘱或所有配额设置都符合预期。测试 Broker 连通性的入口与测试 Client 认证的入口也不是一回事。

发送客户端、Topic、Payload、QoS 与 Retain 都是两版共有能力。这里是未发送的配置示例。
- 本次发送所用连接
- Topic Name 不含通配符
- 应用内容,不强制 JSON
- 协议交付级别
- 保存 Topic 的最后状态
Client 选择本次发送连接;Topic 必须是 Topic Name,不能带订阅通配符。Payload 是应用内容,MQTT 本身不要求 JSON。QoS 选 0/1/2,Retain 决定是否更新 Topic 的保留消息。这些都是 3.1.1 已有能力。

这些属性属于当前 PUBLISH,不继承 CONNECT 的用户属性。UTF-8 声明不等于 JSON 校验。
- 描述内容类型
- 消息有效期,单位秒
- 声明 UTF-8,不校验 JSON
- 仅属于本条消息的元数据

先订阅响应 Topic,再发送请求。关联数据由响应方按约定带回,别名要遵守对端上限。
- 先订阅这个响应位置
- 用相同字节关联请求
- 仅在当前连接内有效
- 已选择 V5 发送客户端
左右滚动表格,查看其余列。
| 发布属性 | 取值和未设置行为 | V5 的用途与边界 |
|---|---|---|
| Content Type | 可选 UTF-8 字符串,如 application/json | 描述内容类型,不自动解码、转换或验证业务结构 |
| Payload Format Indicator | 0 或未设置为未指定字节,1 表示 UTF-8 字符数据;当前开关显示 UTF-8 | 提供编码信息,不等于 JSON 格式,也不是选择 Base64 的工具输入模式 |
| Message Expiry | 秒,四字节整数;缺失表示没有该协议 TTL,协议的 0 表示立即过期 | 限制尚未开始向订阅者转发的消息和保留消息的寿命;不是接收应用已经开始处理后的取消指令 |
| Response Topic | 可选有效 Topic Name,不含通配符 | 请求方说明响应位置;发送请求前应先完成该 Topic 的订阅 |
| Correlation Data | 可选二进制数据,当前文本输入按字节发送 | 响应方按应用约定带回原关联字节,区别多个同时进行的请求 |
| Topic Alias | 1 到对端 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 是否建立并复用了映射。

V3 标准订阅只有过滤器和 QoS 等共有字段,告警监控是另外的工具功能。
- 过滤器可以使用通配符
- 明确订阅属于哪个客户端
- 最终获准级别要看 SUBACK

逐项改变设置再比较结果;订阅标识说明匹配哪份订阅,不是消息唯一 ID。编号 4 定位 RH 选项并解释 RH 1 的用途,不表示截图已选中 RH 1;图中高亮仍为 RH 0,实际 RH 行为见下文实验。
- 申请的最高交付级别
- 抑制这个客户端自身回送
- 保留发布时的 Retain 位
- 新订阅才回送旧状态
- 识别消息匹配哪份订阅
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。
过滤器写成 $share/组名/TopicFilter,比如 $share/version-workers/versions/shared。同一组中,Broker 将一条匹配消息交给其中一个订阅者,适用于消费者工作分担。它不承诺严格轮询、均匀分配或业务处理恰好一次。组名和 Topic Filter 必须满足共享订阅的格式要求,共享订阅不能设置 No Local 为 1。

同组分担工作,不保证均匀分配;部分 Broker 在 V3 下也提供共享订阅扩展。
- 组名与过滤器分别有意义
- 共享订阅不能开启此项
- 组内的一个参与者
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 端口。
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,订阅确认之后再发布。
- V3 客户端关闭 Clean Session,V5 客户端关闭 Clean Start 并设置正的 Session Expiry;使用固定 Client ID。
- 在线订阅
versions/session,正常断开订阅者,再由另一个连接发布 QoS 1 消息。 - 在有效期内重新连接,检查 CONNACK Session Present 与补收消息。不要只检查本地订阅列表。
- V5 再断开,超过 Session Expiry 后恢复,确认旧会话不再存在。
- 在可恢复会话中测试 Message Expiry:断线后发送短 TTL 消息,超过 TTL 再恢复;随后发一条新消息作成功对照。
当前这份源码的 Trace 记录包含 ack_flags,详情面板却只读取另一种 Session Present 字段名,因此可能不显示它。缺少界面行不能当成 0;本轮通过实际报文和 Trace 原始记录的最低位验证恢复,下面的 CONNACK 截图只说明连接接受与接收配额。

CONNACK 配额描述 Broker 的接收能力。Session Present 使用真实报文标志验证,不把详情缺行当成零。
- 仅表示连接被接受
- 零是成功,不是错误
- Broker 接受的别名上限
- Broker 的接收配额
实验结果和实际等待窗口见后面的验证表。断线、过期和负向结果必须连同时间及成功对照阅读;一张没有消息的截图不能证明消息过期功能正常。
下面命令已在本轮 Mosquitto 2.1.2 实测。第一次订阅建立会话并在一秒后按预期超时退出,退出码 27 不是认证错误;其他非零退出必须停止排查。发布命令的 TTL 为两秒,等待五秒后恢复同一客户端,不应收到 stale-command。
# 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,应收到新命令。不要把一秒窗口内没有消息,单独当作过期证明。
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 -vmosquitto_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 也不是业务响应。

收到的 PUBLISH 才是接收证据;PUBACK 本身不能证明订阅者收到。关联字节与原请求保持一致。
- 真实收到的业务响应
- 带回同一 request-7 字节
- 匹配订阅标识 7
- 接收方向的连接内别名
配额实验把辅助接收端的 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 0x00 | Credentials + SecretRef + Compact MCP;不是 Raw password 提交验收 |
| 命令行过期复现 | 退出码 27 为预期超时;旧命令未收到,fresh-command 收到 | 文章列出的 CLI 参数已实测 |
SCRAM-SHA-256 实验使用单独的 EMQX 6.0.0,建立 scram:built_in_database 认证器和合成用户,再在 Mqttable 中选相同方法。 本轮连接使用 Credentials 界面保存的测试密码引用,再经 Compact MCP 创建客户端;不把 Raw password 表单提交视为已验收。必须观察真实 AUTH/CONNACK,不能用“选择了 SCRAM”当作认证成功。该实测不证明 SHA-1/SHA-512 或连接内重新认证已被验证。

认证算法必须匹配 Broker。配置不等于成功,真实 AUTH 和 CONNACK 链路提供本轮验证结果。
- 增强认证需要 MQTT 5
- 算法与 Broker 策略匹配
- 本轮专用的合成用户名
- 由 Runtime 解析密码引用

按时间从 CONNECT、Broker 挑战、客户端响应读到 CONNACK。连接认证通过不代表消息权限已验收。
- 客户端开始连接
- Broker 发起认证挑战
- 客户端返回认证响应
- Broker 接受连接
连接拒绝则分别看 MQTT 3.1.1 CONNACK 返回码和 MQTT 5 原因码;具体数值由 Broker 的实际策略决定。同一 Client ID 接管,在 MQTT 5 中还可看到服务端 DISCONNECT 0x8E。Reason String 可选,Broker 不返回时不要补造文字;错误码速查 可用于定位含义。
左右滚动表格,查看其余列。
| 能力 | 它解决的问题 | 在本文中的边界 |
|---|---|---|
| 原因码与 Reason String | 发布确认、订阅、断开等更明确地说明结果;原因字符串用于诊断 | 原因码不是全部都是错误;诊断字符串不是稳定 API |
| 服务端 DISCONNECT | Broker 主动说明连接为什么结束 | 展示真实接管报文,不模拟没有发生的拒绝原因 |
| CONNACK 的 Maximum QoS、Retain/Wildcard/Subscription Identifier/Shared Subscription Available | 提前告诉客户端服务端可选能力,避免尝试不支持的操作 | 是 Broker 返回属性,不是 New Client 的一组开关;未返回的可选属性不伪造 |
| Server Keep Alive | Broker 告知它希望客户端使用的心跳值 | 不是表单中另一个客户端输入框 |
| Assigned Client Identifier | Broker 分配身份后把它返回给客户端,支持后续使用同一身份 | 不宣称当前生成 Client ID 按钮采用服务端分配 |
| Server Reference | 在相应 CONNACK/DISCONNECT 原因中提示替代服务器 | 属性不等于当前产品会自动迁移连接;本轮不宣称重定向实测 |
| AUTH 与重新认证 | 支持多轮认证,并允许在连接内重新认证 | 本轮验证连接期 SCRAM,不宣称 UI 提供主动重新认证功能 |
先确认 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 会话。