MQTT 5.0 报文解析 02:PUBLISH 与 PUBACK

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 本文将介绍在 MQTT 中用于传递应用消息的 PUBLISH 报文以及它的响应报文。不管是客户端向服务端发布消息,还是服务端向订阅端转发消息,都需要使用 PUBLISH 报文。决定消息流向的主题、消息的实际内容和 QoS 等级,都包含在 PUBLISH 报文中。

欢迎阅读 MQTT 5.0 报文系列 的第二篇文章。在上一篇中,我们已经介绍了 MQTT 5.0 的 CONNECT 和 CONNACK 报文。现在,我们将介绍在 MQTT 中用于传递应用消息的 PUBLISH 报文以及它的响应报文。

不管是客户端向服务端发布消息,还是服务端向订阅端转发消息,都需要使用 PUBLISH 报文。决定消息流向的主题、消息的实际内容和 QoS 等级,都包含在 PUBLISH 报文中。

客户端与服务端在消息传递的过程中,除了 PUBLISH 报文,还会用到 PUBACK、PUBREC、PUBREL、PUBCOMP 这四个报文,它们分别用于实现 MQTT 的 QoS 1 和 QoS 2 消息机制。在本文中,我们将深入研究这五个报文的组成。

报文示例

我们使用 MQTTX CLI公共 MQTT 服务器 发布三条不同 QoS 等级的消息,并使用 Wireshark 工具抓取在客户端与服务器之间往返的 MQTT 报文,Linux 环境可以使用 tcpdump 命令抓取报文,然后导入至 Wireshark 分析。

以下是本示例使用的 MQTTX CLI 命令,为了展示 PUBLISH 报文的属性字段,命令中还设置了 Message Expiry Interval 和 Response Topic 属性:

mqttx pub --hostname broker.emqx.io --mqtt-version 5 \  --topic request --qos 0 --message "This is a QoS 0 message" \  --message-expiry-interval 300 --response-topic response

以下是 Wireshark 抓取到的 MQTTX CLI 发出的 QoS 为 0 的 PUBLISH 报文:

30 31 00 07 72 65 71 75 65 73 74 10 02 00 00 01 2c 08 00 08 72 65 73 70 6f 6e 73 65 54 68 69 73 20 69 73 20 61 20 51 6f 53 20 30 20 6d 65 73 73 61 67 65

这串十六进制字节,对应以下报文内容:

01publishpacket.png

而当我们仅修改 MQTTX CLI 命令中的 QoS 选项,将消息的 QoS 等级设置为 1,我们将看到服务端在收到 PUBLISH 后回复了 PUBACK 报文,他们的报文数据分别为:

Client  -- PUBLISH (32 33 00 .. ..)    ->  Server
Client  <- PUBACK  (40 04 64 4a 10 00) --  Server

此时 PUBLISH 报文中第一个字节从 0x30 变成了 0x32,表示这是一条 QoS 1 消息。

PUBACK 的报文结构比较简单,可以看到 Reason Code 为 0x10,表示消息被接收,但是没有匹配的订阅者。一旦有人订阅了 request 主题,那么 PUBACK 报文中的 Reason Code 就会变成 0x00,即消息被接收,且存在匹配的订阅者。

02pubackpacket.png

继续使用 MQTTX CLI 发布一条 QoS 2 消息,我们将看到客户端和服务端之间发生了两次报文往返,Wireshark 会告诉我们,这些报文分别是 PUBLISH、PUBREC、PUBREL 以及 PUBCOMP,并且它们拥有相同的报文标识符 0x11c2

Client  -- PUBLISH (34 33 00 .. ..)    ->  Server
Client  <- PUBREC  (50 04 11 c2 10 00) --  Server
Client  -- PUBREL  (62 03 11 c2 00)    ->  Server
Client  <- PUBCOMP (70 04 11 c2 00 00) --  Server

如何从由十六进制字节组成的报文数据中准确地知道这是否是一个 PUBLISH 报文,它的 QoS 是多少,它的响应报文中的原因码又是多少,接下来对这些报文的介绍将会回答这些问题。

