MQTT协议抓包

MQTT通讯协议的特点

1. 概述

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分。该协议支持所有平台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和制动器的通信协议。

2. 主要特征

MQTT协议是为大量计算能力有限,且工作在低带宽、不可靠的网络的远程传感器和控制设备通讯而设计的协议,它具有以下主要的几项特性:

(1)小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量,传输的内容最大为256MB;

(2)使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合;

(3)使用TCP/IP 提供网络连接;

3. MQTT协议实现方式

MQTT系统由与服务器通信的客户端组成,通常称服务器为“代理Broker”。客户可以是信息发布者Publish或订阅者Subscribe。每个客户端都可以连接到代理。

信息按主题层次结构组织。当发布者具有要分发的新数据时,它会将包含数据的控制消息发送到连接的代理。然后,代理将信息分发给已订阅该主题的任何客户端。发布者不需要有关于订阅者数量或位置的任何数据,而订阅者又不必配置有关发布者的任何数据。

MQTT传输的消息分为:主题(Topic负载(payload两部分:
(1)Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload);
(2)Payload,可以理解为消息的内容,是指订阅者具体要使用的内容。

4. MQTT协议中的订阅、主题、会话

4.1订阅(Subscription)

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

4.2会话(Session)

每个客户端与服务器建立连接后就是一个会话,客户端和服务器之间有状态交互。会话存在于一个网络之间,也可能在客户端和服务器之间跨越多个连续的网络连接。

4.3主题名(Topic Name)

连接到一个应用程序消息的标签,该标签与服务器的订阅相匹配。服务器会将消息发送给订阅所匹配标签的每个客户端。
系统主题:通过定义$SYS开头的主题可以查看一些系统信息,如客户端连接数量等,
详细介绍:SYS Topics · mqtt/mqtt.org Wiki · GitHub

4.4主题筛选器(Topic Filter)

一个对主题名通配符筛选器,在订阅表达式中使用,表示订阅所匹配到的多个主题。
多级匹配符 #
单级匹配符 +

例如:

(1)对A/#的订阅是对主题A和A下面所有主题的订阅

(2)对A/+的订阅是对A下一级主题的订阅,但不包括A本身

(3)订阅A/+/#是订阅A下面的所有主题(可能包含多级),但不包括A本身

更多主题讨论,请移步

github wiki topic_format · mqtt/mqtt.org Wiki · GitHub

4.5负载(Payload)

消息订阅者所具体接收的内容。

5.保留消息和最后遗嘱

5.1保留消息 Retained Messages

MQTT中,无论是发布还是订阅都不会有任何触发事件。
1Topic只有唯一的retain消息,Broker会保存每个Topic的最后一条retain消息。
发布消息时把retain设置为true,即为保留信息。每个Client订阅Topic后会立即读取到retain消息。如果需要删除retain消息,可以发布一个空的retain消息,因为每个新的retain消息都会覆盖最后一个retain消息。

5.2最后遗嘱 Last Will & Testament

MQTT本身就是为信号不稳定的网络设计的,所以难免一些客户端会无故的和Broker断开连接。 当客户端连接到Broker时,可以指定LWT,Broker会定期检测客户端是否有异常。当客户端异常掉线时,Broker就往连接时指定的topic里推送当时指定的LWT消息。

遗嘱消息的相关设置是在建立连接时的 CONNECT 数据包中设定的:

  • Will Flag:是否使用 LWT 遗嘱消息;
  • Will Topic:遗嘱主题,不可使用通配符;
  • Will QoS:发布遗嘱消息时的 QoS 等级;
  • Will Retain:遗嘱消息的 Retain 标识;
  • Will Message:遗嘱消息内容;

遗嘱消息发布的条件,包括但不限于:

  • Broker 检测到底层的 I/O 异常;
  • Client 未能在 Keepalive 的间隔内和 Broker 之间进行消息交互;
  • Client 在关闭底层 TCP 连接前没有发送 DISCONNECT 数据包;
  • Broker 因为协议错误而关闭了与 Client 的连接;

6.消息服务质量

有三种消息发布服务质量qos(Quality of Service)

6.1“至多一次”Qos0

“至多一次”,消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次记录无所谓,因为不久后还会有第二次发送。

6.2“至少一次”Qos1

“至少一次”, 如果一定时间内,发布端或服务器没有收到PUBACK消息,则会进行重发。这种方式虽然确保了消息到达,但消息重复可能会发生

6.3“只有一次”Qos2

“只有一次”,确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果, 确保用户收到且只会收到一次

0.控制报文的结构

MQTT控制报文由三部分组成:固定报头  可变报头 有效载荷

Fixed header固定报头,所有控制报文都包含
Variable header可变报头,部分控制报文包含
Payload有效载荷,部分控制报文包含

0.1 固定报头

  • 每个 MQTT 控制报文都包  含一个固定报头。固定报头的数据长度为 2~5字节。(Byte = 8bit

固定报头的格式:(MSB,高位在前)

Bit76543210
byte 1MQTT控制报文的类型(1~14)用于指定控制报文类型的标志位
byte 2~5剩余长度(1~4Byte)
  • 剩余长度字段的长度,取决于帧长度。

0.1.1 控制报文的类型

位置:固定报头中,第 1 个字节的 Bit 7~4

控制报文的定义如下:(0/15是保留值,无用。有效类型为 1~14

名字报文流动方向描述
Reserved0禁止保留
CONNECT1客户端到服务端客户端请求连接服务端
CONNACK2服务端到客户端连接报文确认
PUBLISH3两个方向都允许发布消息
PUBACK4两个方向都允许QoS 1消息发布收到确认
PUBREC5两个方向都允许发布收到(保证交付第一步)
PUBREL6两个方向都允许发布释放(保证交付第二步)
PUBCOMP7两个方向都允许QoS 2消息发布完成(保证交互第三步)
SUBSCRIBE8客户端到服务端客户端订阅请求
SUBACK9服务端到客户端订阅请求报文确认
UNSUBSCRIBE10客户端到服务端客户端取消订阅请求
UNSUBACK11服务端到客户端取消订阅报文确认
PINGREQ12客户端到服务端心跳请求
PINGRESP13服务端到客户端心跳响应
DISCONNECT14客户端到服务端客户端断开连接
Reserved15禁止保留
  • QosQuality of service,服务质量。Qos 0/1/2 MQTT规定的服务质量等级,其代表消息传递时不同的可靠程度
  • 其中,567 Qos2时才会有的控制报文

0.0.2 控制报文类型的标志位

 位置:固定报头中,第 1 个字节的 Bit 3~0

控制报文固定报头标志Bit 3Bit 2Bit 1Bit 0
CONNECTReserved0000
CONNACKReserved0000
PUBLISHUsed in MQTT 3.1.1DUP1QoS2QoS2RETAIN3
PUBACKReserved0000
PUBRECReserved0000
PUBRELReserved0010
PUBCOMPReserved0000
SUBSCRIBEReserved0010
SUBACKReserved0000
UNSUBSCRIBEReserved0010
UNSUBACKReserved0000
PINGREQReserved0000
PINGRESPReserved0000
DISCONNECTReserved0000
  • DUP1 =PUBLISH控制报文的重复分发标志

如果该标志被置0,表示这是客户端或服务端第一次请求发送这个PUBLISH报文。如果该标志被置1,表示这可能是一个早前报文请求的重发。

  • QoS2 = PUBLISH报文的服务质量等级
  • RETAIN3= PUBLISH报文的保留标志

协议头示例

https://pics5.baidu.com/feed/63d0f703918fa0ecef4e826637cd3de73c6ddbb0.jpeg@f_auto?token=cd3273334b6e9ab0be188f540e595b97

0.0.3 剩余长度

  • 位置:固定报头中,从第2个字节开始
  • 剩余长度等于可变报头的长度(10字节)加上有效载荷的长度
  • 剩余长度(Remaining Length)表示当前报文剩余部分的字节数,包括可变报头和负载的数据
  • 剩余长度不包括用于编码剩余长度字段本身的字节数。

剩余长度字段 的帧格式:

https://i-blog.csdnimg.cn/blog_migrate/889c26407d5ad8d95f0a29c02e2afde2.png

  • 剩余长度字段 的字节长度:最少1个字节,最多4个字节。
  • 剩余长度字段 可以表示的长度1个字节时,可以表示剩余 0~127 长度。4个字节时,最大表示长度为 2^(7*4) - 1 = 2^28 - 1 = 268435455 长度

https://i-blog.csdnimg.cn/blog_migrate/ca50cf87b48faeaea918fbf648cb3cb7.png

字节数最小值最大值
10 (0x00)127 (0x7F)
2128 (0x80, 0x01)16 383 (0xFF, 0x7F)
316 384 (0x80, 0x80, 0x01)2 097 151 (0xFF, 0xFF, 0x7F)
42 097 152 (0x80, 0x80, 0x80, 0x01)268 435 455 (0xFF, 0xFF, 0xFF, 0x7F)

提示:
       之所以1个字节不能表示 2^8 - 1 = 255长度,是因为:每个字节的最高位 Bit7,并不表示数据,是进位标志位。

       剩余长度的计算:
示例1

      假设本帧剩余字节为 200,计算剩余长度字段。

       使用电脑计算器,将 200 转换为二进制 1100 1000MSB高位在前)
       从右侧低位每7Bit进行一次拆分,依次拆分出:

       1个字节为 100 1000,有进位,高位加上进位1 1100 1000 = 0xC8 (16进制)
       2个字节为 1,无进位,为 1 = 0x01 (16进制)
       那么对应的 字节长度的帧格式如下表:

https://i-blog.csdnimg.cn/blog_migrate/0fc45a4f7fa96a9acae682015282922f.png

示例2
       假设本帧剩余字节为 1000,计算剩余长度字段。

       使用电脑计算器,将 1000 转换为二进制 11 1110 1000MSB高位在前)
       从右侧低位每7Bit进行一次拆分,依次拆分出:

       1个字节为 110 1000,有进位,高位加上进位1 1110 1000 = 0xE8 (16进制)
       2个字节为 11 1,无进位,为 11 1 = 0x07 (16进制)
       那么对应的 字节长度的帧格式如下表:

https://i-blog.csdnimg.cn/blog_migrate/540a7709e70faf20c5190e6ba811c15a.png

示例3
       假设本帧剩余字节为 100,000,000,计算剩余长度字段。

       使用电脑计算器,将 100,000,000 转换为二进制 101 1111 0101 1110 0001 0000 0000MSB高位在前)

       从右侧低位每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进制)
       那么对应的 字节长度的帧格式如下表:

