赞
踩
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分。该协议支持所有平台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和制动器的通信协议。
MQTT协议是为大量计算能力有限,且工作在低带宽、不可靠的网络的远程传感器和控制设备通讯而设计的协议,它具有以下主要的几项特性:
(1)小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量,传输的内容最大为256MB;
(2)使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合;
(3)使用TCP/IP 提供网络连接;
MQTT系统由与服务器通信的客户端组成,通常称服务器为“代理Broker”。客户可以是信息发布者Publish或订阅者Subscribe。每个客户端都可以连接到代理。
信息按主题层次结构组织。当发布者具有要分发的新数据时,它会将包含数据的控制消息发送到连接的代理。然后,代理将信息分发给已订阅该主题的任何客户端。发布者不需要有关于订阅者数量或位置的任何数据,而订阅者又不必配置有关发布者的任何数据。
MQTT传输的消息分为:主题(Topic)和负载(payload)两部分:
(1)Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload);
(2)Payload,可以理解为消息的内容,是指订阅者具体要使用的内容。
订阅包含主题筛选器(Topic Filter)和最大服务质量(QoS)。订阅会与一个会话(Session)关联。一个会话可以包含多个订阅。每一个会话中的每个订阅都有一个不同的主题筛选器。
每个客户端与服务器建立连接后就是一个会话,客户端和服务器之间有状态交互。会话存在于一个网络之间,也可能在客户端和服务器之间跨越多个连续的网络连接。
连接到一个应用程序消息的标签,该标签与服务器的订阅相匹配。服务器会将消息发送给订阅所匹配标签的每个客户端。
系统主题:通过定义$SYS开头的主题可以查看一些系统信息,如客户端连接数量等,
详细介绍:SYS Topics · mqtt/mqtt.org Wiki · GitHub
一个对主题名通配符筛选器,在订阅表达式中使用,表示订阅所匹配到的多个主题。
多级匹配符 #
单级匹配符 +
例如:
(1)对A/#的订阅是对主题A和A下面所有主题的订阅
(2)对A/+的订阅是对A下一级主题的订阅,但不包括A本身
(3)订阅A/+/#是订阅A下面的所有主题(可能包含多级),但不包括A本身
更多主题讨论,请移步
github wiki topic_format · mqtt/mqtt.org Wiki · GitHub
消息订阅者所具体接收的内容。
MQTT中,无论是发布还是订阅都不会有任何触发事件。
1个Topic只有唯一的retain消息,Broker会保存每个Topic的最后一条retain消息。
发布消息时把retain设置为true,即为保留信息。每个Client订阅Topic后会立即读取到retain消息。如果需要删除retain消息,可以发布一个空的retain消息,因为每个新的retain消息都会覆盖最后一个retain消息。
MQTT本身就是为信号不稳定的网络设计的,所以难免一些客户端会无故的和Broker断开连接。 当客户端连接到Broker时,可以指定LWT,Broker会定期检测客户端是否有异常。当客户端异常掉线时,Broker就往连接时指定的topic里推送当时指定的LWT消息。
遗嘱消息的相关设置是在建立连接时的 CONNECT 数据包中设定的:
遗嘱消息发布的条件,包括但不限于:
有三种消息发布服务质量qos(Quality of Service):
“至多一次”,消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次记录无所谓,因为不久后还会有第二次发送。
“至少一次”, 如果一定时间内,发布端或服务器没有收到PUBACK消息,则会进行重发。这种方式虽然确保了消息到达,但消息重复可能会发生。
“只有一次”,确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果, 确保用户收到且只会收到一次。
MQTT控制报文由三部分组成:固定报头 可变报头 有效载荷
Fixed header | 固定报头,所有控制报文都包含 |
---|---|
Variable header | 可变报头,部分控制报文包含 |
Payload | 有效载荷,部分控制报文包含 |
固定报头的格式:(MSB,高位在前)
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文的类型(1~14) | 用于指定控制报文类型的标志位 | ||||||
byte 2~5 | 剩余长度(1~4Byte) |
位置:固定报头中,第 1 个字节的 Bit 7~4。
控制报文的定义如下:(0/15是保留值,无用。有效类型为 1~14)
名字 | 值 | 报文流动方向 | 描述 |
---|---|---|---|
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 | 禁止 | 保留 |
位置:固定报头中,第 1 个字节的 Bit 3~0。
控制报文 | 固定报头标志 | 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 |
如果该标志被置0,表示这是客户端或服务端第一次请求发送这个PUBLISH报文。如果该标志被置1,表示这可能是一个早前报文请求的重发。
协议头示例
剩余长度字段 的帧格式:
字节数 | 最小值 | 最大值 |
---|---|---|
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) |
提示:
之所以1个字节不能表示 2^8 - 1 = 255长度,是因为:每个字节的最高位 Bit7,并不表示数据,是进位标志位。
剩余长度的计算:
示例1:
假设本帧剩余字节为 200,计算剩余长度字段。
使用电脑计算器,将 200 转换为二进制 1100 1000(MSB高位在前)
从右侧低位每7Bit进行一次拆分,依次拆分出:
第1个字节为 100 1000,有进位,高位加上进位1为 1100 1000 = 0xC8 (16进制)。
第2个字节为 1,无进位,为 1 = 0x01 (16进制)。
那么对应的 字节长度的帧格式如下表:
示例2:
假设本帧剩余字节为 1000,计算剩余长度字段。
使用电脑计算器,将 1000 转换为二进制 11 1110 1000(MSB高位在前)
从右侧低位每7Bit进行一次拆分,依次拆分出:
第1个字节为 110 1000,有进位,高位加上进位1为 1110 1000 = 0xE8 (16进制)。
第2个字节为 11 1,无进位,为 11 1 = 0x07 (16进制)。
那么对应的 字节长度的帧格式如下表:
示例3:
假设本帧剩余字节为 100,000,000,计算剩余长度字段。
使用电脑计算器,将 100,000,000 转换为二进制 101 1111 0101 1110 0001 0000 0000(MSB高位在前)
从右侧低位每7Bit进行一次拆分,依次拆分出:
第1个字节为 000 0000,有进位,高位加上进位1为 1000 0000 = 0x80 (16进制)。
第2个字节为 10 0001 0,有进位,高位加上进位1为 1100 0010 = 0xC2 (16进制)。
第3个字节为 1 0101 11,有进位,高位加上进位1为 1101 0111 = 0xD7 (16进制)。
第4个字节为 101 111 = 00,无进位,为 10 1111 = 0x2F (16进制)。
那么对应的 字节长度的帧格式如下表:
某些 MQTT 控制报文包含一个可变报头部分。它在固定报头和负载之间。
可变报头的内容根据报文类型的不同而不同。
可变报头的报文标识符(Packet Identifier)字段存在于多个类型的报文里。
Bit | 7 - 0 |
---|---|
byte 1 | 报文标识符 MSB |
byte 2 | 报文标识符 LSB |
很多控制报文的可变报头部分包含一个两字节的报文标识符字段。这些报文是PUBLISH(QoS > 0时), PUBACK,PUBREC,PUBREL,PUBCOMP,SUBSCRIBE, SUBACK,UNSUBSCIBE,UNSUBACK。
SUBSCRIBE,UNSUBSCRIBE和PUBLISH(QoS大于0)控制报文必须包含一个非零的16位报文标识符(Packet Identifier)。客户端每次发送一个新的这些类型的报文时都必须分配一个当前未使用的报文标识符。如果一个客户端要重发这个特殊的控制报文,在随后重发那个报文时,它必须使用相同的标识符。当客户端处理完这个报文对应的确认后,这个报文标识符就释放可重用。QoS 1的PUBLISH对应的是PUBACK,QoS 2的PUBLISH对应的是PUBCOMP,与SUBSCRIBE或UNSUBSCRIBE对应的分别是SUBACK或UNSUBACK 。发送一个QoS 0的PUBLISH报文时,相同的条件也适用于服务端。
QoS等于0的PUBLISH报文不能包含报文标识符。
PUBACK, PUBREC, PUBREL报文必须包含与最初发送的PUBLISH报文相同的报文标识符 。类似地,SUBACK和UNSUBACK必须包含在对应的SUBSCRIBE和UNSUBSCRIBE报文中使用的报文标识符 。
控制报文 | 报文标识符字段 |
---|---|
CONNECT | 不需要 |
CONNACK | 不需要 |
PUBLISH | 需要(如果QoS > 0) |
PUBACK | 需要 |
PUBREC | 需要 |
PUBREL | 需要 |
PUBCOMP | 需要 |
SUBSCRIBE | 需要 |
SUBACK | 需要 |
UNSUBSCRIBE | 需要 |
UNSUBACK | 需要 |
PINGREQ | 不需要 |
PINGRESP | 不需要 |
DISCONNECT | 不需要 |
客户端和服务端彼此独立地分配报文标识符。因此,客户端服务端组合使用相同的报文标识符可以实现并发的消息交换。
某些MQTT控制报文在报文的最后部分包含一个有效载荷。对于PUBLISH来说有效载荷就是应用消息。
控制报文 | 有效载荷 |
---|---|
CONNECT | 需要 |
CONNACK | 不需要 |
PUBLISH | 可选 |
PUBACK | 不需要 |
PUBREC | 不需要 |
PUBREL | 不需要 |
PUBCOMP | 不需要 |
SUBSCRIBE | 需要 |
SUBACK | 需要 |
UNSUBSCRIBE | 需要 |
UNSUBACK | 不需要 |
PINGREQ | 不需要 |
PINGRESP | 不需要 |
DISCONNECT | 不需要 |
客户端发送PINGREQ报文给服务端的。用于:
PINGREQ 与 CONNECT 可变报头中的 保持连接 Keep Alive 字段有非常大的关联。
保持连接(Keep Alive)是一个以秒为单位的时间间隔,表示为一个16位的字,它是指在客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。客户端负责保证控制报文发送的时间间隔不超过保持连接的值。如果没有任何其它的控制报文可以发送,客户端必须发送一个PINGREQ报文。
不管保持连接的值是多少,客户端任何时候都可以发送PINGREQ报文,并且使用PINGRESP报文判断网络和服务端的活动状态。
如果保持连接的值非零,并且服务端在一点五倍的保持连接时间内没有收到客户端的PINGREQ控制报文,它必须断开客户端的网络连接,认为网络连接已断开。
客户端发送了PINGREQ报文之后,如果在合理的时间内仍没有收到PINGRESP报文,它应该关闭到服务端的网络连接。
保持连接的实际值是由应用指定的,一般是几分钟。允许的最大值是18小时12分15秒。(Keep Alive 取值0~65535,如果 keep alive 的间隔为 0 则 keep alive 机制是无效的)
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 |
PINGREQ 只有固定报头字段,且报文长度只有 2 字节。
PINGREQ 没有 可变报头 字段。
PINGREQ 没有 有效载荷 字段。
服务端发送PINGRESP报文响应客户端的PINGREQ报文。表示服务端还活着。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (13) | 保留位 | ||||||
1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | |
byte 2 | 剩余长度 (0) | |||||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
PINGRESP 只有固定报头字段,且报文长度只有 2 字节。
PINGRESP 没有 可变报头 字段。
PINGRESP 没有 有效载荷 字段。
2.1.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~5 | 剩余长度 |
CONNECT的可变报头包含四个字段:
可变报头中,协议名字段的6个字节是固定的:
客户端用8位的无符号值表示协议的修订版本。对于3.1.1版MQTT协议,协议级别字段的值是4(0x04)。如果发现不支持的协议级别,服务端必须给发送一个返回码为0x01(不支持的协议级别)的CONNACK报文响应CONNECT报文,然后断开客户端的连接。
连接标志字节包含一些用于指定MQTT连接行为的参数。它还指出有效载荷中的字段是否存在。
Bit 1:清理会话标志位。这个标志位用于控制会话状态的生存时间。如果清理会话(CleanSession)标志被设置为0,服务端必须基于当前会话(使用客户端标识符识别)的状态恢复与客户端的通信;如果清理会话(CleanSession)标志被设置为1,客户端和服务端必须丢弃之前的任何会话并开始一个新的会话。会话仅持续和网络连接同样长的时间。与这个会话关联的状态数据不能被任何之后的会话重用。
Bit 2:遗嘱标志位。遗嘱标志(Will Flag)被设置为1,表示如果连接请求被接受了,遗嘱(Will Message)消息必须被存储在服务端并且与这个网络连接关联。之后网络连接关闭时,服务端必须发布这个遗嘱消息,除非服务端收到DISCONNECT报文时删除了这个遗嘱消息。如果遗嘱标志被设置为1,连接标志中的Will QoS和Will Retain字段会被服务端用到,同时有效载荷中必须包含Will Topic和Will Message字段。
Bit 3~4:遗嘱 Qos。这两位用于指定发布遗嘱消息时使用的服务质量等级。如果遗嘱标志被设置为0,遗嘱QoS也必须设置为0(0x00)。如果遗嘱标志被设置为1,遗嘱QoS的值可以等于0(00b),1(01b),2(10b)。它的值不能等于3(11b)。
Bit 5:遗嘱保留标志位。如果遗嘱消息被发布时需要保留,需要指定这一位的值。如果遗嘱标志被设置为0,遗嘱保留(Will Retain)标志也必须设置为0。如果遗嘱标志被设置为1:
如果遗嘱保留被设置为0,服务端必须将遗嘱消息当作非保留消息发布。
如果遗嘱保留被设置为1,服务端必须将遗嘱消息当作保留消息发布。
Bit 6:密码标志位。如果密码(Password)标志被设置为0,有效载荷中不能包含密码字段。如果密码(Password)标志被设置为1,有效载荷中必须包含密码字段。如果用户名标志被设置为0,密码标志也必须设置为0。
Bit 7:用户名标志位。如果用户名(User Name)标志被设置为0,有效载荷中不能包含用户名字段。如果用户名(User Name)标志被设置为1,有效载荷中必须包含用户名字段。
连接标志位最常见的组合是:1100 0010,即为 0xC2。
带用户名密码:
不带用户名密码:
注意:
关于遗嘱相关概念进行说明一下。
当客户端启用遗嘱功能时,在CONNECT协议包的可变报头的连接标志中,必须打开遗嘱标志(byte8的第2位),并在有效载荷中传递遗嘱主题和遗嘱消息。
当服务端判断客户端异常断开时,服务端会向遗嘱主题发送遗嘱消息。
当启用遗嘱时,在标志位中可以设置遗嘱消息的Qos,即可变报头的byte8的第3、4位。
当启用遗嘱保留标志(byte8的第5位)时,表示遗嘱消息在发布时需要保留,即启动遗嘱保留时,服务端要持久化该遗嘱消息(仅仅保留最新一条),其他人后订阅该遗嘱主题时,能收到最后一次的遗嘱消息。
总结一下连接标志字节的常见的情形
保持连接(Keep Alive)是一个以秒为单位的时间长度,表示为一个16位的字,它是指在客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。客户端负责保证控制报文发送的时间间隔不超过保持连接的值。如果没有任何其它的控制报文可以发送,客户端必须发送一个PINGREQ报文。(详见1.1心跳请求)
常见的CONNECT可变报头
服务端发送CONNACK报文响应从客户端收到的CONNECT报文。服务端发送给客户端的第一个报文必须是CONNACK。
如果客户端在合理的时间内没有收到服务端的CONNACK报文,客户端应该关闭网络连接。
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。
当前会话标志位 SP(Session Present):如果服务端收到清理会话(CleanSession)标志为1的连接,除了将CONNACK报文中的返回码设置为0之外,还必须将CONNACK报文中的当前会话设置(Session Present)标志为0。
如果服务端收到一个CleanSession为0的连接,当前会话标志的值取决于服务端是否已经保存了ClientId对应客户端的会话状态。如果服务端已经保存了会话状态,它必须将CONNACK报文中的当前会话标志设置为1。如果服务端没有已保存的会话状态,它必须将CONNACK报文中的当前会话设置为0。还需要将CONNACK报文中的返回码设置为0。
如果服务端发送了一个包含非零返回码的CONNACK报文,它必须将当前会话标志设置为0。
连接返回码:如果服务端收到一个合法的CONNECT报文,但出于某些原因无法处理它,服务端应该尝试发送一个包含非零返回码的CONNACK报文。
如果服务端发送了一个包含非零返回码的CONNACK报文,那么它必须关闭网络连接。
值 | 返回码响应 | 描述 |
---|---|---|
0 | 0x00连接已接受 | 连接已被服务端接受 |
1 | 0x01连接已拒绝,不支持的协议版本 | 服务端不支持客户端请求的MQTT协议级别 |
2 | 0x02连接已拒绝,不合格的客户端标识符 | 客户端标识符是正确的UTF-8编码,但服务端不允许使用 |
3 | 0x03连接已拒绝,服务端不可用 | 网络连接已建立,但MQTT服务不可用 |
4 | 0x04连接已拒绝,无效的用户名或密码 | 用户名或密码的数据格式无效 |
5 | 0x05连接已拒绝,未授权 | 客户端未被授权连接到此服务器 |
6-255 | 保留 |
如果认为上表中的所有连接返回码都不太合适,那么服务端必须关闭网络连接,不需要发送CONNACK报文。
CONNACK 没有 有效载荷 字段。
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 |
CONNACK 没有 可变报头 字段。
CONNACK 没有 有效载荷 字段。
客户端发送DISCONNECT报文之后,必须关闭网络连接,并且不能通过这个网络连接再发送任何控制报文。
服务端在收到DISCONNECT报文时,必须丢弃任何与当前连接关联的未发布的遗嘱消息,并且关闭网络连接(如果客户端 还没关闭连接的话)。
客户端向服务端发送SUBSCRIBE报文用于创建一个或多个订阅。每个订阅注册客户端关心的一个或多个主题。为了将应用消息转发给那些订阅匹配的主题,服务端发送PUBLISH报文给客户端。SUBSCRIBE报文也(为每个订阅)指定了最大的QoS等级,服务端根据这个发送应用消息给客户端。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (8) | 保留位 | ||||||
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
byte 2~4 | 剩余长度 |
SUBSCRIBE控制报固定报头的第3,2,1,0位是保留位,必须分别设置为0,0,1,0。服务端必须将其它的任何值都当做是不合法的并关闭网络连接。
剩余长度字段:等于SUBSCRIBE可变报头的长度(2字节)加上有效载荷的长度。
可变报头包含客户端报文标识符。
报文标识符相当于自定义的Topic的ID,用ID号去代替具体的Topic,而不是字段,使得区分发来的Topic的同时又可以节省流量,可自定义,建议自己预先拟定一个服务ID表。
订阅返回,返回Topic订阅成功信息,返回的不是具体Topic,返回的就是报文标识符。
SUBSCRIBE报文的有效载荷包含了一个主题过滤器列表,它们表示客户端想要订阅的主题。SUBSCRIBE报文有效载荷中的主题过滤器列表必须是UTF-8字符串。服务端应该支持包含通配符的主题过滤器。如果服务端选择不支持包含通配符的主题过滤器,必须拒绝任何包含通配符过滤器的订阅请求。每一个过滤器后面跟着一个字节,这个字节被叫做 服务质量要求(Requested QoS)。它给出了服务端向客户端发送应用消息所允许的最大QoS等级。
SUBSCRIBE报文的有效载荷必须包含至少一对主题过滤器 和 QoS等级字段组合。没有有效载荷的SUBSCRIBE报文是违反协议的。
当前版本的协议没有用到服务质量要求(Requested QoS)字节的高六位。如果有效载荷中的任何位是非零值,或者QoS不等于0,1或2,服务端必须认为SUBSCRIBE报文是不合法的并关闭网络连接。
响应:服务端收到客户端发送的一个SUBSCRIBE报文时,必须使用SUBACK报文响应。
服务端发送给客户端的SUBACK报文对每一对主题过滤器 和QoS等级都必须包含一个返回码。这个返回码必须表示那个订阅被授予的最大QoS等级,或者表示这个订阅失败。服务端可以授予比订阅者要求的低一些的QoS等级。为响应订阅而发出的消息的有效载荷的QoS必须是原始发布消息的QoS和服务端授予的QoS两者中的最小值。如果原始消息的QoS是1而被授予的最大QoS是0,允许服务端重复发送一个消息的副本给订阅者。
服务端发送SUBACK报文给客户端,用于确认它已收到并且正在处理SUBSCRIBE报文。
SUBACK报文包含一个返回码清单,它们指定了SUBSCRIBE请求的每个订阅被授予的最大QoS等级。
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 |
可变报头包含等待确认的SUBSCRIBE报文的报文标识符。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
有效载荷包含一个返回码清单。每个返回码对应等待确认的SUBSCRIBE报文中的一个主题过滤器。返回码的顺序必须和SUBSCRIBE报文中主题过滤器的顺序相同。
下面表格描述了有效载荷中单字节编码的返回码字段。
允许的返回码值:
0x00 - 最大 Qos0
0x01 - 成功 - 最大 Qos1
0x02 - 成功 - 最大 Qos2
0x80 - Failure 失败
0x00, 0x01, 0x02, 0x80之外的SUBACK返回码是保留的,不能使用。
客户端发送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。服务端必须认为任何其它的值都是不合法的并关闭网络连接。
可变报头包含一个报文标识符。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
UNSUBSCRIBE报文的有效载荷包含客户端想要取消订阅的主题过滤器列表。UNSUBSCRIBE报文中的主题过滤器必须是连续打包的UTF-8编码字符串。
UNSUBSCRIBE报文的有效载荷必须至少包含一个消息过滤器。没有有效载荷的UNSUBSCRIBE报文是违反协议的。
UNSUBSCRIBE的有效载荷比SUBSCRIBE少了服务质量要求(Requested QoS)。
响应:UNSUBSCRIBE报文提供的主题过滤器(无论是否包含通配符)必须与服务端持有的这个客户端的当前主题过滤器集合逐个字符比较。如果有任何过滤器完全匹配,那么它(服务端)自己的订阅将被删除,否则不会有进一步的处理
如果服务端删除了一个订阅:
它必须停止分发任何新消息给这个客户端。
它必须完成分发任何已经开始往客户端发送的QoS 1和QoS 2的消息。
它可以继续发送任何现存的准备分发给客户端的缓存消息。
服务端必须发送UNSUBACK报文响应客户端的UNSUBSCRIBE请求。UNSUBACK报文必须包含和UNSUBSCRIBE报文相同的报文标识符 。即使没有删除任何主题订阅,服务端也必须发送一个SUBACK响应。
服务端发送UNSUBACK报文给客户端用于确认收到UNSUBSCRIBE报文。
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。
可变报头包含等待确认的UNSUBSCRIBE报文的报文标识符。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
UNSUBACK报文没有有效载荷。
CON
10 23 00 04 4d 51 54 54 04 c0 00 64 00 05 6e 65 74 74 79 00 05 61 64 6d 69 6e 00 09 67 6f 6f 64 77 65 31 32 33
SUB
82 24 00 01 00 1f 2f 67 6f 6f 64 77 65 2f 63 63 6d 2f 63 6c 69 65 6e 74 2f 69 6e 76 65 6e 74 65 72 69 6e 66 6f 02
UNSUB
a2 23 00 01 00 1f 2f 67 6f 6f 64 77 65 2f 63 63 6d 2f 63 6c 69 65 6e 74 2f 69 6e 76 65 6e 74 65 72 69 6e 66 6f
PUBLISH控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (3) | DUP | QoS-H | QoS-L | RETAIN | |||
0 | 0 | 1 | 1 | X | X | X | X | |
byte 2~5 | 剩余长度 |
位置:第1个字节,第3位。
如果DUP标志被设置为0,表示这是客户端或服务端第一次请求发送这个PUBLISH报文。如果DUP标志被设置为1,表示这可能是一个早前报文请求的重发。
客户端或服务端请求重发一个PUBLISH报文时,必须将DUP标志设置为1。对于QoS 0的消息,DUP标志必须设置为0。
位置:第1个字节,第2-1位。这个字段表示应用消息分发的服务质量等级保证。
QoS值 | Bit 2 | Bit 1 | 描述 |
---|---|---|---|
0 | 0 | 0 | 最多分发一次 |
1 | 0 | 1 | 至少分发一次 |
2 | 1 | 0 | 只分发一次 |
- | 1 | 1 | 保留位 |
PUBLISH报文不能将QoS所有的位设置为1。如果服务端或客户端收到QoS所有位都为1的PUBLISH报文,它必须关闭网络连接。
位置:第1个字节,第0位。
如果客户端发给服务端的PUBLISH报文的保留(RETAIN)标志被设置为1,服务端必须存储这个应用消息和它的服务质量等级(QoS),以便它可以被分发给未来的主题名匹配的订阅者。一个新的订阅建立时,对每个匹配的主题名,如果存在最近保留的消息,它必须被发送给这个订阅者。如果服务端收到一条保留(RETAIN)标志为1的QoS 0消息,它必须丢弃之前为那个主题保留的任何消息。它应该将这个新的QoS 0消息当作那个主题的新保留消息,但是任何时候都可以选择丢弃它。如果这种情况发生了,那个主题将没有保留消息。
服务端发送PUBLISH报文给客户端时,如果消息是作为客户端一个新订阅的结果发送,它必须将报文的保留标志设为1。当一个PUBLISH报文发送给客户端是因为匹配一个已建立的订阅时,服务端必须将保留标志设为0,不管它收到的这个消息中保留标志的值是多少。
保留标志为1且有效载荷为零字节的PUBLISH报文会被服务端当作正常消息处理,它会被发送给订阅主题匹配的客户端。此外,同一个主题下任何现存的保留消息必须被移除,因此这个主题之后的任何订阅者都不会收到一个保留消息。当作正常 意思是现存的客户端收到的消息中保留标志未被设置。服务端不能存储零字节的保留消息。
如果客户端发给服务端的PUBLISH报文的保留标志位0,服务端不能存储这个消息也不能移除或替换任何现存的保留消息。
剩余长度字段等于可变报头的长度加上有效载荷的长度。图中为261(000100000101B),按照之前剩余长度字段的算法第一个字节为0x85(10000101B),第二个字节为0x02(10B)。
可变报头按顺序包含主题名和报文标识符。
Byte1和Byte2是主题名长度。
主题名(Topic Name)用于识别有效载荷数据应该被发布到哪一个信息通道。
主题名必须是PUBLISH报文可变报头的第一个字段。它必须是 1.5.3节定义的UTF-8编码的字符串。
PUBLISH报文中的主题名不能包含通配符,字母的大写与小写对应不同Topic。
服务端发送给订阅客户端的PUBLISH报文的主题名必须匹配该订阅的主题过滤器
只有当QoS等级是1或2时,报文标识符(Packet Identifier)字段才能出现在PUBLISH报文中。
有效载荷包含将被发布的应用消息。数据的内容和格式是应用特定的。有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度。包含零长度有效载荷的PUBLISH报文是合法的。
响应:PUBLISH报文的接收者必须按照根据PUBLISH报文中的QoS等级发送响应。
服务质量等级 | 预期响应 |
---|---|
QoS 0 | 无响应 |
QoS 1 | PUBACK报文 |
QoS 2 | PUBREC报文 |
动作:
PUBACK报文是对QoS 1等级的PUBLISH报文的响应。
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。
包含等待确认的PUBLISH报文的报文标识符。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
PUBACK 报文没有有效载荷。
PUBACK 报文只有 固定报头 和 可变报头,共4个字节。
PUBREC报文是对QoS等级2的PUBLISH报文的响应。它是QoS 2等级协议交换的第二个报文。
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。
可变报头包含等待确认的PUBLISH报文的报文标识符。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
PUBREC报文没有有效载荷。
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报文是对PUBREC报文的响应。它是QoS 2等级协议交换的第三个报文。
PUBREL控制报文固定报头的第3,2,1,0位是保留位,必须被设置为0,0,1,0。服务端必须将其它的任何值都当做是不合法的并关闭网络连接。
剩余长度字段:表示可变报头的长度。对PUBREL报文这个值等于2。
可变报头包含与等待确认的PUBREC报文相同的报文标识符。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
PUBREL 报文没有有效载荷。
PUBCOMP报文是对PUBREL报文的响应。它是QoS 2等级协议交换的第四个也是最后一个报文。
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。
可变报头包含与等待确认的PUBREL报文相同的报文标识符。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
PUBCOMP 报文没有有效载荷。
参考:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。