PUBLISH 报文结构

固定报头

PUBLISH 报文的固定报头中,首字节的高 4 位的值固定为 3(0b0011),低 4 位则由以下三个字段组成:

  • DUP(Bit 3):客户端或服务端在重传 PUBLISH 报文时,需要将 DUP 标志设置为 1,表示这是一个重传的报文。收到 DUP 为 1 的 PUBLISH 报文的数量和频率可以为我们揭示当前通信链路的质量。
  • QoS(Bit 2 - 1):用于指定消息的 QoS 等级。
  • Retain(Bit 0):设置为 1,表示当前消息是 保留消息;设置为 0,则表示当前消息是普通的消息。

紧随其后的是剩余长度(Remaining Length)字段,指示了当前报文剩余部分的字节数。

PUBLISH 报文结构

可变报头

PUBLISH 报文的可变报头按顺序包含以下字段:

  • 主题名(Topic Name):这是一个 UTF-8 编码的字符串,用来指示消息应该被发布到哪一个信息通道。
  • 报文标识符(Packet Identifier):这是一个两个字节长度的无符号整数,用来唯一地标识当前正在传输的消息,只有在 QoS 等级为 1 或 2 时,报文标识符才会出现在 PUBLISH 报文中。
  • 属性(Properties):下表列出了 PUBLISH 报文的所有可用属性,这里我们不再额外花费篇幅具体介绍每个属性的用途,你可以点击属性名以查看对应的博客:
Identifier Property Name Type
0x01 Payload Format Indicator 单字节
0x02 Message Expiry Interval 四字节整数
0x23 Topic Alias 双字节整数
0x08 Response Topic UTF-8 编码的字符串
0x09 Correlation Data 二进制数据
0x26 User Property UTF-8 字符串对
0x0B Subscription Identifier 变长字节整数
0x03 Content Type UTF-8 编码的字符串

有效载荷

我们发送的应用消息的实际内容,就存放在 PUBLISH 报文的有效载荷中,它可以承载任意格式的应用消息,比如 JSON、ProtoBuf 等等。

PUBACK 报文结构

固定报头

固定报头中首字节的高 4 位的值固定为 4(0b0100),表示这是一个 PUBACK 报文,低 4 位是保留位,固定全部为 0。

紧随其后的是剩余长度(Remaining Length)字段,指示了当前报文剩余部分的字节数。

PUBACK 报文结构

可变报头

PUBACK 报文的可变报头按顺序包含以下字段:

  • 报文标识符(Packet Identifier):与 PUBLISH 报文不同,PUBACK 报文中的报文标识符必须存在,它用于向对端指示这是对哪一个 QoS 为 1 的 PUBLISH 报文的响应。
  • 原因码(Reason Code):这是一个单字节的无符号整数,用于向 PUBLISH 报文的发布端指示发布结果,比如是否因为未授权而被拒绝发布。下表列出了PUBACK 报文所有可用的 Reason Code:
Value Reason Code Name Description
0x00 Success 消息被接受。
0x10 No matching subscribers 消息被接受,但是当前没有匹配的订阅者。
0x80 Unspecified error 表示未指明的错误。当一方不希望向另一方透露错误的具体原因,或者协议规范中没有能够匹配当前情况的 Reason Code 时,那么它将在报文中使用这个 Reason Code。
0x83 Implementation specific error PUBLISH 报文有效,但是不被当前接收方的实现所接受。
0x87 Not authorized PUBLISH 报文没有通过服务端的权限检查,可能是因为当前客户端不具备向对应主题发布消息的权限。
0x90 Topic Name invalid 主题名的格式正确,但是不被接收端接受。
0x91 Packet identifier in use PUBLISH 报文中的 Packet ID 正在被使用,这通常意味着客户端和服务端的会话状态不匹配,或者有一方的实现不正确。
0x97 Quota exceeded 表示超出了配额限制。服务端可能会对发布端的发送配额进行限制,比如每天最多为其转发 1000 条消息。当发布端的配额耗尽,服务端就会在 PUBACK 等确认报文中使用这个 Reason Code 提醒对方。
0x99 Payload format invalid 表示有效载荷的格式与 Payload Format Indicator 属性所指示的格式不匹配。
  • 属性(Properties):下表列出了 PUBACK 报文的所有可用属性。
