第三章 MQTT控制报文 MQTT Control Packets
目录
- 第一章 – 介绍
- 第二章 – MQTT控制报文格式
- 第三章 – MQTT控制报文
- 第四章 – 操作行为
- 第五章 – 安全
- 第六章 – 使用WebSocket
- 第七章 – 一致性目标
- 附录B – 强制性规范声明
3.1 CONNECT – 连接服务端
客户端到服务端的网络连接建立后,客户端发送给服务端的第一个报文必须是CONNECT
报文 [MQTT-3.1.0-1]。
- 在一个网络连接上,客户端只能发送一次
CONNECT
报文。 - 服务端必须将客户端发送的第二个
CONNECT
报文当作协议违规处理并断开客户端的连接 [MQTT-3.1.0-2]。
有关错误处理的信息请查看4.8节。
有效载荷包含一个或多个编码的字段。包括客户端的唯一标识符,Will主题,Will消息,用户名和密码。除了客户端标识之外,其它的字段都是可选的,基于标志位来决定可变报头中是否需要包含这些字段。
3.1.1 固定报头 Fixed header
图例 3.1 – CONNECT报文的固定报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT报文类型 (1) | Reserved 保留位 | ||||||
0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | |
byte 2... | 剩余长度 |
剩余长度字段
剩余长度等于可变报头的长度(10字节)加上有效载荷的长度。
编码方式见 2.2.3节的说明。
3.1.2 可变报头 Variable header
CONNECT报文的可变报头按下列次序包含四个字段:协议名(Protocol Name)
,协议级别(Protocol Level)
,连接标志(Connect Flags)
和保持连接(Keep Alive)
。
协议名 Protocol Name
图例 3.2 -协议名字节构成
说明 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
协议名 | |||||||||
byte 1 | 长度 MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | 长度 LSB (4) | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
byte 3 | ‘M’ | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
byte 4 | ‘Q’ | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 |
byte 5 | ‘T’ | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
byte 6 | ‘T’ | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
协议名是表示协议名 MQTT 的UTF-8编码的字符串。MQTT规范的后续版本不会改变这个字符串的偏移和长度。
如果协议名不正确服务端可以断开客户端的连接,也可以按照某些其它规范继续处理CONNECT报文。对于后一种情况,按照本规范,服务端不能继续处理CONNECT报文 [MQTT-3.1.2-1]。
非规范评注
数据包检测工具,例如防火墙,可以使用协议名来识别MQTT流量。
协议级别 Protocol Level
图例 3.3 - Protocol Level byte协议级别字节构成
说明 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
协议级别 | |||||||||
byte 7 | Level(4) | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
客户端用8位的无符号值表示协议的修订版本。对于3.1.1版协议,协议级别字段的值是4(0x04)。如果发现不支持的协议级别,服务端必须给发送一个返回码为0x01(不支持的协议级别)的CONNACK报文响应CONNECT报文,然后断开客户端的连接 [MQTT-3.1.2-2]。
连接标志 Connect Flags
连接标志字节包含一些用于指定MQTT连接行为的参数。它还指出有效载荷中的字段是否存在。
图例 3.4 -连接标志位
服务端必须验证CONNECT控制报文的保留标志位(第0位)是否为0,如果不为0必须断开客户端连接 [MQTT-3.1.2-3]。
清理会话 Clean Session
位置: 连接标志字节的第1位
这个二进制位指定了会话状态的处理方式。
客户端和服务端可以保存会话状态,以支持跨网络连接的可靠消息传输。这个标志位用于控制会话状态的生存时间。
如果清理会话(CleanSession)标志被设置为0,服务端必须基于当前会话(使用客户端标识符识别)的状态恢复与客户端的通信。
如果没有与这个客户端标识符关联的会话,服务端必须创建一个新的会话。
在连接断开之后,当连接断开后,客户端和服务端必须保存会话信息 [MQTT-3.1.2-4]。
当清理会话标志为0的会话连接断开之后,服务端必须将之后的QoS 1和QoS 2级别的消息保存为会话状态的一部分,如果这些消息匹配断开连接时客户端的任何订阅 [MQTT-3.1.2-5]。
服务端也可以保存满足相同条件的QoS 0级别的消息。
如果清理会话(CleanSession)标志被设置为1,客户端和服务端必须丢弃之前的任何会话并开始一个新的会话。会话仅持续和网络连接同样长的时间。与这个会话关联的状态数据不能被任何之后的会话重用 [MQTT-3.1.2-6]。
客户端的会话状态包括:
- 已经发送给服务端,但是还没有完成确认的QoS 1和QoS 2级别的消息
- 已从服务端接收,但是还没有完成确认的QoS 2级别的消息。
服务端的会话状态包括:
- 会话是否存在,即使会话状态的其它部分都是空。
- 客户端的订阅信息。
- 已经发送给客户端,但是还没有完成确认的QoS 1和QoS 2级别的消息。
- 即将传输给客户端的QoS 1和QoS 2级别的消息。
- 已从客户端接收,但是还没有完成确认的QoS 2级别的消息。
- 可选,准备发送给客户端的QoS 0级别的消息。
保留消息不是服务端会话状态的一部分,会话终止时不能删除保留消息 [MQTT-3.1.2.7]。
有关状态存储的限制和细节见第 4.1节。
当清理会话标志被设置为1时,客户端和服务端的状态删除不需要是原子操作。
非规范评注
为了确保在发生故障时状态的一致性,客户端应该使用会话状态标志1重复请求连接,直到连接成功。
非规范评注
一般来说,客户端连接时总是将清理会话标志设置为0或1,并且不交替使用两种值。这个选择取决于具体的应用。清理会话标志设置为1的客户端不会收到旧的应用消息,而且在每次连接成功后都需要重新订阅任何相关的主题。清理会话标志设置为0的客户端会收到所有在它连接断开期间发布的QoS 1和QoS 2级别的消息。因此,要确保不丢失连接断开期间的消息,需要使用QoS 1或 QoS 2级别,同时将清理会话标志设置为0。
非规范评注
清理会话标志0的客户端连接时,它请求服务端在连接断开后保留它的MQTT会话状态。如果打算在之后的某个时间点重连到这个服务端,客户端连接应该只使用清理会话标志0。当客户端决定之后不再使用这个会话时,应该将清理会话标志设置为1最后再连接一次,然后断开连接。
遗嘱标志 Will Flag
位置: 连接标志的第2位。
遗嘱标志(Will Flag)被设置为1,表示如果连接请求被接受了,遗嘱(Will Message)消息必须被存储在服务端并且与这个网络连接关联。之后网络连接关闭时,服务端必须发布这个遗嘱消息,除非服务端收到DISCONNECT报文时删除了这个遗嘱消息 [MQTT-3.1.2-8] 。
遗嘱消息发布的条件,包括但不限于:
- 服务端检测到了一个I/O错误或者网络故障。
- 客户端在保持连接(Keep Alive)的时间内未能通讯。
- 客户端没有先发送DISCONNECT报文直接关闭了网络连接。
- 由于协议错误服务端关闭了网络连接。
如果遗嘱标志被设置为1,连接标志中的Will QoS和Will Retain字段会被服务端用到,同时有效载荷中必须包含Will Topic和Will Message字段 [MQTT-3.1.2-9]。
一旦被发布或者服务端收到了客户端发送的DISCONNECT报文,遗嘱消息就必须从存储的会话状态中移除 [MQTT-3.1.2-10]。
如果遗嘱标志被设置为0,连接标志中的Will QoS和Will Retain字段必须设置为0,并且有效载荷中不能包含Will Topic和Will Message字段 [MQTT-3.1.2-11]。
如果遗嘱标志被设置为0,网络连接断开时,不能发送遗嘱消息 [MQTT-3.1.2-12]。
服务端应该迅速发布遗嘱消息。在关机或故障的情况下,服务端可以推迟遗嘱消息的发布直到之后的重启。如果发生了这种情况,在服务器故障和遗嘱消息被发布之间可能会有一个延迟。
遗嘱QoS Will QoS
位置:连接标志的第4和第3位。
这两位用于指定发布遗嘱消息时使用的服务质量等级。
如果遗嘱标志被设置为0,遗嘱QoS也必须设置为0(0x00) [MQTT-3.1.2-13]。
如果遗嘱标志被设置为1,遗嘱QoS的值可以等于0(0x00),1(0x01),2(0x02)。它的值不能等于3 [MQTT-3.1.2-14]。
遗嘱保留 Will Retain
位置:连接标志的第5位。
如果遗嘱消息被发布时需要保留,需要指定这一位的值。
如果遗嘱标志被设置为0,遗嘱保留(Will Retain)标志也必须设置为0 [MQTT-3.1.2-15]。
如果遗嘱标志被设置为1:
-
如果遗嘱保留被设置为0,服务端必须将遗嘱消息当作非保留消息发布 [MQTT-3.1.2-16]。
-
如果遗嘱保留被设置为1,服务端必须将遗嘱消息当作保留消息发布 [MQTT-3.1.2-17]。
用户名标志 User Name Flag
位置:连接标志的第7位。
如果用户名(User Name)标志被设置为0,有效载荷中不能包含用户名字段 [MQTT-3.1.2-18]。
如果用户名(User Name)标志被设置为1,有效载荷中必须包含用户名字段 [MQTT-3.1.2-19]。
密码标志 Password Flag
位置:连接标志的第6位。
如果密码(Password)标志被设置为0,有效载荷中不能包含密码字段 [MQTT-3.1.2-20]。
如果密码(Password)标志被设置为1,有效载荷中必须包含密码字段 [MQTT-3.1.2-21]。
如果用户名标志被设置为0,密码标志也必须设置为0 [MQTT-3.1.2-22]。
保持连接 Keep Alive
图例 3.5保持连接字节
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
byte 9 | 保持连接 Keep Alive MSB | |||||||
byte 10 | 保持连接 Keep Alive LSB |
保持连接(Keep Alive)是一个以秒为单位的时间间隔,表示为一个16位的字,它是指在客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。客户端负责保证控制报文发送的时间间隔不超过保持连接的值。如果没有任何其它的控制报文可以发送,客户端必须发送一个PINGREQ报文 [MQTT-3.1.2-23]。
不管保持连接的值是多少,客户端任何时候都可以发送PINGREQ报文,并且使用PINGRESP报文判断网络和服务端的活动状态。
如果保持连接的值非零,并且服务端在一点五倍的保持连接时间内没有收到客户端的控制报文,它必须断开客户端的网络连接,认为网络连接已断开 [MQTT-3.1.2-24]。
客户端发送了PINGREQ报文之后,如果在合理的时间内仍没有收到PINGRESP报文,它应该关闭到服务端的网络连接。
保持连接的值为零表示关闭保持连接功能。这意味着,服务端不需要因为客户端不活跃而断开连接。注意:不管保持连接的值是多少,任何时候,只要服务端认为客户端是不活跃或无响应的,可以断开客户端的连接。
非规范评注
保持连接的实际值是由应用指定的,一般是几分钟。允许的最大值是18小时12分15秒。
可变报头非规范示例
图例 3.6 -可变报头非规范示例
3.1.3 有效载荷 Payload
CONNECT报文的有效载荷(payload)包含一个或多个以长度为前缀的字段,可变报头中的标志决定是否包含这些字段。如果包含的话,必须按这个顺序出现:客户端标识符,遗嘱主题,遗嘱消息,用户名,密码 [MQTT-3.1.3-1]。
客户端标识符 Client Identifier
服务端使用客户端标识符 (ClientId) 识别客户端。连接服务端的每个客户端都有唯一的客户端标识符(ClientId)。客户端和服务端都必须使用ClientId识别两者之间的MQTT会话相关的状态 [MQTT-3.1.3-2]。
客户端标识符 (ClientId) 必须存在而且必须是CONNECT报文有效载荷的第一个字段 [MQTT-3.1.3-3]。
客户端标识符必须是1.5.3节定义的UTF-8编码字符串 [MQTT-3.1.3-4]。
服务端必须允许1到23个字节长的UTF-8编码的客户端标识符,客户端标识符只能包含这些字符:“0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ”(大写字母,小写字母和数字)[MQTT-3.1.3-5]。
服务端可以允许编码后超过23个字节的客户端标识符 (ClientId)。服务端可以允许包含不是上面列表字符的客户端标识符 (ClientId)。
服务端可以允许客户端提供一个零字节的客户端标识符 (ClientId) ,如果这样做了,服务端必须将这看作特殊情况并分配唯一的客户端标识符给那个客户端。然后它必须假设客户端提供了那个唯一的客户端标识符,正常处理这个CONNECT报文 [MQTT-3.1.3-6]。
如果客户端提供了一个零字节的客户端标识符,它必须同时将清理会话标志设置为1 [MQTT-3.1.3-7]。
如果客户端提供的ClientId为零字节且清理会话标志为0,服务端必须发送返回码为0x02(表示标识符不合格)的CONNACK报文响应客户端的CONNECT报文,然后关闭网络连接 [MQTT-3.1.3-8]。
如果服务端拒绝了这个ClientId,它必须发送返回码为0x02(表示标识符不合格)的CONNACK报文响应客户端的CONNECT报文,然后关闭网络连接 [MQTT-3.1.3-9]。
非规范评注
客户端实现可以提供一个方便的方法用于生成随机的ClientId。当清理会话标志被设置为0时应该主动放弃使用这种方法。
遗嘱主题 Will Topic
如果遗嘱标志被设置为1,有效载荷的下一个字段是遗嘱主题(Will Topic)。遗嘱主题必须是 1.5.3节定义的UTF-8编码字符串 [MQTT-3.1.3-10]。
遗嘱消息 Will Message
如果遗嘱标志被设置为1,有效载荷的下一个字段是遗嘱消息。遗嘱消息定义了将被发布到遗嘱主题的应用消息,见3.1.2.5节的描述。这个字段由一个两字节的长度和遗嘱消息的有效载荷组成,表示为零字节或多个字节序列。长度给出了跟在后面的数据的字节数,不包含长度字段本身占用的两个字节。
遗嘱消息被发布到遗嘱主题时,它的有效载荷只包含这个字段的数据部分,不包含开头的两个长度字节。
用户名 User Name
如果用户名(User Name)标志被设置为1,有效载荷的下一个字段就是它。用户名必须是 1.5.3节定义的UTF-8编码字符串 [MQTT-3.1.3-11]。服务端可以将它用于身份验证和授权。
密码 Password
如果密码(Password)标志被设置为1,有效载荷的下一个字段就是它。密码字段包含一个两字节的长度字段,长度表示二进制数据的字节数(不包含长度字段本身占用的两个字节),后面跟着0到65535字节的二进制数据。
图例 3.7 - 密码字节
Bit | 7 - 0 |
---|---|
byte 1 | 数据长度 MSB |
byte 2 | 数据长度 LSB |
byte 3 …. | 如果长度大于0,这里就是数据部分 |
3.1.4 响应 Response
注意:服务器可以在同一个TCP端口或其他网络端点上支持多种协议(包括本协议的早期版本)。如果服务器确定协议是MQTT 3.1.1,那么它按照下面的方法验证连接请求。
- 网络连接建立后,如果服务端在合理的时间内没有收到CONNECT报文,服务端应该关闭这个连接。
- 服务端必须按照3.1节的要求验证CONNECT报文,如果报文不符合规范,服务端不发送CONNACK报文直接关闭网络连接 [MQTT-3.1.4-1]。
- 服务端可以检查CONNECT报文的内容是不是满足任何进一步的限制,可以执行身份验证和授权检查。如果任何一项检查没通过,按照3.2节的描述,它应该发送一个适当的、返回码非零的CONNACK响应,并且必须关闭这个网络连接。
如果验证成功,服务端会执行下列步骤。
- 如果ClientId表明客户端已经连接到这个服务端,那么服务端必须断开原有的客户端连接 [MQTT-3.1.4-2]。
- 服务端必须按照 3.1.2.4节的描述执行清理会话的过程 [MQTT-3.1.4-3]。
- 服务端必须发送返回码为零的CONNACK报文作为CONNECT报文的确认响应 [MQTT-3.1.4-4]。
- 开始消息分发和保持连接状态监视。
允许客户端在发送CONNECT报文之后立即发送其它的控制报文;客户端不需要等待服务端的CONNACK报文。如果服务端拒绝了CONNECT,它不能处理客户端在CONNECT报文之后发送的任何数据 [MQTT-3.1.4-5]。
非规范评注
客户端通常会等待一个CONNACK报文。然而客户端有权在收到CONNACK之前发送控制报文,由于不需要维持连接状态,这可以简化客户端的实现。
3.2 CONNACK – 确认连接请求
服务端发送CONNACK报文响应从客户端收到的CONNECT报文。服务端发送给客户端的第一个报文必须是CONNACK [MQTT-3.2.0-1]。
如果客户端在合理的时间内没有收到服务端的CONNACK报文,客户端应该关闭网络连接。合理 的时间取决于应用的类型和通信基础设施。
3.2.1 固定报头
固定报头的格式见 图例 3.8 – CONNACK 报文固定报头 的描述。
图例 3.8 – CONNACK 报文固定报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT报文类型 (2) | Reserved 保留位 | ||||||
0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | |
byte 2... | 剩余长度 (2) | |||||||
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
剩余长度字段
表示可变报头的长度。对于CONNACK报文这个值等于2。
3.2.2 可变报头
可变报头的格式见 图例 3.9 –CONNACK报文可变报头 的描述。
图例 3.9 –CONNACK报文可变报头
描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
连接确认标志 | Reserved 保留位 | SP1 | |||||||
byte 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | X | |
连接返回码 | |||||||||
byte 2 | X | X | X | X | X | X | X | X |
描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
连接确认标志 | Reserved 保留位 | SP1 | |||||||
byte 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | X | |
连接返回码 | |||||||||
byte 2 | X | X | X | X | X | X | X | X |
连接确认标志 Connect Acknowledge Flags
第1个字节是 连接确认标志,位7-1是保留位且必须设置为0。 第0 (SP)位 是当前会话(Session Present)标志。
当前会话 Session Present
位置:连接确认标志的第0位。
如果服务端收到清理会话(CleanSession)标志为1的连接,除了将CONNACK报文中的返回码设置为0之外,还必须将CONNACK报文中的当前会话设置(Session Present)标志为0 [MQTT-3.2.2-1]。
如果服务端收到一个CleanSession为0的连接,当前会话标志的值取决于服务端是否已经保存了ClientId对应客户端的会话状态。如果服务端已经保存了会话状态,它必须将CONNACK报文中的当前会话标志设置为1 [MQTT-3.2.2-2]。如果服务端没有已保存的会话状态,它必须将CONNACK报文中的当前会话设置为0。还需要将CONNACK报文中的返回码设置为0 [MQTT-3.2.2-3]。
当前会话标志使服务端和客户端在是否有已存储的会话状态上保持一致。
一旦完成了会话的初始化设置,已经保存会话状态的客户端将期望服务端维持它存储的会话状态。如果客户端从服务端收到的当前的值与预期的不同,客户端可以选择继续这个会话或者断开连接。客户端可以丢弃客户端和服务端之间的会话状态,方法是,断开连接,将清理会话标志设置为1,再次连接,然后再次断开连接。
如果服务端发送了一个包含非零返回码的CONNACK报文,它必须将当前会话标志设置为0 [MQTT-3.2.2-4]。
连接返回码 Connect Return code
位置:可变报头的第2个字节。
连接返回码字段使用一个字节的无符号值,在 表格 3.1 –连接返回码的值 中列出。如果服务端收到一个合法的CONNECT报文,但出于某些原因无法处理它,服务端应该尝试发送一个包含非零返回码(表格中的某一个)的CONNACK报文。如果服务端发送了一个包含非零返回码的CONNACK报文,那么它必须关闭网络连接 [MQTT-3.2.2-5].。
表格 3.1 –连接返回码的值
值 | 返回码响应 | 描述 |
---|---|---|
0 | 0x00连接已接受 | 连接已被服务端接受 |
1 | 0x01连接已拒绝,不支持的协议版本 | 服务端不支持客户端请求的MQTT协议级别 |
2 | 0x02连接已拒绝,不合格的客户端标识符 | 客户端标识符是正确的UTF-8编码,但服务端不允许使用 |
3 | 0x03连接已拒绝,服务端不可用 | 网络连接已建立,但MQTT服务不可用 |
4 | 0x04连接已拒绝,无效的用户名或密码 | 用户名或密码的数据格式无效 |
5 | 0x05连接已拒绝,未授权 | 客户端未被授权连接到此服务器 |
6-255 | 保留 |
如果认为上表中的所有连接返回码都不太合适,那么服务端必须关闭网络连接,不需要发送CONNACK报文 [MQTT-3.2.2-6]。
3.2.3 有效载荷
CONNACK报文没有有效载荷。
3.3 PUBLISH – 发布消息
PUBLISH控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。
3.3.1 固定报头
图例 3.10 – PUBLISH报文固定报头描述了固定报头的格式
图例 3.10 – PUBLISH报文固定报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (3) | DUP | QoS-H | QoS- | RETAIN | |||
0 | 0 | 1 | 1 | X | X | X | X | |
byte 2... | 剩余长度 |
重发标志 DUP
位置:第1个字节,第3位
如果DUP标志被设置为0,表示这是客户端或服务端第一次请求发送这个PUBLISH报文。如果DUP标志被设置为1,表示这可能是一个早前报文请求的重发。
客户端或服务端请求重发一个PUBLISH报文时,必须将DUP标志设置为1 [MQTT-3.3.1.-1].。对于QoS 0的消息,DUP标志必须设置为0 [MQTT-3.3.1-2]。
服务端发送PUBLISH报文给订阅者时,收到(入站)的PUBLISH报文的DUP标志的值不会被传播。发送(出站)的PUBLISH报文与收到(入站)的PUBLISH报文中的DUP标志是独立设置的,它的值必须单独的根据发送(出站)的PUBLISH报文是否是一个重发来确定 [MQTT-3.3.1-3]。
非规范评注
接收者收到一个DUP标志为1的控制报文时,不能假设它看到了一个这个报文之前的一个副本。
非规范评注
需要特别指出的是,DUP标志关注的是控制报文本身,与它包含的应用消息无关。当使用QoS 1时,客户端可能会收到一个DUP标志为0的PUBLISH报文,这个报文包含一个它之前收到过的应用消息的副本,但是用的是不同的报文标识符。 2.3.1节提供了有关报文标识符的更多信息。
服务质量等级 QoS
位置: 第1个字节,第2-1位。
这个字段表示应用消息分发的服务质量等级保证。服务质量等级在 表格 3.2 -服务质量定义 中列出。
表格 3.2 -服务质量定义
QoS值 | Bit 2 | Bit 1 | 描述 |
---|---|---|---|
0 | 0 | 0 | 最多分发一次 |
1 | 0 | 1 | 至少分发一次 |
2 | 1 | 0 | 只分发一次 |
- | 1 | 1 | 保留位 |
PUBLISH报文不能将QoS所有的位设置为1。如果服务端或客户端收到QoS所有位都为1的PUBLISH报文,它必须关闭网络连接 [MQTT-3.3.1-4]。
保留标志 RETAIN
位置: 第1个字节,第0位。
如果客户端发给服务端的PUBLISH报文的保留(RETAIN)标志被设置为1,服务端必须存储这个应用消息和它的服务质量等级(QoS),以便它可以被分发给未来的主题名匹配的订阅者 [MQTT-3.3.1-5]。一个新的订阅建立时,对每个匹配的主题名,如果存在最近保留的消息,它必须被发送给这个订阅者 [MQTT-3.3.1-6]。如果服务端收到一条保留(RETAIN)标志为1的QoS 0消息,它必须丢弃之前为那个主题保留的任何消息。它应该将这个新的QoS 0消息当作那个主题的新保留消息,但是任何时候都可以选择丢弃它 — 如果这种情况发生了,那个主题将没有保留消息 [MQTT-3.3.1-7]。有关存储状态的更多信息见 4.1节。
服务端发送PUBLISH报文给客户端时,如果消息是作为客户端一个新订阅的结果发送,它必须将报文的保留标志设为1 [MQTT-3.3.1-8]。当一个PUBLISH报文发送给客户端是因为匹配一个已建立的订阅时,服务端必须将保留标志设为0,不管它收到的这个消息中保留标志的值是多少 [MQTT-3.3.1-9]。
保留标志为1且有效载荷为零字节的PUBLISH报文会被服务端当作正常消息处理,它会被发送给订阅主题匹配的客户端。此外,同一个主题下任何现存的保留消息必须被移除,因此这个主题之后的任何订阅者都不会收到一个保留消息 [MQTT-3.3.1-10]。当作正常 意思是现存的客户端收到的消息中保留标志未被设置。服务端不能存储零字节的保留消息 [MQTT-3.3.1-11]。
如果客户端发给服务端的PUBLISH报文的保留标志位0,服务端不能存储这个消息也不能移除或替换任何现存的保留消息 [MQTT-3.3.1-12]。
非规范评注
对于发布者不定期发送状态消息这个场景,保留消息很有用。新的订阅者将会收到最近的状态。
剩余长度字段
等于可变报头的长度加上有效载荷的长度。
3.3.2 可变报头
可变报头按顺序包含主题名和报文标识符。
主题名 Topic Name
主题名(Topic Name)用于识别有效载荷数据应该被发布到哪一个信息通道。
主题名必须是PUBLISH报文可变报头的第一个字段。它必须是 1.5.3节定义的UTF-8编码的字符串 [MQTT-3.3.2-1]。
PUBLISH报文中的主题名不能包含通配符 [MQTT-3.3.2-2]。
服务端发送给订阅客户端的PUBLISH报文的主题名必须匹配该订阅的主题过滤器(根据 4.7节定义的匹配过程)[MQTT-3.3.2-3]。
报文标识符 Packet Identifier
只有当QoS等级是1或2时,报文标识符(Packet Identifier)字段才能出现在PUBLISH报文中。2.3.1节提供了有关报文标识符的更多信息。
可变报头非规范示例
图例 3.11 – PUBLISH报文可变报头非规范示例 举例说明了 表格 3.3 - PUBLISH报文非规范示例 中简要描述的PUBLISH报文的可变报头。
表格 3.3 - PUBLISH报文非规范示例
Field | Value |
---|---|
主题名 | a/b |
报文标识符 | 10 |
图例 3.11 – PUBLISH报文可变报头非规范示例
描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
Topic Name 主题名 | |||||||||
byte 1 | Length MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | Length LSB (3) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
byte 3 | ‘a’ (0x61) | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
byte 4 | ‘/’ (0x2F) | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 |
byte 5 | ‘b’ (0x62) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 |
报文标识符 | |||||||||
byte 6 | 报文标识符 MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 7 | 报文标识符 LSB (10) | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
示例中的主题名为 “a/b”,长度等于3,报文标识符为 “10”
3.3.3 有效载荷
有效载荷包含将被发布的应用消息。数据的内容和格式是应用特定的。有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度。包含零长度有效载荷的PUBLISH报文是合法的。
3.3.4 响应
PUBLISH报文的接收者必须按照根据PUBLISH报文中的QoS等级发送响应,见下面表格的描述 [MQTT-3.3.4-1]。
表格 3.4 – PUBLISH报文的预期响应
服务质量等级 | 预期响应 |
---|---|
QoS 0 | 无响应 |
QoS 1 | PUBACK报文 |
QoS 2 | PUBREC报文 |
3.3.5 动作 Actions
客户端使用PUBLISH报文发送应用消息给服务端,目的是分发到其它订阅匹配的客户端。
服务端使用PUBLISH报文发送应用消息给每一个订阅匹配的客户端。
客户端使用带通配符的主题过滤器请求订阅时,客户端的订阅可能会重复,因此发布的消息可能会匹配多个过滤器。对于这种情况,服务端必须将消息分发给所有订阅匹配的QoS等级最高的客户端 [MQTT-3.3.5-1]。服务端之后可以按照订阅的QoS等级,分发消息的副本给每一个匹配的订阅者。
收到一个PUBLISH报文时,接收者的动作取决于4.3节描述的QoS等级。
如果服务端实现不授权某个客户端发布PUBLISH报文,它没有办法通知那个客户端。它必须按照正常的QoS规则发送一个正面的确认,或者关闭网络连接 [MQTT-3.3.5-2]。
3.4 PUBACK – 发布确认
PUBACK报文是对QoS 1等级的PUBLISH报文的响应。
3.4.1 固定报头
图例 3.12 - PUBACK报文固定报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT报文类型 (4) | 保留位 | ||||||
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | |
byte 2... | 剩余长度 | |||||||
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
剩余长度字段
表示可变报头的长度。对PUBACK报文这个值等于2.
3.4.2 可变报头
包含等待确认的PUBLISH报文的报文标识符。
图例 3.13 – PUBACK报文可变报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
3.4.3 有效载荷
PUBACK报文没有有效载荷。
3.4.4 动作
完整的描述见 4.3.2节。
3.5 PUBREC – 发布收到(QoS 2,第一步)
PUBREC报文是对QoS等级2的PUBLISH报文的响应。它是QoS 2等级协议交换的第二个报文。
3.5.1 固定报头
图例 3.14 – PUBREC报文固定报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (5) | 保留位 | ||||||
0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | |
byte 2 | 剩余长度 (2) | |||||||
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
剩余长度字段
表示可变报头的长度。对PUBREC报文它的值等于2。
3.5.2 可变报头
可变报头包含等待确认的PUBLISH报文的报文标识符。
图例 3.15 – PUBREC报文可变报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
3.5.3 有效载荷
PUBREC报文没有有效载荷。
3.5.4 动作
完整的描述见 4.3.3节。
3.6 PUBREL – 发布释放(QoS 2,第二步)
PUBREL报文是对PUBREC报文的响应。它是QoS 2等级协议交换的第三个报文。
3.6.1 固定报头
图例 3.16 – PUBREL报文固定报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (6) | 保留位 | ||||||
1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | |
byte 2 | 剩余长度 (2) | |||||||
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
PUBREL控制报文固定报头的第3,2,1,0位是保留位,必须被设置为0,0,1,0。服务端必须将其它的任何值都当做是不合法的并关闭网络连接 [MQTT-3.6.1-1]。
剩余长度字段
表示可变报头的长度。对PUBREL报文这个值等于2.
3.6.2 可变报头
可变报头包含与等待确认的PUBREC报文相同的报文标识符。
图例 3.17 – PUBREL报文可变报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
3.6.3 有效载荷
PUBREL报文没有有效载荷。
3.6.4 动作
完整的描述见 4.3.3节。
3.7 PUBCOMP – 发布完成(QoS 2,第三步)
PUBCOMP报文是对PUBREL报文的响应。它是QoS 2等级协议交换的第四个也是最后一个报文。
3.7.1 固定报头
图例 3.18 – PUBCOMP报文固定报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (7) | 保留位 | ||||||
0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | |
byte 2 | 剩余长度 (2) | |||||||
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
剩余长度字段
表示可变报头的长度。对PUBCOMP报文这个值等于2。
3.7.2 可变报头
可变报头包含与等待确认的PUBREL报文相同的报文标识符。
图例 3.19 – PUBCOMP报文可变报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
3.7.3 有效载荷
PUBCOMP报文没有有效载荷。
3.7.4 动作
完整的描述见4.3.3节。
3.8 SUBSCRIBE - 订阅主题
客户端向服务端发送SUBSCRIBE报文用于创建一个或多个订阅。每个订阅注册客户端关心的一个或多个主题。为了将应用消息转发给与那些订阅匹配的主题,服务端发送PUBLISH报文给客户端。SUBSCRIBE报文也(为每个订阅)指定了最大的QoS等级,服务端根据这个发送应用消息给客户端。
3.8.1 固定报头
图例 3.20 – SUBSCRIBE报文固定报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
byte 1 | MQTT控制报文类型 (8) | 保留位 | ||||||
1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | |
byte 2 | 剩余长度 |
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (8) | 保留位 | ||||||
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
byte 2 | 剩余长度 |
SUBSCRIBE控制报固定报头的第3,2,1,0位是保留位,必须分别设置为0,0,1,0。服务端必须将其它的任何值都当做是不合法的并关闭网络连接 [MQTT-3.8.1-1]。
剩余长度字段
等于可变报头的长度(2字节)加上有效载荷的长度。
3.8.2可变报头
可变报头包含报文标识符。2.3.1提供了有关报文标识符的更多信息。
可变报头非规范示例
图例 3.21 – 报文标识符等于10的可变报头,非规范示例 展示了报文标识符设置为10时的可变报头。
图例 3.21 – 报文标识符等于10的可变报头,非规范示例
描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
报文标识符 | |||||||||
byte 1 | 报文标识符 MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | 报文标识符 LSB (10) | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
3.8.3 有效载荷
SUBSCRIBE报文的有效载荷包含了一个主题过滤器列表,它们表示客户端想要订阅的主题。SUBSCRIBE报文有效载荷中的主题过滤器列表必须是1.5.3节定义的UTF-8字符串 [MQTT-3.8.3-1]。服务端应该支持包含通配符(4.7.1节定义的)的主题过滤器。如果服务端选择不支持包含通配符的主题过滤器,必须拒绝任何包含通配符过滤器的订阅请求 [MQTT-3.8.3-2]。每一个过滤器后面跟着一个字节,这个字节被叫做 服务质量要求(Requested QoS)。它给出了服务端向客户端发送应用消息所允许的最大QoS等级。
SUBSCRIBE报文的有效载荷必须包含至少一对主题过滤器 和 QoS等级字段组合。没有有效载荷的SUBSCRIBE报文是违反协议的 [MQTT-3.8.3-3]。有关错误处理的信息请查看4.8节。
请求的最大服务质量等级字段编码为一个字节,它后面跟着UTF-8编码的主题名,那些主题过滤器 /和QoS等级组合是连续地打包。
图例 3.22 – SUBSCRIBE报文有效载荷格式
描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
主题过滤器 | ||||||||
byte 1 | 长度 MSB | |||||||
byte 2 | 长度 LSB | |||||||
bytes 3..N | 主题过滤器(Topic Filter) | |||||||
服务质量要求(Requested QoS) | ||||||||
保留位 | 服务质量等级 | |||||||
byte N+1 | 0 | 0 | 0 | 0 | 0 | 0 | X | X |
描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
主题过滤器 | ||||||||
byte 1 | 长度 MSB | |||||||
byte 2 | 长度 LSB | |||||||
byte 3..N | 主题过滤器(Topic Filter) | |||||||
服务质量要求(Requested QoS) | ||||||||
保留位 | 服务质量等级 | |||||||
byte N+1 | 0 | 0 | 0 | 0 | 0 | 0 | X | X |
当前版本的协议没有用到服务质量要求(Requested QoS)字节的高六位。如果有效载荷中的任何位是非零值,或者QoS不等于0,1或2,服务端必须认为SUBSCRIBE报文是不合法的并关闭网络连接 [MQTT-3-8.3-4]。
有效载荷非规范示例
图例 3.23 – 有效载荷字节格式非规范示例 展示了 表格 3.5 – 有效载荷非规范示例 中简略描述的SUBSCRIBE报文的有效载荷。
表格 3.5 – 有效载荷非规范示例
主题名 | “a/b” |
---|---|
服务质量要求 | 0x01 |
主题名 | “c/d” |
服务质量要求 | 0x02 |
图例 3.23 – 有效载荷字节格式非规范示例
描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
主题过滤器(Topic Filter) | |||||||||
byte 1 | Length MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | Length LSB (3) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
byte 3 | ‘a’ (0x61) | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
byte 4 | ‘/’ (0x2F) | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 |
byte 5 | ‘b’ (0x62) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 |
服务质量要求(Requested QoS) | |||||||||
byte 6 | Requested QoS(1) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
主题过滤器(Topic Filter) | |||||||||
byte 7 | Length MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 8 | Length LSB (3) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
byte 9 | ‘c’ (0x63) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 |
byte 10 | ‘/’ (0x2F) | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 |
byte 11 | ‘d’ (0x64) | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
服务质量要求(Requested QoS) | |||||||||
byte 12 | Requested QoS(2) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
3.8.4 响应
服务端收到客户端发送的一个SUBSCRIBE报文时,必须使用SUBACK报文响应 [MQTT-3.8.4-1]。SUBACK报文必须和等待确认的SUBSCRIBE报文有相同的报文标识符 [MQTT-3.8.4-2]。
允许服务端在发送SUBACK报文之前就开始发送与订阅匹配的PUBLISH报文。
如果服务端收到一个SUBSCRIBE报文,报文的主题过滤器与一个现存订阅的主题过滤器相同,那么必须使用新的订阅彻底替换现存的订阅。新订阅的主题过滤器和之前订阅的相同,但是它的最大QoS值可以不同。与这个主题过滤器匹配的任何现存的保留消息必须被重发,但是发布流程不能中断 [MQTT-3.8.4-3]。
如果主题过滤器不同于任何现存订阅的过滤器,服务端会创建一个新的订阅并发送所有匹配的保留消息。
如果服务端收到包含多个主题过滤器的SUBSCRIBE报文,它必须如同收到了一系列的多个SUBSCRIBE报文一样处理那个,除了需要将它们的响应合并到一个单独的SUBACK报文发送 [MQTT-3.8.4-4]。
服务端发送给客户端的SUBACK报文对每一对主题过滤器 和QoS等级都必须包含一个返回码。这个返回码必须表示那个订阅被授予的最大QoS等级,或者表示这个订阅失败 [MQTT-3.8.4-5]。服务端可以授予比订阅者要求的低一些的QoS等级。为响应订阅而发出的消息的有效载荷的QoS必须是原始发布消息的QoS和服务端授予的QoS两者中的最小值。如果原始消息的QoS是1而被授予的最大QoS是0,允许服务端重复发送一个消息的副本给订阅者 [MQTT-3.8.4-6]。
非规范示例 对某个特定的主题过滤器,如果正在订阅的客户端被授予的最大QoS等级是1,那么匹配这个过滤器的QoS等级0的应用消息会按QoS等级0分发给这个客户端。这意味着客户端最多收到这个消息的一个副本。从另一方面说,发布给同一主题的QoS等级2的消息会被服务端降级到QoS等级1再分发给客户端,因此客户端可能会收到重复的消息副本。
如果正在订阅的客户端被授予的最大QoS等级是0,那么原来按QoS等级2发布给客户端的应用消息在繁忙时可能会丢失,但是服务端不应该发送重复的消息副本。发布给同一主题的 QoS等级1的消息在传输给客户端时可能会丢失或重复。
非规范评注
使用QoS等级2订阅一个主题过滤器等于是说:我想要按照它们发布时的QoS等级接受匹配这个过滤器的消息 。这意味着,确定消息分发时可能的最大QoS等级是发布者的责任,而订阅者可以要求服务端降低QoS到更适合它的等级。
3.9 SUBACK – 订阅确认
服务端发送SUBACK报文给客户端,用于确认它已收到并且正在处理SUBSCRIBE报文。
SUBACK报文包含一个返回码清单,它们指定了SUBSCRIBE请求的每个订阅被授予的最大QoS等级。
3.9.1 固定报头
图例 3.24 – SUBACK报文固定报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (9) | 保留位 | ||||||
1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | |
byte 2 | 剩余长度 |
剩余长度字段
等于可变报头的长度加上有效载荷的长度。
3.9.2 可变报头
可变报头包含等待确认的SUBSCRIBE报文的报文标识符。图例 3.25 – SUBACK报文可变报头 描述了可变报头的格式。
图例 3.25 – SUBACK报文可变报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
3.9.3 有效载荷
有效载荷包含一个返回码清单。每个返回码对应等待确认的SUBSCRIBE报文中的一个主题过滤器。返回码的顺序必须和SUBSCRIBE报文中主题过滤器的顺序相同 [MQTT-3.9.3-1]。
图例 3.26 – SUBACK报文有效载荷格式 描述了有效载荷中单字节编码的返回码字段。
图例 3.26 – SUBACK报文有效载荷格式
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
返回码 | ||||||||
byte 1 | X | 0 | 0 | 0 | 0 | 0 | X | X |
允许的返回码值:
- 0x00 - 最大QoS 0
- 0x01 - 成功 – 最大QoS 1
- 0x02 - 成功 – 最大 QoS 2
- 0x80 - Failure 失败
0x00, 0x01, 0x02, 0x80之外的SUBACK返回码是保留的,不能使用[MQTT-3.9.3-2]。
有效载荷非规范示例
图例 3.27 -有效载荷字节格式非规范示例 展示了在 表格 3.6 -有效载荷非规范示例 简要描述的SUBACK报文的有效载荷。
表格 3.6 -有效载荷非规范示例
Success - Maximum QoS 0 | 0 |
---|---|
Success - Maximum QoS 2 | 2 |
Failure | 128 |
图例 3.27 -有效载荷字节格式非规范示例
描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
byte 1 | Success - Maximum QoS 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | Success - Maximum QoS 2 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
byte 3 | Failure | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
3.10 UNSUBSCRIBE – 取消订阅
客户端发送UNSUBSCRIBE报文给服务端,用于取消订阅主题。
3.10.1 固定报头
图例 3.28 – UNSUBSCRIBE报文固定报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (10) | 保留位 | ||||||
1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | |
byte 2 | 剩余长度 |
UNSUBSCRIBE报文固定报头的第3,2,1,0位是保留位且必须分别设置为0,0,1,0。服务端必须认为任何其它的值都是不合法的并关闭网络连接 [MQTT-3.10.1-1]。
剩余长度字段
等于可变报头的长度加上有效载荷的长度。
3.10.2 可变报头
可变报头包含一个报文标识符。2.3.1节提供了有关报文标识符的更多信息。
图例 3.29 – UNSUBSCRIBE报文可变报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
3.10.3 有效载荷
UNSUBSCRIBE报文的有效载荷包含客户端想要取消订阅的主题过滤器列表。UNSUBSCRIBE报文中的主题过滤器必须是连续打包的、按照1.5.3节定义的UTF-8编码字符串 [MQTT-3.10.3-1]。
UNSUBSCRIBE报文的有效载荷必须至少包含一个消息过滤器。没有有效载荷的UNSUBSCRIBE报文是违反协议的 [MQTT-3.10.3-2]。有关错误处理的更多信息请查看4.8节。
有效载荷非规范示例
图例 3.30 -有效载荷字节格式非规范示例 展示了 表格 3.7 -有效载荷非规范示例 简要描述的UNSUBSCRIBE报文的有效载荷。
表格 3.7 -有效载荷非规范示例
主题过滤器 | “a/b” |
---|---|
主题过滤器 | “c/d” |
图例 3.30 -有效载荷字节格式非规范示例
描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
主题过滤器 | |||||||||
byte 1 | Length MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | Length LSB (3) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
byte 3 | ‘a’ (0x61) | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
byte 4 | ‘/’ (0x2F) | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 |
byte 5 | ‘b’ (0x62) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 |
主题过滤器 | |||||||||
byte 6 | Length MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 7 | Length LSB (3) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
byte 8 | ‘c’ (0x63) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 |
byte 9 | ‘/’ (0x2F) | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 |
byte 10 | ‘d’ (0x64) | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
3.10.4 响应
UNSUBSCRIBE报文提供的主题过滤器(无论是否包含通配符)必须与服务端持有的这个客户端的当前主题过滤器集合逐个字符比较。如果有任何过滤器完全匹配,那么它(服务端)自己的订阅将被删除,否则不会有进一步的处理 [MQTT-3.10.4-1]。
如果服务端删除了一个订阅:
- 它必须停止分发任何新消息给这个客户端 [MQTT-3.10.4-2]。
- 它必须完成分发任何已经开始往客户端发送的QoS 1和QoS 2的消息 [MQTT-3.10.4-3]。
- 它可以继续发送任何现存的准备分发给客户端的缓存消息。
服务端必须发送UNSUBACK报文响应客户端的UNSUBSCRIBE请求。UNSUBACK报文必须包含和UNSUBSCRIBE报文相同的报文标识符 [MQTT-3.10.4-4]。即使没有删除任何主题订阅,服务端也必须发送一个UNSUBACK响应 [MQTT-3.10.4-5]。
如果服务端收到包含多个主题过滤器的UNSUBSCRIBE报文,它必须如同收到了一系列的多个UNSUBSCRIBE报文一样处理那个报文,除了将它们的响应合并到一个单独的UNSUBACK报文外。 [MQTT-3.10.4-6]。
3.11 UNSUBACK – 取消订阅确认
服务端发送UNSUBACK报文给客户端用于确认收到UNSUBSCRIBE报文。
3.11.1 固定报头
图例 3.31 – UNSUBACK报文固定报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
byte 1 | MQTT控制报文类型 (11) | 保留位 | ||||||
1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | |
byte 2 | 剩余长度 (2) | |||||||
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (11) | 保留位 | ||||||
1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | |
byte 2 | 剩余长度 (2) | |||||||
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
剩余长度字段
表示可变报头的长度,对UNSUBACK报文这个值等于2。
3.11.2 可变报头
可变报头包含等待确认的UNSUBSCRIBE报文的报文标识符。
图例 3.32 – UNSUBACK报文可变报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
3.11.3 有效载荷
UNSUBACK报文没有有效载荷。
3.12 PINGREQ – 心跳请求
客户端发送PINGREQ报文给服务端的。用于:
- 在没有任何其它控制报文从客户端发给服务的时,告知服务端客户端还活着。
- 请求服务端发送 响应确认它还活着。
- 使用网络以确认网络连接没有断开。
保持连接(Keep Alive)处理中用到这个报文,详细信息请查看 3.1.2.10节。
3.12.1 固定报头
图例 3.33 – PINGREQ报文固定报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
byte 1 | MQTT控制报文类型 (12) | 保留位 | ||||||
1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | |
byte 2 | 剩余长度 (0) | |||||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (12) | 保留位 | ||||||
1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | |
byte 2 | 剩余长度 (0) | |||||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
3.12.2 可变报头
PINGREQ报文没有可变报头。
3.12.3 有效载荷
PINGREQ报文没有有效载荷。
3.12.4 响应
服务端必须发送 PINGRESP报文响应客户端的PINGREQ报文 [MQTT-3.12.4-1]。
3.13 PINGRESP – 心跳响应
服务端发送PINGRESP报文响应客户端的PINGREQ报文。表示服务端还活着。
保持连接(Keep Alive)处理中用到这个报文,详情请查看 3.1.2.10节。
3.13.1 固定报头
图例 3.34 – PINGRESP报文固定报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (13) | 保留位 | ||||||
1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | |
byte 2 | 剩余长度 (0) | |||||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
3.13.2 可变报头
PINGRESP报文没有可变报头。
3.13.3 有效载荷
PINGRESP报文没有有效载荷。
3.14 DISCONNECT –断开连接
DISCONNECT报文是客户端发给服务端的最后一个控制报文。表示客户端正常断开连接。
3.14.1 固定报头
图例 3.35 – DISCONNECT报文固定报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (14) | 保留位 | ||||||
1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | |
byte 2 | 剩余长度 (0) | |||||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
服务端必须验证所有的保留位都被设置为0,如果它们不为0必须断开连接 [MQTT-3.14.1-1]。
3.14.2 可变报头
DISCONNECT报文没有可变报头。
3.14.3 有效载荷
DISCONNECT报文没有有效载荷。
3.14.4 响应
客户端发送DISCONNECT报文之后:
- 必须关闭网络连接 [MQTT-3.14.4-1]。
- 不能通过那个网络连接再发送任何控制报文 [MQTT-3.14.4-2]。
服务端在收到DISCONNECT报文时:
- 必须丢弃任何与当前连接关联的未发布的遗嘱消息,具体描述见 3.1.2.5节 [MQTT-3.14.4-3]。
- 应该关闭网络连接,如果客户端 还没有这么做。