4、MQTT报文格式

1、MQTT 控制报文格式

  MQTT 控制报文的结构

  在MQTT协议中,一个MQTT数据包由:固定头(Fixed header)、可变头(Variable header)、消息体(payload)三部分构成。MQTT数据包结构如下:

  (1)固定头(Fixed header):存在于所有MQTT数据包中,表示数据包类型及数据包的分组类标识。
  (2)可变头(Variable header):存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其具体内容。
  (3)消息体(Payload):存在于部分MQTT数据包中,表示客户端收到的具体内容。

  1.1、 固定报头

  每个 MQTT 控制报文都包含一个固定报头。固定报头的格式如下图所示:

  MQTT固定报文头最少有两个字节,第一字节包含消息类型(Message Type)和QoS级别等标志位。第二字节开始是剩余长度字段,该长度是后面的可变报文头加消息负载的总长度,该字段最多允许四个字节。

  剩余长度字段单个字节最大值为二进制0b0111 1111,16进制0x7F。也就是说,单个字节可以描述的最大长度是127字节。为什么不是256字节呢?因为MQTT协议规定,单个字节第八位(最高位)若为1,则表示后续还有字节存在,第八位起“延续位”的作用。

  例如,数字64,编码为一个字节,十进制表示为64,十六进制表示为0×40。数字321(65+2*128)编码为两个字节,重要性最低的放在前面,第一个字节为65+128=193(0xC1),第二个字节是2(0x02),表示2×128。

  由于MQTT协议最多只允许使用四个字节表示剩余长度(如表1),并且最后一字节最大值只能是0x7F不能是0xFF,所以能发送的最大消息长度是256MB,而不是512MB。

  (1)MQTT 控制报文的类型

  位置:byte1, bit7-bit4。

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

名字

报文流动方向

描述

Reserved

0

禁止

保留

CONNECT

1

客户端到服务端

客户端请求连接服务端

CONNACK

2

服务端到客户端

连接报文确认

PUBLISH

3

两个方向都允许

发布消息

PUBACK

4

两个方向都允许

QoS 1 消息发布收到确认

PUBREC

5

两个方向都允许

发布收到(保证交付第一步)

PUBREL

6

两个方向都允许

发布释放(保证交付第二步)MQTT-3.1.1-CN 15

PUBCOMP

7

两个方向都允许

QoS 2 消息发布完成(保证交互第三步)

SUBSCRIBE

8

客户端到服务端

客户端订阅请求

SUBACK

9

服务端到客户端

订阅请求报文确认

UNSUBSCRIBE

10

客户端到服务端

客户端取消订阅请求

UNSUBACK

11

服务端到客户端

取消订阅报文确认

PINGREQ

12

客户端到服务端

心跳请求

PINGRESP

13

服务端到客户端

心跳响应

DISCONNECT

14

客户端到服务端

客户端断开连接

Reserved

15

禁止

保留

  固定报文头中的第一个字节包含连接标志(Connect Flags),连接标志用来区分MQTT的消息类型。MQTT协议拥有14种不同的消息类型,可简单分为连接及终止、发布和订阅、QoS 2消息的机制以及各种确认ACK。

  (2)、用于指定控制报文类型的标志位

  固定报头第 1 个字节的剩余的 4 位 [3-0]包含每个 MQTT 控制报文类型特定的标志。

  表格中任何标记为“保留”的标志位, 都是保留给以后使用的, 必须设置为表格中列出的值 。 在不使用标识位的消息类型中,标识位被作为保留位。如果收到无效的标志时,接收端必须关闭网络连接。

控制报文