https://i-blog.csdnimg.cn/blog_migrate/021144b7c60b0fcac69980bd05194fa2.png

0.2 可变报头 

       某些 MQTT 控制报文包含一个可变报头部分。它在固定报头和负载之间。

       可变报头的内容根据报文类型的不同而不同。

       可变报头的报文标识符(Packet Identifier)字段存在于多个类型的报文里。

0.2.1 报文标识符 Packet Identifier

Bit7 - 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不需要

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

0.3 有效载荷

      某些MQTT控制报文在报文的最后部分包含一个有效载荷。对于PUBLISH来说有效载荷就是应用消息。

控制报文有效载荷
CONNECT需要
CONNACK不需要
PUBLISH可选
PUBACK不需要
PUBREC不需要
PUBREL不需要
PUBCOMP不需要
SUBSCRIBE需要
SUBACK需要
UNSUBSCRIBE需要
UNSUBACK不需要
PINGREQ不需要
PINGRESP不需要
DISCONNECT不需要

1.心跳

1.1 心跳请求(PINGREQ)

客户端发送PINGREQ报文给服务端的。用于:

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

PINGREQ CONNECT 可变报头中的 保持连接 Keep Alive 字段有非常大的关联。

保持连接(Keep Alive)是一个以秒为单位的时间间隔,表示为一个16位的字,它是指在客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。客户端负责保证控制报文发送的时间间隔不超过保持连接的值。如果没有任何其它的控制报文可以发送,客户端必须发送一个PINGREQ报文。

