欢迎阅读 MQTT 5.0 报文系列的第三篇文章。在上一篇中,我们介绍了 MQTT 5.0 的 PUBLISH 及其响应报文。现在,我们将介绍用于订阅和取消订阅的控制报文。
在 MQTT 中,SUBSCRIBE 报文用于发起订阅请求,SUBACK 报文用于返回订阅结果。而 UNSUBSCRIBE 和 UNSUBACK 报文则在取消订阅时使用。相比于取消订阅,订阅操作更加常用。不过在本文中,我们仍然会一并介绍订阅与取消订阅报文的结构与组成。
首先我们借助 Wireshark 来捕获一次真实的 MQTT 订阅请求与响应,这里我们使用 MQTTX CLI 向 公共 MQTT 服务器 发起订阅请求。以下命令将创建一个主题为 demo,且最大 QoS 设置为 2 的订阅:
mqttx sub --hostname broker.emqx.io --mqtt-version 5 --topic demo --qos 2
以下是 Wireshark 捕获到的 SUBSCRIBE 和 SUBACK 报文数据:
# SUBSCRIBE
82 0a 05 be 00 00 04 64 65 6d 6f 02
# SUBACK
90 04 05 be 00 02
Linux 环境可以先使用 tcpdump 命令抓取报文,然后导入至 Wireshark 分析。
这些由十六进制字节组成的原始且晦涩的报文数据,它们分别对应着以下报文内容:
也许你开始好奇它们是如何完成从简单的 MQTTX CLI 命令到复杂的报文数据的转换,或者好奇当你捕获到一个 MQTT 报文,你应该如何从中提取你想要的信息。
那么在接下来的 SUBSCRIBE、SUBACK、UNSUBSCRIBE 以及 UNSUBACK 的报文结构的介绍中,你的疑问将得到解答。
在 SUBSCRIBE 报文中,固定报头中首字节的高 4 位值必须为 8(0b1000),而低 4 位保留位必须被设置为 2(0b0010)。第一个字节之后,仍然是剩余长度(Remaining Length)字段,它是一个可变字节整数。
SUBSCRIBE 报文的可变报头按顺序包含以下字段:
Identifier | Property Name | Type |
---|---|---|
0x0B | Subscription Identifier | 变长字节整数 |
0x26 | User Property | UTF-8 字符串对 |
SUBSCRIBE 报文的有效载荷包含一个或多个主题过滤器/订阅选项对。主题过滤器是一个 UTF-8 编码的字符串,用于向服务端指明客户端希望订阅的主题,订阅选项则仅占用一个字节,目前由以下四个选项组成:
SUBACK 报文的首字节高 4 位的值为 9(0b1001),低 4 位都必须全部被设置为 0。
SUBACK 的可变报头按顺序包含以下字段:
Identifier | Property Name | Type |
---|---|---|
0x1F | Reason String | UTF-8 编码的字符串 |
0x26 | User Property | UTF-8 字符串对 |
SUBACK 报文的有效载荷包含了一个 Reason Code 列表,Reason Code 指示了订阅是否成功或者失败的原因。一个 Reason Code 对应 SUBSCRIBE 报文的一个主题过滤器,所以 SUBACK 报文中 Reason Code 的顺序必须与 SUBSCRIBE 报文中主题过滤器的顺序一致。
下表列出了 SUBACK 报文可用的所有 Reason Code:
Value | Reason Code Name | Description |
---|---|---|
0x00 | Granted QoS 0 | 订阅被接受且最大 QoS 等级为 0。服务端授予的 QoS 等级可能低于客户端请求的 QoS 等级,这主要取决于服务端是否支持所有 QoS 或者是相应的权限设置。 |
0x01 | Granted QoS 1 | 订阅被接受且最大 QoS 等级为 1。 |
0x02 | Granted QoS 2 | 订阅被接受且最大 QoS 等级为 2。 |
0x80 | Unspecified error | 表示未指明的错误。当一方不希望向另一方透露错误的具体原因,或者协议规范中没有能够匹配当前情况的 Reason Code 时,那么它会在报文中使用这个 Reason Code。 |
0x83 | Implementation specific error | SUBSCRIBE 报文有效,但是不被当前服务端的实现所接受。 |
0x87 | Not authorized | 客户端无权进行此订阅。 |
0x8F | Topic Filter invalid | 主题过滤器的格式正确,但是不被服务端接受。比如主题过滤器的层级超过了服务端允许的最大数量限制。 |
0x91 | Packet Identifier in use | 收到报文中的 Packet ID 正在被使用。 |
0x97 | Quota exceeded | 表示超出了配额限制。服务端可能会对订阅端的订阅配额进行限制,比如一个客户端最多建立 10 个订阅。 |
0x9E | Shared Subscriptions not supported | 服务端不支持共享订阅。 |
0xA1 | Subscription Identifiers not supported | 服务端不支持订阅标识符。 |
0xA2 | Wildcard Subscriptions not supported | 服务端不支持通配符订阅。 |
与 SUBSCRIBE 报文相同,唯一的区别是报文类型字段的值从 8(0b1000) 变成了 10(0b1010)。
与 SUBSCRIBE 报文相同。
UNSUBSCRIBE 报文的有效载荷包含一个或多个客户端希望取消订阅的主题过滤器,这些主题过滤器同样是 UTF-8 编码的字符串,并且多个主题过滤器紧密相连。
UNSUBACK 报文的首字节高 4 位的值为 11(0b1011),低 4 位都必须全部被设置为 0。
UNSUBACK 的可变报头按顺序包含报文标识符和属性字段,可用的属性与 SUBACK 报文相同。
UNSUBACK 报文的有效载荷同样包含了一个 Reason Code 列表,Reason Code 指示了取消订阅是否成功或者失败的原因。这些 Reason Code 按顺序对应 UNSUBSCRIBE 报文的主题过滤器。
下表列出了 UNSUBACK 报文可用的所有 Reason Code:
Value | Reason Code Name | Description |
---|---|---|
0x00 | Success | 订阅已被删除 |
0x11 | No subscription existed | 服务端中不存在该订阅。 |
0x80 | Unspecified error | 取消订阅无法完成,服务器要么不希望透露原因,要么没有其他原因代码适用。 |
0x83 | Implementation specific error | UNSUBSCRIBE 报文有效,但是不被当前服务端的实现所接受。 |
0x87 | Not authorized | 客户端无权取消此订阅。 |
0x8F | Topic Filter invalid | 主题过滤器的格式正确,但是不被服务端接受。比如主题过滤器的层级超过了服务端允许的最大数量限制。 |
0x91 | Packet Identifier in use | 收到报文中的 Packet ID 正在被使用。 |
SUBSCRIBE 和 SUBACK 报文用于订阅,UNSUBSCRIBE 和 UNSUBACK 用于取消订阅,想要订阅或者取消订阅的主题过滤器列表,都在对应报文的有效载荷中,SUBSCRIBE 报文中的每个主题主题过滤器,都与一组订阅选项相关联。
指示请求结果的 Reason Code 位于 SUBACK 和 UNSUBACK 报文的有效载荷部分,并且它是一个列表,一一对应请求报文中的主题过滤器。
在下一篇文章中,我们将继续研究 MQTT 的心跳报文。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。