Identifier Property Name Type
0x1F Reason String UTF-8 编码的字符串
0x26 User Property UTF-8 字符串对

有效载荷

PUBACK 报文不包含有效载荷。

PUBREC、PUBREL、PUBCOMP 报文结构

PUBREC、PUBREL 和 PUBCOMP 的报文结构与 PUBACK 基本一致,它们的区别主要在于固定报头中报文类型字段的值,以及可以使用的原因码。

报文类型字段的值为 5(0b0101),表示这是一个 PUBREC 报文;值为 6(0b0110),则表示这是一个 PUBREL报文;值为 7(0b0111),则表示这是一个 PUBCOMP 报文。

PUBREC 作为 QoS 2 消息流程中对 PUBLISH 报文的确认报文,它可以使用的原因码与 PUBACK 完全一致。PUBREL 和 PUBCOMP 报文可用的原因码如下:

Identifier Reason Code Name Description
0x00 Success 由 QoS 2 消息的发送端在 PUBREL 报文中返回时,表示消息已经被释放,即之后将不会再重传该消息。由 QoS 2 消息的接收端在 PUBREC 报文中返回时,表示消息中使用的报文标识符已经释放,现在发送端可以使用该报文标识符发送新的消息。
0x92 Packet Identifier not found 表示收到了未知的报文标识符,这通常意味着当前服务端和客户端的会话状态不匹配。

总结

PUBLISH 报文中的主题决定了消息的流向,QoS 则决定了消息的可靠性,同时也决定了传输时将用到哪些报文,PUBACK 报文用于 QoS 1 消息,PUBREC、PUBREC 和 PUBCOMP 报文用于 QoS 2 消息。QoS 大于 0 时报文中还需要包含报文标识符来关联 PUBLISH 报文和它的响应报文。

PUBLISH 报文的有效载荷不限制数据类型,所以我们可以传输任意格式的应用消息。另外,属性可以满足我们在更多场景下的需要,比如主题别名可以减少每个消息的大小,消息过期间隔可以为有时效性的消息设置过期时间等等。

PUBLISH 报文的响应报文除了向发送端指示消息被接收以外,还能通过 Reason Code 进一步指示发布结果。所以当订阅端迟迟无法收到消息,我们还可以通过发布端收到的响应报文中的原因码来排查问题。

以上就是对 MQTT PUBLISH 及其响应报文的介绍,在下一篇文章中,我们将继续研究订阅和取消订阅报文的结构和组成。