不管保持连接的值是多少,客户端任何时候都可以发送PINGREQ报文,并且使用PINGRESP报文判断网络和服务端的活动状态。

如果保持连接的值非零,并且服务端在一点五倍的保持连接时间内没有收到客户端的PINGREQ控制报文,它必须断开客户端的网络连接,认为网络连接已断开。

客户端发送了PINGREQ报文之后,如果在合理的时间内仍没有收到PINGRESP报文,它应该关闭到服务端的网络连接。

保持连接的实际值是由应用指定的,一般是几分钟。允许的最大值是18小时1215秒。(Keep Alive 取值0~65535,如果 keep alive 的间隔为 0 keep alive 机制是无效的)

Bit76543210
byte 1MQTT控制报文类型 (12)保留位
11000000
byte 2剩余长度 (0)
00000000

       PINGREQ 只有固定报头字段,且报文长度只有 2 字节。

       PINGREQ 没有 可变报头 字段。

       PINGREQ 没有 有效载荷 字段。

 

1.2心跳响应(PINGRESP)

服务端发送PINGRESP报文响应客户端的PINGREQ报文。表示服务端还活着。

Bit76543210
byte 1MQTT控制报文类型 (13)保留位
10100000
byte 2剩余长度 (0)
00000000

       PINGRESP 只有固定报头字段,且报文长度只有 2 字节。

       PINGRESP 没有 可变报头 字段。

       PINGRESP 没有 有效载荷 字段。