固定报头标志

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

 

  DUP:发布消息的副本。用来在保证消息的可靠传输,如果设置为1,则在下面的变长中增加MessageId,并且需要回复确认,以保证消息传输完成,但不能用于检测消息重复发送。

  QoS:发布消息的服务质量,即:保证消息传递的次数
    00:最多一次(QoS==0),即:<=1。消息发布完全依赖底层 TCP/IP 网络。协议里没有定义应答和重试,会发生消息丢失或重复。消息要么只会到达服务端一次,要么根本没有到达。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
    01:至少一次(QoS==1),即:>=1。确保消息到达,但消息重复可能会发生。服务器的消息接收由PUBACK消息进行确认,如果通信链路或发送设备异常,或者指定时间内没有收到确认消息,发送端会重发这条在消息头中设置了DUP位的消息。
    10:只有一次(QoS==2),即:=1。确保消息到达一次。这是最高级别的消息传递,消息丢失和重复都是不可接受的,使用这个服务质量等级会有额外的开销。 这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果。小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量。
    11:预留

    比如目前流行的共享单车智能锁,智能锁可以定时使用QoS level 0质量消息请求服务器,发送单车的当前位置,如果服务器没收到也没关系,反正过一段时间又会再发送一次。之后用户可以通过App查询周围单车位置,找到单车后需要进行解锁,这时候可以使用QoS level 1质量消息,手机App不断的发送解锁消息给单车锁,确保有一次消息能达到以解锁单车。最后用户用完单车后,需要提交付款表单,可以使用QoS level 2质量消息,这样确保只传递一次数据,否则用户就会多付钱了。

  RETAIN: 发布保留标识,表示服务器要保留这次推送的信息,如果有新的订阅者出现,就把这消息推送给它,如果设有那么推送至当前订阅者后释放。

  (3)、 剩余长度

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

  固定头的第二字节用来保存变长头部和消息体的总大小的,但不是直接保存的。这一字节是可以扩展,其保存机制,前7位用于保存长度,后一部用做标识。当最后一位为1时,表示长度不足,需要使用二个字节继续保存。例如:计算出后面的大小为0

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

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

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

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

  表格  剩余长度字段的大小

  1.2、可变报头

  可变报文头主要包含协议名、协议版本、连接标志(Connect Flags)、心跳间隔时间(Keep Alive timer)、连接返回码(Connect Return Code)、主题名(Topic Name)等。

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

  (1) 报文标识符

  (2)遗愿标志(Will Flag)

  在可变报文头的连接标志位字段(Connect Flags)里有三个Will标志位:Will Flag、Will QoS和Will Retain Flag,这些Will字段用于监控客户端与服务器之间的连接状况。如果设置了Will Flag,就必须设置Will QoS和Will Retain标志位,消息主体中也必须有Will Topic和Will Message字段。

  那遗愿消息是怎么回事呢?服务器与客户端通信时,当遇到异常或客户端心跳超时的情况,MQTT服务器会替客户端发布一个Will消息。当然如果服务器收到来自客户端的DISCONNECT消息,则不会触发Will消息的发送。 

  因此,Will字段可以应用于设备掉线后需要通知用户的场景。

  (3)连接保活心跳机制(Keep Alive Timer)

  MQTT客户端可以设置一个心跳间隔时间(Keep Alive Timer),表示在每个心跳间隔时间内发送一条消息。如果在这个时间周期内,没有业务数据相关的消息,客户端会发一个PINGREQ消息,相应的,服务器会返回一个PINGRESP消息进行确认。如果服务器在一个半(1.5)心跳间隔时间周期内没有收到来自客户端的消息,就会断开与客户端的连接。心跳间隔时间最大值大约可以设置为18个小时,0值意味着客户端不断开。

  1.3、有效载荷(Payload)

  Payload直译为负荷,可能让人摸不着头脑,实际上可以理解为消息主体(body)。

  当MQTT发送的消息类型是CONNECT(连接)、PUBLISH(发布)、SUBSCRIBE(订阅)、SUBACK(订阅确认)、UNSUBSCRIBE(取消订阅)时,则会带有负荷。

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

  下面表格包含有效载荷的控制报文 列出了需要有效载荷的控制报文。

 

posted @ 2020-08-12 17:16  孤情剑客  阅读(3390)  评论(0编辑  收藏  举报