相关实践学习
RocketMQ一站式入门使用
从源码编译、部署broker、部署namesrv,使用java客户端首发消息等一站式入门RocketMQ。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
EMQ
|
18天前
|
运维 Linux 网络性能优化
MQTT 5.0 报文解析 05:DISCONNECT
在 MQTT 中,客户端和服务端可以在断开网络连接前向对端发送一个 DISCONNECT 报文,来指示连接关闭的原因。客户端发送的 DISCONNECT 报文还可以影响服务端在连接断开后的行为,例如是否发送遗嘱消息,是否更新会话过期间隔。
EMQ
26 0
MQTT 5.0 报文解析 05:DISCONNECT
|
1天前
|
消息中间件 自然语言处理 负载均衡
RabbitMQ揭秘:轻量级消息队列的优缺点全解析
**RabbitMQ简介** RabbitMQ是源自电信行业的消息中间件,支持AMQP协议,提供轻量、快速且易于部署的解决方案。它拥有灵活的路由配置,广泛的语言支持,适用于异步处理、负载均衡、日志收集和微服务通信等场景。然而,当面临大量消息堆积或高吞吐量需求时,性能可能会下降,并且扩展和开发成本相对较高。
14 0
EMQ
|
12天前
|
安全 开发工具 数据安全/隐私保护
MQTT 5.0 报文解析 06:AUTH
MQTT 5.0 引入了增强认证特性,它使 MQTT 除了简单密码认证和 Token 认证以外,还能够支持质询/响应风格的认证。为了实现这一点,它在原先 CONNECT 和 CONNACK 报文的基础上,又引入了 AUTH 报文来实现任意多次的认证数据交换,以支持各种不同类型的认证机制,例如 SCRAM、Kerberos 认证等等。
EMQ
225 2
MQTT 5.0 报文解析 06:AUTH
EMQ
|
26天前
|
开发工具
MQTT 5.0 报文解析 04:PINGREQ 与 PINGRESP
除了用于连接、发布和订阅的控制报文,MQTT 还有一类报文用于在客户端和服务端之间模拟心跳,以达到保持连接的目的,它们分别是 PINGREQ 报文和 PINGRESP 报文,我们通常也会称它们为心跳报文。
EMQ
148 0
MQTT 5.0 报文解析 04:PINGREQ 与 PINGRESP
|
21小时前
|
消息中间件 Java 双11
RocketMQ:揭秘电商巨头背后的消息队列秘密
**RocketMQ概览:**高性能分布式消息队列,适用于有序消息、事务处理、流计算、消息推送、日志处理及Binlog分发。在双11等高流量场景下证明了其性能、稳定性和低延迟。Java开发,利于扩展,性能超RabbitMQ,支持死信队列,但可能有集成兼容性问题。适合Java开发者,为电商等场景优化,每秒处理大量消息。
19 3
RocketMQ:揭秘电商巨头背后的消息队列秘密
|
7天前
|
消息中间件 测试技术 开发工具
消息队列 MQ操作报错合集之收到"WARN RocketmqClient - consumeMessage Orderly return"警告,是什么原因
在使用消息队列MQ时,可能会遇到各种报错情况。以下是一些常见的错误场景、可能的原因以及解决建议的汇总:1.连接错误、2.消息发送失败、3.消息消费报错、4.消息重试与死信处理、5.资源与权限问题、6.配置错误、7.系统资源限制、8.版本兼容性问题。
|
7天前
|
消息中间件 设计模式 网络安全
消息队列 MQ操作报错合集之broker启用controller配置时,遇到报错,是什么导致的
在使用消息队列MQ时,可能会遇到各种报错情况。以下是一些常见的错误场景、可能的原因以及解决建议的汇总:1.连接错误、2.消息发送失败、3.消息消费报错、4.消息重试与死信处理、5.资源与权限问题、6.配置错误、7.系统资源限制、8.版本兼容性问题。
|
7天前
|
消息中间件 Java 测试技术
消息队列 MQ操作报错合集之设置了setKeepAliveInterval(1)但仍然出现客户端未连接,该怎么解决
在使用消息队列MQ时,可能会遇到各种报错情况。以下是一些常见的错误场景、可能的原因以及解决建议的汇总:1.连接错误、2.消息发送失败、3.消息消费报错、4.消息重试与死信处理、5.资源与权限问题、6.配置错误、7.系统资源限制、8.版本兼容性问题。
|
7天前
|
消息中间件 Apache RocketMQ
消息队列 MQ操作报错合集之设置了controller后,有一主一从,但只显示一个,该怎么解决
在使用消息队列MQ时,可能会遇到各种报错情况。以下是一些常见的错误场景、可能的原因以及解决建议的汇总:1.连接错误、2.消息发送失败、3.消息消费报错、4.消息重试与死信处理、5.资源与权限问题、6.配置错误、7.系统资源限制、8.版本兼容性问题。
|
7天前
|
消息中间件 网络安全 网络虚拟化
消息队列 MQ操作报错合集之如何实现公网访问内网RocketMQ集群
在使用消息队列MQ时,可能会遇到各种报错情况。以下是一些常见的错误场景、可能的原因以及解决建议的汇总:1.连接错误、2.消息发送失败、3.消息消费报错、4.消息重试与死信处理、5.资源与权限问题、6.配置错误、7.系统资源限制、8.版本兼容性问题。

相关产品

  • 云消息队列 MQ
  • 推荐镜像

    更多