2.连接

2.1 CONNECT - 连接服务器

  • 客户端到服务端的TCP/UDP网络连接建立后,客户端发送给服务端的第一条报文必须是 CONNECT 报文
  • 在一个网络连接上,客户端只能发送一次 CONNECT 报文
  • 服务端必须将客户端发送的第二个 CONNECT 报文当作协议违规。

https://i-blog.csdnimg.cn/blog_migrate/f378ab37a2765b22aa9399e5dd8fe7dd.png

2.1.1 CONNECT固定报文头 

Bit76543210
byte 1MQTT报文类型 (1)Reserved 保留位
00010000
byte 2~5剩余长度
  • CONNECT 的第1个字节为 0x10,剩余长度字段需要根据可变报头和有效载荷的长度来确定。

 

 

  • 剩余长度字段等于可变报头的长度加上有效载荷的长度。图中为280x1C)。

 

 2.1.2 CONNECT 可变报头

       CONNECT的可变报头包含四个字段:

https://i-blog.csdnimg.cn/blog_migrate/700b90678fd6f0764313f2d9b7f9a5cf.png

  • 协议名(Protocol Name

       可变报头中,协议名字段的6个字节是固定的:

https://i-blog.csdnimg.cn/blog_migrate/3746c30e6a36bf3ab7fc5c0a78ab01fd.png

 

 

  • 协议级别(Protocol Level

https://i-blog.csdnimg.cn/blog_migrate/a2341064d7e80f6a5ee901d974efd8fe.png

   客户端用8位的无符号值表示协议的修订版本。对于3.1.1MQTT协议,协议级别字段的值是4(0x04)。如果发现不支持的协议级别,服务端必须给发送一个返回码为0x01(不支持的协议级别)的CONNACK报文响应CONNECT报文,然后断开客户端的连接。

 

  • 连接标志(Connect Flags

       连接标志字节包含一些用于指定MQTT连接行为的参数。它还指出有效载荷中的字段是否存在。

https://i-blog.csdnimg.cn/blog_migrate/3fb070c35e4c8fee2facf26e3f620055.png

Bit 1:清理会话标志位。这个标志位用于控制会话状态的生存时间。如果清理会话(CleanSession)标志被设置为0,服务端必须基于当前会话(使用客户端标识符识别)的状态恢复与客户端的通信;如果清理会话(CleanSession)标志被设置为1,客户端和服务端必须丢弃之前的任何会话并开始一个新的会话。会话仅持续和网络连接同样长的时间。与这个会话关联的状态数据不能被任何之后的会话重用。

Bit 2:遗嘱标志位。遗嘱标志(Will Flag)被设置为1,表示如果连接请求被接受了,遗嘱(Will Message)消息必须被存储在服务端并且与这个网络连接关联。之后网络连接关闭时,服务端必须发布这个遗嘱消息,除非服务端收到DISCONNECT报文时删除了这个遗嘱消息。如果遗嘱标志被设置为1,连接标志中的Will QoSWill Retain字段会被服务端用到,同时有效载荷中必须包含Will TopicWill 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的第34位。

当启用遗嘱保留标志(byte8的第5位)时,表示遗嘱消息在发布时需要保留,即启动遗嘱保留时,服务端要持久化该遗嘱消息(仅仅保留最新一条),其他人后订阅该遗嘱主题时,能收到最后一次的遗嘱消息。

总结一下连接标志字节的常见的情形

https://pics7.baidu.com/feed/0eb30f2442a7d9330be778c3b411b51a72f00192.jpeg@f_auto?token=a302313e62ddfea564684451d5c068fe

  • 保持连接时间(Keep Alive

https://i-blog.csdnimg.cn/blog_migrate/9345ad8f7596fa48d302f0968820c636.png

       保持连接(Keep Alive是一个以秒为单位的时间长度,表示为一个16位的字,它是指在客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。客户端负责保证控制报文发送的时间间隔不超过保持连接的值。如果没有任何其它的控制报文可以发送,客户端必须发送一个PINGREQ报文。(详见1.1心跳请求)

常见的CONNECT可变报头

https://pics1.baidu.com/feed/dc54564e9258d109ba47bcf0c902a8b66d814daa.jpeg@f_auto?token=ee1a3b37178c2e367d27bc3953f5e50d

2.2 连接响应(CONNACK)

       服务端发送CONNACK报文响应从客户端收到的CONNECT报文。服务端发送给客户端的第一个报文必须是CONNACK

       如果客户端在合理的时间内没有收到服务端的CONNACK报文,客户端应该关闭网络连接。

https://i-blog.csdnimg.cn/blog_migrate/0f46018bc9ab61daec3bbdbd80049281.png

       CONNACK 没有 有效载荷 字段。

2.2.1 固定报头 

Bit76543210
byte 1MQTT报文类型 (2)Reserved 保留位
00100000
byte 2剩余长度 (2)
00000010

       剩余长度值,表示可变报头的长度。对于CONNACK报文这个值为2

 

 2.2.2 可变报头

https://i-blog.csdnimg.cn/blog_migrate/607fcc0170a15df9acbe836dd34250fa.png

       当前会话标志位 SPSession Present):如果服务端收到清理会话(CleanSession)标志为1的连接,除了将CONNACK报文中的返回码设置为0之外,还必须将CONNACK报文中的当前会话设置(Session Present)标志为0

       如果服务端收到一个CleanSession0的连接,当前会话标志的值取决于服务端是否已经保存了ClientId对应客户端的会话状态。如果服务端已经保存了会话状态,它必须将CONNACK报文中的当前会话标志设置为1。如果服务端没有已保存的会话状态,它必须将CONNACK报文中的当前会话设置为0。还需要将CONNACK报文中的返回码设置为0

   如果服务端发送了一个包含非零返回码的CONNACK报文,它必须将当前会话标志设置为0
       连接返回码:如果服务端收到一个合法的CONNECT报文,但出于某些原因无法处理它,服务端应该尝试发送一个包含非零返回码的CONNACK报文。

      如果服务端发送了一个包含非零返回码的CONNACK报文,那么它必须关闭网络连接。

返回码响应描述
00x00连接已接受连接已被服务端接受
10x01连接已拒绝,不支持的协议版本服务端不支持客户端请求的MQTT协议级别
20x02连接已拒绝,不合格的客户端标识符客户端标识符是正确的UTF-8编码,但服务端不允许使用
30x03连接已拒绝,服务端不可用网络连接已建立,但MQTT服务不可用
40x04连接已拒绝,无效的用户名或密码用户名或密码的数据格式无效
50x05连接已拒绝,未授权客户端未被授权连接到此服务器
6-255保留

       如果认为上表中的所有连接返回码都不太合适,那么服务端必须关闭网络连接,不需要发送CONNACK报文。

       CONNACK 没有 有效载荷 字段。

2.3 DISCONNECT - 断开连接

  DISCONNECT报文是客户端发给服务端的最后一个控制报文。表示客户端正常断开连接。

2.3.1 固定报头

Bit76543210
byte 1MQTT控制报文类型 (14)保留位
11100000
byte 2剩余长度 (0)
00000000

       CONNACK 没有 可变报头 字段。

       CONNACK 没有 有效载荷 字段。

       客户端发送DISCONNECT报文之后,必须关闭网络连接,并且不能通过这个网络连接再发送任何控制报文。

       服务端在收到DISCONNECT报文时,必须丢弃任何与当前连接关联的未发布的遗嘱消息,并且关闭网络连接(如果客户端 还没关闭连接的话)。

 

3.订阅

3.1 SUBSCRIBE - 订阅主题报文

       客户端向服务端发送SUBSCRIBE报文用于创建一个或多个订阅。每个订阅注册客户端关心的一个或多个主题。为了将应用消息转发给那些订阅匹配的主题,服务端发送PUBLISH报文给客户端。SUBSCRIBE报文也(为每个订阅)指定了最大的QoS等级,服务端根据这个发送应用消息给客户端。

3.1.1 固定报头

Bit76543210
byte 1MQTT控制报文类型 (8)保留位
10000000
byte 2~4剩余长度

       SUBSCRIBE控制报固定报头的第3,2,1,0位是保留位,必须分别设置为0,0,1,0。服务端必须将其它的任何值都当做是不合法的并关闭网络连接。

       剩余长度字段:等于SUBSCRIBE可变报头的长度(2字节)加上有效载荷的长度。

 

3.1.2 可变报头

       可变报头包含客户端报文标识符。

       报文标识符相当于自定义的TopicID,用ID号去代替具体的Topic,而不是字段,使得区分发来的Topic的同时又可以节省流量,可自定义,建议自己预先拟定一个服务ID表。

       订阅返回,返回Topic订阅成功信息,返回的不是具体Topic,返回的就是报文标识符。

https://i-blog.csdnimg.cn/blog_migrate/5ade4933989556e24d467cabb65531f7.png

 

3.1.3 有效载荷

       SUBSCRIBE报文的有效载荷包含了一个主题过滤器列表,它们表示客户端想要订阅的主题。SUBSCRIBE报文有效载荷中的主题过滤器列表必须是UTF-8字符串。服务端应该支持包含通配符的主题过滤器。如果服务端选择不支持包含通配符的主题过滤器,必须拒绝任何包含通配符过滤器的订阅请求。每一个过滤器后面跟着一个字节,这个字节被叫做 服务质量要求(Requested QoS)。它给出了服务端向客户端发送应用消息所允许的最大QoS等级。

       SUBSCRIBE报文的有效载荷必须包含至少一对主题过滤器 QoS等级字段组合。没有有效载荷的SUBSCRIBE报文是违反协议的。

https://i-blog.csdnimg.cn/blog_migrate/ba45e1835be04170a1eb49e1595ca2dd.png

       当前版本的协议没有用到服务质量要求(Requested QoS)字节的高六位。如果有效载荷中的任何位是非零值,或者QoS不等于0,12,服务端必须认为SUBSCRIBE报文是不合法的并关闭网络连接。

       响应:服务端收到客户端发送的一个SUBSCRIBE报文时,必须使用SUBACK报文响应。

       服务端发送给客户端的SUBACK报文对每一对主题过滤器 QoS等级都必须包含一个返回码。这个返回码必须表示那个订阅被授予的最大QoS等级,或者表示这个订阅失败。服务端可以授予比订阅者要求的低一些的QoS等级。为响应订阅而发出的消息的有效载荷的QoS必须是原始发布消息的QoS和服务端授予的QoS两者中的最小值。如果原始消息的QoS1而被授予的最大QoS0,允许服务端重复发送一个消息的副本给订阅者。

 

 

3.2订阅确认(SUBACK)

      服务端发送SUBACK报文给客户端,用于确认它已收到并且正在处理SUBSCRIBE报文。

       SUBACK报文包含一个返回码清单,它们指定了SUBSCRIBE请求的每个订阅被授予的最大QoS等级。

3.2.1 固定报头

Bit76543210
byte 1MQTT控制报文类型 (11)保留位
10110000
byte 2剩余长度 (2)
00000010

 

3.2.2 可变报头

       可变报头包含等待确认的SUBSCRIBE报文的报文标识符。

Bit76543210
byte 1报文标识符 MSB
byte 2报文标识符 LSB

 

3.2.3 有效载荷

       有效载荷包含一个返回码清单。每个返回码对应等待确认的SUBSCRIBE报文中的一个主题过滤器。返回码的顺序必须和SUBSCRIBE报文中主题过滤器的顺序相同。

       下面表格描述了有效载荷中单字节编码的返回码字段。

https://i-blog.csdnimg.cn/blog_migrate/48618eb43855cd513fe7261cd1dbaa19.png

       允许的返回码值:

       0x00 - 最大 Qos0

       0x01 - 成功 - 最大 Qos1

       0x02 - 成功 - 最大 Qos2

       0x80 - Failure 失败

       0x00, 0x01, 0x02, 0x80之外的SUBACK返回码是保留的,不能使用。

 

3.3取消订阅(UNSUBSCRIBE)

       客户端发送UNSUBSCRIBE报文给服务端,用于取消订阅主题。

3.3.1 固定报头

Bit76543210
byte 1MQTT控制报文类型 (10)保留位
10100010
byte 2剩余长度

       UNSUBSCRIBE报文固定报头的第3,2,1,0位是保留位且必须分别设置为0,0,1,0。服务端必须认为任何其它的值都是不合法的并关闭网络连接。

 

 

3.3.2 可变报头

       可变报头包含一个报文标识符。

Bit76543210
byte 1报文标识符 MSB
byte 2报文标识符 LSB

 

3.3.3 有效载荷

       UNSUBSCRIBE报文的有效载荷包含客户端想要取消订阅的主题过滤器列表。UNSUBSCRIBE报文中的主题过滤器必须是连续打包的UTF-8编码字符串。

       UNSUBSCRIBE报文的有效载荷必须至少包含一个消息过滤器。没有有效载荷的UNSUBSCRIBE报文是违反协议的。

https://i-blog.csdnimg.cn/blog_migrate/95a5033c71489ad6ede17c97cdacc14f.png

       UNSUBSCRIBE的有效载荷比SUBSCRIBE少了服务质量要求Requested QoS)。

       响应:UNSUBSCRIBE报文提供的主题过滤器(无论是否包含通配符)必须与服务端持有的这个客户端的当前主题过滤器集合逐个字符比较。如果有任何过滤器完全匹配,那么它(服务端)自己的订阅将被删除,否则不会有进一步的处理

       如果服务端删除了一个订阅:

       它必须停止分发任何新消息给这个客户端。
       它必须完成分发任何已经开始往客户端发送的QoS 1QoS 2的消息。
       它可以继续发送任何现存的准备分发给客户端的缓存消息。

      服务端必须发送UNSUBACK报文响应客户端的UNSUBSCRIBE请求。UNSUBACK报文必须包含和UNSUBSCRIBE报文相同的报文标识符 。即使没有删除任何主题订阅,服务端也必须发送一个SUBACK响应。

 

 

3.4 取消订阅确认(UNSUBACK)

       服务端发送UNSUBACK报文给客户端用于确认收到UNSUBSCRIBE报文。

3.4.1 固定报头

Bit76543210
byte 1MQTT控制报文类型 (11)保留位
10110000
byte 2剩余长度 (2)
00000010

       剩余长度字段:表示可变报头的长度,对UNSUBACK报文这个值等于2

 

3.4.2 可变报头 

       可变报头包含等待确认的UNSUBSCRIBE报文的报文标识符。

Bit76543210
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

 

4.发布

4.1 发布消息(PUBLISH)

       PUBLISH控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。

4.1.1 固定报头

Bit76543210
byte 1MQTT控制报文类型 (3)DUPQoS-HQoS-LRETAIN
0011XXXX
byte 2~5剩余长度
DUP重发标志 

       位置:第1个字节,第3位。

       如果DUP标志被设置为0,表示这是客户端或服务端第一次请求发送这个PUBLISH报文。如果DUP标志被设置为1,表示这可能是一个早前报文请求的重发。

       客户端或服务端请求重发一个PUBLISH报文时,必须将DUP标志设置为1。对于QoS 0的消息,DUP标志必须设置为0

Qos服务质量等级

      位置:第1个字节,第2-1位。这个字段表示应用消息分发的服务质量等级保证。

QoS值Bit 2Bit 1描述
000最多分发一次
101至少分发一次
210只分发一次
-11保留位

        PUBLISH报文不能将QoS所有的位设置为1。如果服务端或客户端收到QoS所有位都为1PUBLISH报文,它必须关闭网络连接。

RETAIN保留标志

       位置:第1个字节,第0位。

       如果客户端发给服务端的PUBLISH报文的保留(RETAIN)标志被设置为1,服务端必须存储这个应用消息和它的服务质量等级(QoS),以便它可以被分发给未来的主题名匹配的订阅者。一个新的订阅建立时,对每个匹配的主题名,如果存在最近保留的消息,它必须被发送给这个订阅者。如果服务端收到一条保留(RETAIN)标志为1QoS 0消息,它必须丢弃之前为那个主题保留的任何消息。它应该将这个新的QoS 0消息当作那个主题的新保留消息,但是任何时候都可以选择丢弃它。如果这种情况发生了,那个主题将没有保留消息。

       服务端发送PUBLISH报文给客户端时,如果消息是作为客户端一个新订阅的结果发送,它必须将报文的保留标志设为1。当一个PUBLISH报文发送给客户端是因为匹配一个已建立的订阅时,服务端必须将保留标志设为0,不管它收到的这个消息中保留标志的值是多少。

       保留标志为1且有效载荷为零字节的PUBLISH报文会被服务端当作正常消息处理,它会被发送给订阅主题匹配的客户端。此外,同一个主题下任何现存的保留消息必须被移除,因此这个主题之后的任何订阅者都不会收到一个保留消息。当作正常 意思是现存的客户端收到的消息中保留标志未被设置。服务端不能存储零字节的保留消息。

       如果客户端发给服务端的PUBLISH报文的保留标志位0,服务端不能存储这个消息也不能移除或替换任何现存的保留消息。

 

 

剩余长度字段等于可变报头的长度加上有效载荷的长度。图中为261000100000101B),按照之前剩余长度字段的算法第一个字节为0x8510000101B),第二个字节为0x0210B)。

 

 

