第一章 概述

1.1 术语

网络连接 Network Connection

MQTT使用的底层传输协议基础设施。

  • 客户端使用它连接服务端。
  • 它提供有序的、可靠的、双向字节流传输。

应用消息 Application Message MQTT协议通过网络传输应用数据。应用消息通过MQTT传输时,它们有关联的服务质量(QoS)和主题(Topic)。

客户端 Client

使用MQTT的程序或设备。客户端总是通过网络连接到服务端。它可以

  • 发布应用消息给其它相关的客户端。
  • 订阅以请求接受相关的应用消息。
  • 取消订阅以移除接受应用消息的请求。
  • 从服务端断开连接。

服务端 Server

一个程序或设备,作为发送消息的客户端和请求订阅的客户端之间的中介。服务端

  • 接受来自客户端的网络连接。
  • 接受客户端发布的应用消息。
  • 处理客户端的订阅和取消订阅请求。
  • 转发应用消息给符合条件的已订阅客户端。

订阅 Subscription

订阅包含一个主题过滤器(Topic Filter)和一个最大的服务质量(QoS)等级。订阅与单个会话(Session)关联。会话可以包含多于一个的订阅。会话的每个订阅都有一个不同的主题过滤器。

主题名 Topic Name

附加在应用消息上的一个标签,服务端已知且与订阅匹配。服务端发送应用消息的一个副本给每一个匹配的客户端订阅。

主题过滤器 Topic Filter

订阅中包含的一个表达式,用于表示相关的一个或多个主题。主题过滤器可以使用通配符。

会话 Session

客户端和服务端之间的状态交互。一些会话持续时长与网络连接一样,另一些可以在客户端和服务端的多个连续网络连接间扩展。

控制报文 MQTT Control Packet

通过网络连接发送的信息数据包。MQTT规范定义了十四种不同类型的控制报文,其中一个(PUBLISH报文)用于传输应用消息。

1.2数据表示 Data representations

1.2.1 二进制位 Bits

字节中的位从0到7。第7位是最高有效位,第0位是最低有效位。

1.2.2 整数数值 Integer data values

整数数值是16位,使用大端序(big-endian,高位字节在低位字节前面)。这意味着一个16位的字在网络上表示为最高有效字节(MSB),后面跟着最低有效字节(LSB)。

第二章 MQTT 控制报文格式

2.1 MQTT控制报文的结构 Structure of an MQTT Control Packet

MQTT协议通过交换预定义的MQTT控制报文来通信。这一节描述这些报文的格式。

MQTT控制报文由三部分组成,按照 图例 2.1 –MQTT控制报文的结构 描述的顺序:

图例 2.1 –MQTT控制报文的结构
Fixed header 固定报头,所有控制报文都包含
Variable header 可变报头,部分控制报文包含
Payload 有效载荷,部分控制报文包含

2.2 固定报头 Fixed header

每个MQTT控制报文都包含一个固定报头。图例 2.2 -固定报头的格式 描述了固定报头的格式。

图例 2.2 -固定报头的格式
Bit 7 6 5 4 3 2 1 0
byte 1 MQTT控制报文的类型 用于指定控制报文类型的标志位
byte 2… 剩余长度

2.2.1 MQTT控制报文的类型 MQTT Control Packet type

位置:第1个字节,二进制位7-4。

表示为4位无符号值,这些值的定义见 表格 2.1 -控制报文的类型

表格 2.1 -控制报文的类型
名字 报文流动方向 描述
Reserved 0 禁止 保留
CONNECT 1 客户端到服务端 客户端请求连接服务端
CONNACK 2 服务端到客户端 连接报文确认
PUBLISH 3 两个方向都允许 发布消息
PUBACK 4 两个方向都允许 QoS 1消息发布收到确认
PUBREC 5 两个方向都允许 发布收到(保证交付第一步)
PUBREL 6 两个方向都允许 发布释放(保证交付第二步)
PUBCOMP 7 两个方向都允许 QoS 2消息发布完成(保证交互第三步)
SUBSCRIBE 8 客户端到服务端 客户端订阅请求
SUBACK 9 服务端到客户端 订阅请求报文确认
UNSUBSCRIBE 10 客户端到服务端 客户端取消订阅请求
UNSUBACK 11 服务端到客户端 取消订阅报文确认
PINGREQ 12 客户端到服务端 心跳请求
PINGRESP 13 服务端到客户端 心跳响应
DISCONNECT 14 客户端到服务端 客户端断开连接
Reserved 15 禁止 保留

2.2.2 标志 Flags

固定报头第1个字节的剩余的4位 [3-0]包含每个MQTT控制报文类型特定的标志,见 表格 2.2 -标志位。表格 2.2中任何标记为“保留”的标志位,都是保留给以后使用的,必须设置为表格中列出的值 [MQTT-2.2.2-1]。如果收到非法的标志,接收者必须关闭网络连接。有关错误处理的详细信息见 4.8节 [MQTT-2.2.2-2]。

