MQTT自学笔记(二)—Message format

前言:

关于MQTT的学习我是参照的MQTT V3.1版本。
这里有MQTT V3.1的在线版本:在线版本
官方下载地址:PDF格式版本
我就是看这个官方的英文文档学习的,其实我这博客基本上就是翻译和总结了这个协议文档。

Message format

每个MQTT消息头命令消息包含一个Fixed header (固定头部)。一些信息还需要一个Variable header(可变头部)和Payload(有效载荷)。消息头每个部分的格式描述在以下部分:

1. Fixed header

每个MQTT消息头命令消息包含一个固定头部。2个字节(16个字)。如下表显示:
Fixed header

Byte 1

包含消息类型和标志(DUP、QoS级别和保留)字段。

1. Mesage Type

Postion: byte 1, bits 7-4
表示为4位无符号值,MQTT V3.1版本的协议枚举在下表:
MQTT自学笔记(二)—Message format_第1张图片
除去0和15位置属于保留,总共14种事件类型。

2. DUP flag(打开标志)

Postion: byte 1, bits 3
保证消息可靠传输,默认为0,只占用一个bit,表示第一次发送,不能用来检测消息重复发送,在客户端或者服务器端尝试重发PUBLISH, PUBREL, SUBSCRIBE 或 UNSUBSCRIBE消息,注意需要满足以下条件:
当QoS > 0
消息需要回复确认

此时,在可变头部需要包含消息ID。当值为1时,表示当前消息先前已经被传送过。

3. QoS level ( Quality of Service)

Postion: byte 1, bits 2-1
这个标志使用两个2进制表示PUBLISH类型的消息:
MQTT自学笔记(二)—Message format_第2张图片

4. RETAIN(保持)

Postion: byte 1, bits 0
这个标志位仅针对PUBLISH消息。
仅针对PUBLISH消息。不同值,不同含义:
1:表示发送的消息需要一直持久保存(不受服务器重启影响),不但要发送给当前的订阅者,并且以后新来的订阅了此Topic name的订阅者会马上得到推送。
备注:新来乍到的订阅者,只会取出最新的一个RETAIN flag = 1的消息推送。
0:仅仅为当前订阅者推送此消息。

假如服务器收到一个空消息体(zero-length payload)、RETAIN = 1、已存在Topic name的PUBLISH消息,服务器可以删除掉对应的已被持久化的PUBLISH消息。

Byte 2

1. Remaining Length

(至少一个字节)包含剩余的长度字段。在以下部分中会描述这部分字段。所有数据值都在高位优先顺序:高位字节在前,低位字节在后。提出了一种16位字作为最重要的字节(MSB),其次是最低有效字节(LSB)。
在当前消息中剩余的byte(字节)数,包含可变头部和负荷(称之为内容/body,更为合适)。单个字节最大值:01111111,16进制:0x7F,10进制为127。
单个字节为什么不能是11111111(0xFF)呢?
因为MQTT协议规定,第八位(最高位)若为1,则表示还有后续字节存在。同时MQTT协议最多允许4个字节表示剩余长度。那么最大长度为:0xFF,0xFF,0xFF,0x7F,二进制表示为:11111111,11111111,11111111,01111111,十进制:268435455
比如:十进制的321是需要用两个字节来表示(65 + 2*128 ),转换成十六进制是0xFF ,0x41,(网络字节顺序)。
byte=261120KB=256MB=0.25GB 四个字节之间值的范围:
MQTT自学笔记(二)—Message format_第3张图片
1、一个十进制编码数转换成Remaining Length(不确定的字节值的算法)如下:

do
{
  digit = X MOD 128
  X = X DIV 128
  // if there are more digits to encode, set the top bit of this digit
  if ( X > 0 )
    digit = digit OR 0x80
  endif
  //'output' digit
  }
while ( X> 0 )

这里的“MOD”表示:按模运算符(% in C),
“DIV”表示:整除算法( / in C )
“OR”表示:是按位或(| in C)。
***digit是以字节为单位的Remaining Length。***digit对val求模,最大值可能是127,一旦127 | 10000000 = 11111111 = 0xff = 255
请注意: Remaining Length(剩余长度),只在Fixed header (固定头部)中,无论是一个字节,还是四个字节,不能被算作Variable header(可变头部)中。
2、对于Remaining Length解析成十进制的算法如下:

multiplier = 1 ;
value = 0 ;
do 
{
  digit = 'next digit from stream' ;
  value += (digit AND 127) * multiplier;
  multiplier *= 128;
 }
while ((digit AND 128) != 0)

这里的”AND”表示:和操作云算符(& inC)。
算法结束时,value就是Remaining Length,这个值是十进制的。一般最后一个字节小于127(01111111),和0x80(10000000)进行&操作,最终结果都为0,因此计算会终止。

Variable header

固定头部仅定义了消息类型和一些标志位,一些消息的元数据,需要放入可变头部中。
需要再次强调的是:
Variable header(可变头部)字节长度 + Playload(负荷)字节长度 = Remaining Length(剩余长度)
这个是需要牢记的。可变头部,包含了协议名称,版本号,连接标志,用户授权,心跳时间等内容,这部分和后面要讲到的CONNECT消息类型,有重复,暂时略过。

Payload

消息体主要是为配合固定/可变头部命令而存在,(比如CONNECT可变头部User name标记若为1则需要在payload(消息体/负载 )中附加用户名称字符串。
CONNECT/SUBSCRIBE/SUBACK/PUBLISH等消息有消息体。PUBLISH的消息体以二进制形式对待。
MQTT协议只允许在PUBLISH类型消息体中使用自定义特性,不可以在固定/可变头部想加入自定义私有特性,这也是为了协议免于流于形式,变得很分裂也为了兼顾现有客户端等。比如支持压缩等,那就可以在Playload中定义数据支持,在应用中进行读取处理。

Message identifiers

Message identifiers(消息标识符/消息ID)存在以下的MQTT消息的可变头部中:PUBLISH, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK。并且要求Fixed header (固定头部)中的QoS level标志值为1或2时。
Message identifiers是一个16位无符号位的short类型值(值不能为 0,0做保留作为无效的消息ID),仅仅要求在一个特定方向(服务器发往客户端为一个方向,客户端发送到服务器端为另一个方向)的通信消息中必须唯一。比如客户端发往服务器,有可能存在服务器发往客户端会同时存在重复,但不碍事。
Message identifiers占用两个字节,需要指出的是先MSB然后LSB,也就是网络字节顺序(也称大端顺序)。
message identifiers

MQTT and UTF-8

utf - 8是一个高效的编码优化的支持ASCII字符编码文本通信的Unicode字符串。
在MQTT中,字符串以两个字节为前缀,表示长度,如下表所示。
UTF-8
字符串长度是编码字符串字符的字节数,而不是字符的数量。例如,utf - 8编码的字符串”OTWP”如下表所示,头两个字节为一个完整的无符号数字,代表字符串字节长度,后面四个字节才是字符串真正的长度,共六个字节
MQTT自学笔记(二)—Message format_第4张图片

MQTT无论是可变头部还是消息体中,只要是字符串部分,都是采用了修改版的UTF-8编码。

总结

总之,掌握固定头部的QoS level、RETAIN标记、可变头部的Connect flags作用和意义,对彻底理解MQTT很有帮助。

你可能感兴趣的:(技术知识库,网络通信,MQTT)