4.1.2 可变报头

       可变报头按顺序包含主题名和报文标识符。

https://i-blog.csdnimg.cn/blog_migrate/2c429d507e766c219e8ba3a7eacbcd8c.png

Byte1Byte2是主题名长度。

 

 

       主题名(Topic Name)用于识别有效载荷数据应该被发布到哪一个信息通道。

       主题名必须是PUBLISH报文可变报头的第一个字段。它必须是 1.5.3节定义的UTF-8编码的字符串。

       PUBLISH报文中的主题名不能包含通配符,字母的大写与小写对应不同Topic

 

       服务端发送给订阅客户端的PUBLISH报文的主题名必须匹配该订阅的主题过滤器

       只有当QoS等级是12时,报文标识符(Packet Identifier)字段才能出现在PUBLISH报文中。

 

 

 

 

4.1.3 有效载荷

       有效载荷包含将被发布的应用消息。数据的内容和格式是应用特定的。有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度。包含零长度有效载荷的PUBLISH报文是合法的。

       响应:PUBLISH报文的接收者必须按照根据PUBLISH报文中的QoS等级发送响应。

服务质量等级预期响应
QoS 0无响应
QoS 1PUBACK报文
QoS 2PUBREC报文

动作:

  • 客户端使用PUBLISH报文发送应用消息给服务端,目的是分发到其它订阅匹配的客户端。
  • 服务端使用PUBLISH报文发送应用消息给每一个订阅匹配的客户端。

 

 

 