表格 2.2 - 标志位 Flag Bits
控制报文 固定报头标志 Bit 3 Bit 2 Bit 1 Bit 0
CONNECT Reserved 0 0 0 0
CONNACK Reserved 0 0 0 0
PUBLISH Used in MQTT 3.1.1 DUP1 QoS2 QoS2 RETAIN3
PUBACK Reserved 0 0 0 0
PUBREC Reserved 0 0 0 0
PUBREL Reserved 0 0 1 0
PUBCOMP Reserved 0 0 0 0
SUBSCRIBE Reserved 0 0 1 0
SUBACK Reserved 0 0 0 0
UNSUBSCRIBE Reserved 0 0 1 0
UNSUBACK Reserved 0 0 0 0
PINGREQ Reserved 0 0 0 0
PINGRESP Reserved 0 0 0 0
DISCONNECT Reserved 0 0 0 0
  • DUP1 =控制报文的重复分发标志
  • QoS2 = PUBLISH报文的服务质量等级
  • RETAIN3 = PUBLISH报文的保留标志

PUBLISH控制报文中的DUP, QoS和RETAIN标志的描述见 3.3.1节。

2.2.3 剩余长度 Remaining Length

位置:从第2个字节开始。

剩余长度(Remaining Length)表示当前报文剩余部分的字节数,包括可变报头和负载的数据。剩余长度不包括用于编码剩余长度字段本身的字节数。

剩余长度字段使用一个变长度编码方案,对小于128的值它使用单字节编码。更大的值按下面的方式处理。低7位有效位用于编码数据,最高有效位用于指示是否有更多的字节。因此每个字节可以编码128个数值和一个延续位(continuation bit)。剩余长度字段最大4个字节。

非规范评注

例如,十进制数64会被编码为一个字节,数值是64,十六进制表示为0x40,。十进制数字321(=65+2*128)被编码为两个字节,最低有效位在前。第一个字节是 65+128=193。注意最高位为1表示后面至少还有一个字节。第二个字节是1。

非规范评注

这允许应用发送最大256MB(268,435,455)大小的控制报文。这个数值在报文中的表示是:0xFF,0xFF,0xFF,0x7F。

表格 2.4剩余长度字段的大小展示了剩余长度字段所表示的值随字节增长。

表格 2.4剩余长度字段的大小 Size of Remaining Length field
字节数 最小值 最大值
1 0 (0x00) 127 (0x7F)
2 128 (0x80, 0x01) 16 383 (0xFF, 0x7F)
3 16 384 (0x80, 0x80, 0x01) 2 097 151 (0xFF, 0xFF, 0x7F)
4 2 097 152 (0x80, 0x80, 0x80, 0x01) 268 435 455 (0xFF, 0xFF, 0xFF, 0x7F)

分别表示(每个字节的低7位用于编码数据,最高位是标志位):

  • 1个字节时,从0(0x00)到127(0x7f)
  • 2个字节时,从128(0x80,0x01)到16383(0Xff,0x7f)
  • 3个字节时,从16384(0x80,0x80,0x01)到2097151(0xFF,0xFF,0x7F)
  • 4个字节时,从2097152(0x80,0x80,0x80,0x01)到268435455(0xFF,0xFF,0xFF,0x7F)

非规范评注

非负整数X使用变长编码方案的算法如下:

  1. do
  2. encodedByte = X MOD 128
  3. X = X DIV 128
  4. // if there are more data to encode, set the top bit of this byte
  5. if ( X > 0 )
  6. encodedByte = encodedByte OR 128
  7. endif
  8. 'output' encodedByte
  9. while ( X > 0 )

MOD是模运算,DIV是整数除法,OR是位操作或(C语言中分别是%,/,|)

非规范评注

剩余长度字段的解码算法如下:

  1. multiplier = 1
  2. value = 0
  3. do
  4. encodedByte = 'next byte from stream'
  5. value += (encodedByte AND 127) * multiplier
  6. if (multiplier > 128*128*128)
  7. throw Error(Malformed Remaining Length)
  8. multiplier *= 128
  9. while ((encodedByte AND 128) != 0)

AND是位操作与(C语言中的&)

这个算法终止时,value包含的就是剩余长度的值。

2.3 可变报头 Variable header

某些MQTT控制报文包含一个可变报头部分。它在固定报头和负载之间。可变报头的内容根据报文类型的不同而不同。可变报头的报文标识符(Packet Identifier)字段存在于在多个类型的报文里。

2.3.1 报文标识符 Packet Identifier

图例 2.3 -报文标识符字节 Packet Identifier bytes
Bit 7 - 0
byte 1 报文标识符 MSB
byte 2 报文标识符 LSB

很多控制报文的可变报头部分包含一个两字节的报文标识符字段。这些报文是PUBLISH(QoS > 0时), PUBACK,PUBREC,PUBREL,PUBCOMP,SUBSCRIBE, SUBACK,UNSUBSCRIBE,UNSUBACK。