4.2 发布确认( PUBACK)

       PUBACK报文是对QoS 1等级的PUBLISH报文的响应。

4.2.1 固定报头

Bit76543210
byte 1MQTT报文类型 (4)保留位
01000000
byte 2剩余长度
00000010

        剩余长度字段:表示可变报头的长度。对PUBACK报文这个值等于2

 

 

4.2.2 可变报头

       包含等待确认的PUBLISH报文的报文标识符。

Bit76543210
byte 1报文标识符 MSB
byte 2报文标识符 LSB

  PUBACK 报文没有有效载荷。

  PUBACK 报文只有 固定报头 可变报头,共4个字节。

 

 

4.3 发布收到(PUBREC)

     PUBREC报文是对QoS等级2PUBLISH报文的响应。它是QoS 2等级协议交换的第二个报文。

4.3.1 固定报头 

Bit76543210
byte 1MQTT控制报文类型 (5)保留位
01010000
byte 2剩余长度 (2)
00000010

 剩余长度字段:表示可变报头的长度。对PUBREC报文这个值等于2 

 

4.3.2 可变报头

可变报头包含等待确认的PUBLISH报文的报文标识符。

Bit76543210
byte 1报文标识符 MSB
byte 2报文标识符 LSB

 PUBREC报文没有有效载荷。

 

4.4 发布释放(PUBREL) 

Bit76543210
byte 1MQTT控制报文类型 (6)保留位
11000010
byte 2剩余长度 (2)
00000010

PUBREL报文是对PUBREC报文的响应。它是QoS 2等级协议交换的第三个报文。

4.4.1 固定报头 

PUBREL控制报文固定报头的第3,2,1,0位是保留位,必须被设置为0,0,1,0。服务端必须将其它的任何值都当做是不合法的并关闭网络连接。

剩余长度字段:表示可变报头的长度。对PUBREL报文这个值等于2

 

 

4.4.2 可变报头

可变报头包含与等待确认的PUBREC报文相同的报文标识符。

Bit76543210
byte 1报文标识符 MSB
byte 2报文标识符 LSB

 PUBREL 报文没有有效载荷。

 

4.5 发布收到(PUBCOMP)

PUBCOMP报文是对PUBREL报文的响应。它是QoS 2等级协议交换的第四个也是最后一个报文。

4.5.1 固定报头

Bit76543210
byte 1MQTT控制报文类型 (7)保留位
01110000
byte 2剩余长度 (2)
00000010

剩余长度字段:表示可变报头的长度。对PUBCOMP报文这个值等于2

 

 

4.5.2 可变报头

可变报头包含与等待确认的PUBREL报文相同的报文标识符。

Bit76543210
byte 1报文标识符 MSB
byte 2报文标识符 LSB

 PUBCOMP 报文没有有效载荷。

 

参考:

第一章 - MQTT介绍 - MQTT 协议中文版 - 开发文档 - 文江博客1.1 MQTT协议的组织结构 Organization of MQTT本规范分为七个章节: 第一章 – 介绍第二章 – MQTT控制报文格式第三章 – MQTT控…https://www.wenjiangs.com/doc/etuaya5fp

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值