SUBSCRIBE,UNSUBSCRIBE和PUBLISH(QoS大于0)控制报文必须包含一个非零的16位报文标识符(Packet Identifier)[MQTT-2.3.1-1]。客户端每次发送一个新的这些类型的报文时都必须分配一个当前未使用的报文标识符 [MQTT-2.3.1-2]。如果一个客户端要重发这个特殊的控制报文,在随后重发那个报文时,它必须使用相同的标识符。当客户端处理完这个报文对应的确认后,这个报文标识符就释放可重用。QoS 1的PUBLISH对应的是PUBACK,QoS 2的PUBLISH对应的是PUBCOMP,与SUBSCRIBE或UNSUBSCRIBE对应的分别是SUBACK或UNSUBACK [MQTT-2.3.1-3]。发送一个QoS 0的PUBLISH报文时,相同的条件也适用于服务端 [MQTT-2.3.1-4]。

QoS等于0的PUBLISH报文不能包含报文标识符 [MQTT-2.3.1-5]。

PUBACK, PUBREC, PUBREL报文必须包含与最初发送的PUBLISH报文相同的报文标识符 [MQTT-2.3.1-6]。类似地,SUBACK和UNSUBACK必须包含在对应的SUBSCRIBE和UNSUBSCRIBE报文中使用的报文标识符 [MQTT-2.3.1-7]。

需要报文标识符的控制报文在 表格 2.5 -包含报文标识符的控制报文 中列出。

表格 2.5 -包含报文标识符的控制报文 Control Packets that contain a Packet Identifier
控制报文 报文标识符字段
CONNECT 不需要
CONNACK 不需要
PUBLISH 需要(如果QoS > 0)
PUBACK 需要
PUBREC 需要
PUBREL 需要
PUBCOMP 需要
SUBSCRIBE 需要
SUBACK 需要
UNSUBSCRIBE 需要
UNSUBACK 需要
PINGREQ 不需要
PINGRESP 不需要
DISCONNECT 不需要

客户端和服务端彼此独立地分配报文标识符。因此,客户端服务端组合使用相同的报文标识符可以实现并发的消息交换。

非规范评注

客户端发送标识符为0x1234的PUBLISH报文,它有可能会在收到那个报文的PUBACK之前,先收到服务端发送的另一个不同的但是报文标识符也为0x1234的PUBLISH报文。

Client Server
PUBLISH Packet Identifier=0x1234—-
—PUBLISH Packet Identifier=0x1234
PUBACK Packet Identifier=0x1234—-
—PUBACK Packet Identifier=0x1234

2.4 有效载荷 Payload

某些MQTT控制报文在报文的最后部分包含一个有效载荷,这将在第三章论述。对于PUBLISH来说有效载荷就是应用消息。表格 2.6 – 包含有效载荷的控制报文 列出了需要有效载荷的控制报文。

表格 2.6 – 包含有效载荷的控制报文 Control Packets that contain a Payload
控制报文 有效载荷
CONNECT 需要
CONNACK 不需要
PUBLISH 可选
PUBACK 不需要
PUBREC 不需要
PUBREL 不需要
PUBCOMP 不需要
SUBSCRIBE 需要
SUBACK 需要
UNSUBSCRIBE 需要
UNSUBACK 不需要
PINGREQ 不需要
PINGRESP 不需要
DISCONNECT 不需要

第三章目录 MQTT控制报文 MQTT Control Packets

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 -连接标志位

MQTT 协议 1-3 章 - 图1

服务端必须验证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 -可变报头非规范示例[

](0301-CONNECT.md)MQTT 协议 1-3 章 - 图2

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,那么它按照下面的方法验证连接请求。

  1. 网络连接建立后,如果服务端在合理的时间内没有收到CONNECT报文,服务端应该关闭这个连接。
  2. 服务端必须按照3.1节的要求验证CONNECT报文,如果报文不符合规范,服务端不发送CONNACK报文直接关闭网络连接 [MQTT-3.1.4-1]。
  3. 服务端可以检查CONNECT报文的内容是不是满足任何进一步的限制,可以执行身份验证和授权检查。如果任何一项检查没通过,按照3.2节的描述,它应该发送一个适当的、返回码非零的CONNACK响应,并且必须关闭这个网络连接。

如果验证成功,服务端会执行下列步骤。

  1. 如果ClientId表明客户端已经连接到这个服务端,那么服务端必须断开原有的客户端连接 [MQTT-3.1.4-2]。
  2. 服务端必须按照 3.1.2.4节的描述执行清理会话的过程 [MQTT-3.1.4-3]。
  3. 服务端必须发送返回码为零的CONNACK报文作为CONNECT报文的确认响应 [MQTT-3.1.4-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报文给服务端的。用于:

  1. 在没有任何其它控制报文从客户端发给服务的时,告知服务端客户端还活着。
  2. 请求服务端发送 响应确认它还活着。
  3. 使用网络以确认网络连接没有断开。

保持连接(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]。
  • 应该关闭网络连接,如果客户端 还没有这么做。