在MQTT协议中,一个控制报文(数据包)的结构按照前后顺序分如下三部分:

结构名

中文名

解释说明

Fixed header

固定报头

报文的最开始部分,所有报文都包含这个部分

Variable header

可变报头

固定报文的附加部分,有些报文没有这个部分

Payload

有效载荷

需要携带的信息内容,有些报文没有这个部分

下图是MQTT控制报文(数据包)格式的结构示意图:

1、固定报头(Fixed header):

固定报头存在于所有MQTT数据包中,表示数据包类型及控制类标志等。固定报头由至少2个字节组成,格式如下:

Bit(位号)76543210

Byte1(第一个字节)

组合代表MQTT控制报文(数据包)的类型

控制报文的标志位(Flags),可理解为一种属性参数

Byte2(第二个字节起)

剩余长度,当前报文剩余部分的字节数,包括可变报头和有效负载

1.1、控制报文类型(Control Packet type):

固定报头第一个字节的高四位(7-4号位)是代表控制报文的类型,也就是这个数据包是做什么用的。是用7-4号位的二进制(也就是1111--0000之间)组合值,来代表具体的含义,见下表:

7-4号位

十进制值

报文类型

报文允许发起方向

报文描述

0000

0

Reserved

禁止

保留,不可用

0001

1

CONNECT

客户端―→服务端

客户端请求连接到服务端的代理服务

0010

2

CONNACK

客户端←―服务端

连接请求的回复确认报文

0011

3

PUBLISH

客户端←→服务端

发布主题消息

0100

4

PUBACK

客户端←→服务端

发布确认,是QoS=1时,对 PUBLISH 的响应确认

0101

5

PUBREC

客户端←→服务端

发布收到,是QoS=2时,对 PUBLISH 的响应确认,是QoS=2实现的第一步

0110

6

PUBREL

客户端←→服务端

发布释放,是QoS=2时,对 PUBREC 的响应确认,是QoS=2实现的第二步

0111

7

PUBCOMP

客户端←→服务端

发布完成,是QoS=2时,对 PUBREL 的响应确认,是QoS=2实现的第三步

1000

8

SUBSCRIBE

客户端―→服务端

客户端订阅主题,可一次订阅一个或多个主题(使用通配符)

1001

9

SUBACK

客户端←―服务端

订阅完成确认,是对 SUBSCRIBE 的响应确认

1010

10

UNSUBSCRIBE

客户端―→服务端

取消订阅,客户端发起的取消对某个主题的订阅

1011

11

UNSUBACK

客户端←―服务端

取消订阅确认,是对 UNSUBSCRIBE 的响应确认

1100

12

PINGREQ

客户端―→服务端

心跳,表示这个数据包是为通知服务端客户端还在正常连接着

1101

13

PINGRESP

客户端←―服务端

心跳响应,表示服务端已经成功收到了客户端的心跳

1110

14

DISCONNECT

客户端―→服务端

断开连接,客户端通知服务端,需要断开当前网络连接

1111

15

Reserved

禁止

保留,不可用

1.2、标志(Flags):

固定报头第1个字节的低4位 (3-0号位)包含每个MQTT控制报文类型特定的标志,必须与控制报文类型配套对应使用,否则服务端代理服务会拒绝服务或断开连接。具体的见下表(保留的标志必须按照表中的值设置):

报文类型

标志类型

Bit3

Bit2

Bit1

Bit0

CONNECT

保留

0

0

0

0

CONNACK

保留

0

0

0

0

PUBLISH

使用

是否为重复发

服务质量高位

服务质量低位

是否保存消息

PUBACK

保留

0

0

0

0

PUBREC

保留

0

0

0

0

PUBREL

保留

0

0

1

0

PUBCOMP

保留

0

0

0

0

SUBSCRIBE

保留

0

0

1

0

SUBACK

保留

0

0

0

0

UNSUBSCRIBE

保留

0

0

1

0

UNSUBACK

保留

0

0

0

0

PINGREQ

保留

0

0

0

0

PINGRESP

保留

0

0

0

0

DISCONNECT

保留

0

0

0

0

注:关于用“是否”描述的实际就是布尔类型,0表示否,1表示是;

1.3、第一字节各类型报文具体值:

固定报头报文类型高4位和标志位的低4位综合起来,最终第一个字节是有一个具体值的。为了更好的理解第一个字节的具体值是怎样得出来的,在下表列出了不同类型的报文及某个报文不同标志时的具体值:

报文类型

标志作用

二进制值

10进制值

16进制值

CONNECT

连接服务端

00010000

16

0x10

CONNACK

连接成功确认

00100000

32

0x20

PUBLISH

新发布等级0不保存

00110000

48

0x30

PUBLISH

新发布等级0需保存

00110001

49

0x31

PUBLISH

新发布等级1不保存

00110010

50

0x32

PUBLISH

新发布等级1需保存

00110011

51

0x33

PUBLISH

新发布等级2不保存

00110100

52

0x34

PUBLISH

新发布等级2需保存

00110001

53

0x35

PUBLISH

重发等级2不保存

00111000

56

0x38

PUBLISH

重发等级2需保存

00111001

57

0x39

PUBACK

等级1发布成功

01000000

64

0x40

PUBREC

等级2发布收到

01010000

80

0x50

PUBREL

等级2发布释放

01100010

98

0x62

PUBCOMP

等级2发布完成

01110000

112

0x70

SUBSCRIBE

订阅主题

10000010

130

0x82

SUBACK

订阅完成确认

10010000

144

0x90

UNSUBSCRIBE

取消订阅

10100010

162

0xA2

UNSUBACK

取消完成确认

10110000

176

0xB0

PINGREQ

心跳包

11000000

192

0xC0

PINGRESP

心跳回复

11010000

208

0xD0

DISCONNECT

断开网络连接

11100000

224

0xE0

注:关于发布主题还有其他情况这里就没有全部列出,根据表中的规律就可以计算出实际的值了。

1.3、剩余长度(Remaining Length):

剩余长度是从第二个字节开始,最多允许占用四个字节。描述本次传送的应用消息在剩余长度字节之后(不包括剩余长度字节本身)还有多少个字节,包括可变报头(有的报文没有这部分) + 有效载荷(有的报文没有这部分)的所有字节数量。

根据上面描述,剩余长度属于变长的编码规则,也就是它可能是1-4个字节中的任何一种情况,那么怎样知道当前这个报文的剩余长度是占用了几个字节的呢?如果不能确定,那么接收方就无法正确解析数据了。所以MQTT协议规定剩余长度的每个字节的最高位(也就是7号位)作为是否还有下一个字节剩余长度的标志位,不做长度数值的表述位。这样每给剩余长度字节最大代表长度值就是127(二进制 1111111 的值)了,因为只有7个位表示长度了。向后每增加一个字节都代表前一个字节满值再加1的倍数,四个字节的剩余长度代表的长度值最大可为268435455。

如果剩余长度值不大于127,则只用一个字节表示,例如121,则剩余长度字节的二进制是01111001,含义见下表:

7号位

6-0号位

0

1111001

接下来没有剩余长度字节了

剩余长度是:121

如果剩余长度值大于127小于16384,则需用两个字节表示,例如15971,则剩余长度两字节具体值则是0xE3 0x7C(11100011 01111100),含义见下表:

1字节7号位

1字节6-0号位

2字节7号位

2字节6-0号位

1

1100011

0

1111100

后面还有字节描述长度

本子节描述长度:99

后面没有长度字节了

本字节描述长度:124 * 128 = 15872

两个字节代表的长度值相加 99 + 15872 = 15971,这既是完整的剩余长度值了。后面这个字节每增加1,则代表剩余长度值增加128。也就是前面字节的低7位值满都为1(127)再加1,就到后面字节加1,前面字节低7位归0。再加满再到后面字节加1,以此类推。所以两个字节可以表述的最大值是(11111111 01111111)127+(127*128) = 16383。

??由于使用了两个字节表述剩余长度,那么前面的字节的最高位7号位就要置1,以告诉解析程序后面的字节还要按照剩余长度来计算。

如果剩余长度值大于16383小于2097152,则需用三个字节表示,例如2097150,则剩余长度三字节具体值则是0xFE 0xFF 0x7F(11111110 11111111 01111111),含义见下表:

1字节7号位

1字节6-0号位

2字节7号位

2字节6-0号位

3字节7号位

3字节6-0号位

1

1111110

1

1111111

0

1111111

还有长度字节

长度:126

还有长度字节

长度:127 * 128 = 16256

长度最后字节

长度:127 * 16384 = 2080768

三个字节代表的长度值相加 126 + 16256 + 2080768 = 2097150,这既是完整的剩余长度值了。3字节每增加1,则代表剩余长度值增加16384,即前两个字节满值再加1。四字节的原理也是这样向后推导,这里就不再列举了。

剩余长度使用1-4个字节可以描述的长度范围见下表:

字节数

最小值10/16进制

最小值2进制

最大值10/16进制

最大值2进制

1

0(0x00)

00000000

127(0x7F)

01111111

2

128(0x80,0x01)

10000000 00000001

16383(0xFF,0x7F)

11111111 01111111

3

16384(0x80,0x80,0x01)

10000000 10000000 00000001

2097151(0xFF,0xFF,0x7F)

11111111 11111111 01111111

4

2097152(0x80,0x80,0x80,0x01)

10000000 10000000 10000000 00000001

268435455(0xFF,0xFF,0xFF,0x7F)

11111111 11111111 11111111 01111111

2、可变报头(Variable header):

可变报头在固定报头与有效负载之间,不是所有的报文都有可变报头。报文类型不同可变报头的内容也不同。后面会对各报文的可变报头逐一讨论。某些类型的报文中的可变报头还包含报文标识符(Packet Identifier)字段。

2.1、报文标识符(Packet Identifier):

报文标识符,一定程度上相当于是每个报文的唯一ID,用于识别报文身份的。重复发送报文时,必须使用相同的报文标识符。在需要应答的控制报文里,标识符可以区分是应答的哪个报文。某些控制报文的可变报头部分包含一个两字节的报文标识符字段。这些报文分别是PUBLISH(QoS > 0时), PUBACK,PUBREC,PUBREL,PUBCOMP,SUBSCRIBE, SUBACK,UNSUBSCRIBE,UNSUBACK。

??需要使用标识符的报文,发送方在每次发送一个新的报文时,必须分配一个没有使用过的报文标识符。报文标识符固定使用两个字节,按照双字节读值可用范围是0-65535(00000000 00000000 -- 11111111 11111111)。

3、有效载荷(Payload):

在一些需要携带用户自定义的应用消息的MQTT控制报文中,会将这些信息放在报文的最后部分,称之为有效载荷。对于PUBLISH来说有效载荷就是应用消息。不同的控制报文有效载荷内容不同,后面会在分别介绍控制报文时具体讨论。下表列出哪些控制报文有包含有效载荷:

控制报文

有效载荷

CONNECT

需要

CONNACK

不 需要

PUBLISH

可选,可以零长度

PUBACK

不需要

PUBREC

不需要

PUBREL

不需要

PUBCOMP

不需要

SUBSCRIBE

需要

SUBACK

需要

UNSUBSCRIBE

需要

UNSUBACK

不需要

PINGREQ

不需要

PINGRESP

不需要

DISCONNECT

不需要

本节完,待续......

mqtt 变为乱码 接受16进制字节流_转战物联网#183;基础篇07-深入理解MQTT协议之控制报文(数据包)格式...相关推荐

  1. C#16进制数转为10进制有符号数——处理传感器16进制字节流原始数据

    原数据是四个字节组成例如 B102 一共16bit,要转化成有符号数,则最大是2^15,范围是(-32768,32768). 转换公式: 1.先转化为10进制trans int xx= Convert ...

  2. python ascii码16进制转换_如何在Python十六进制整数和ASCii编码的字符串之间进行转换...

    今天,我将与您分享一种将Python十六进制整数转换为ASCii编码的字符串的方法,该方法具有很好的参考价值,希望对大家有所帮助. 让我们一起关注小编,看看 当使用Pyserial与STM32通信时, ...

  3. pythonprint字节按照16进制输出_对python以16进制打印字节数组的方法详解

    对python以16进制打印字节数组的方法详解 一.问题描述 如果直接用print打印bytes的话,有时候会直接显示ascii对应的字符,看起来很蛋疼. 二.运行效果 上面一行是直接用print打印 ...

  4. java int 16进制字符串_关于将java int类型转换为16进制字符串的问题

    package p5.com.byd.mes.dev.tool.baseToChar; /** * 任意进制转换器 * @author solar */ public class Decimal { ...

  5. android 16进制转字符串乱码,HEX(16进制)转ascii 乱码

    满意答案 下表列出了字符集中的 0 - 127. 代码 字符 代码 字符 代码 字符 代码 字符 0 32 [空格] 64 @ 96 ` 1 33 ! 65 A 97 a 2 34 " 66 ...

  6. mysql 16进制 绕过_注入绕过技巧

    1.绕过空格(注释符/* */,%a0): 两个空格代替一个空格,用Tab代替空格,%a0=空格: %20%09%0a %0b %0c %0d %a0/**/ 最基本的绕过方法,用注释替换空格: /* ...

  7. python 16进制加法_在python中追加2个十六进制值

    我试图在python中附加一些十六进制值,我似乎总是在数字之间得到0x.从我搜索的内容来看,如果不将其转换为点燃的值,这是不可能的?我不确定. a = 0x7b b = 0x80000 hex(a) ...

  8. java 打印16进制数组_如何在Java中将字节数组转换为十六进制字符串?

    从这里的讨论,特别是这个答案,这是我目前使用的功能:private final static char[] hexArray = "0123456789ABCDEF".toChar ...

  9. 16进制-2进制-10进制转换包(终端交互专用)

    因业务需要,与终端机器进行交互.特此写了个2进制10进制16进制字符串之间的转换包. package com.tools;import nettyServer.nServerAccept; impor ...

最新文章

  1. sql and和in能连用吗_解析法律英语中【同义词连用】现象
  2. 就地电子数据展示与保留
  3. 在Indicator中添加动态Checkbox,无需绑定数据源,支持全选 - Ehlib学习(二)
  4. easyui 插入中间行
  5. 生活不是拍电影,不会在关键时刻有个人来救你。
  6. Qt文档阅读笔记-QHostInfo官方解析与实例(根据Host获取IP)
  7. jquery时间倒计时
  8. PKUSC2019划水记
  9. Pycharm主题颜色设置
  10. 大功率UWB模块 XZM3000 移植手册 《从DWM1000到XZM3000移植手册》
  11. django下载文件异常 - ValueError: read of closed file
  12. 在虚拟机的ubuntu 中配置 tftp 服务器(2021-4-14)
  13. 爱普生Epson L301 清零软件+图解教程
  14. iOS打包失败的可能情况
  15. 电脑插入了U盘却读取不出来的方法
  16. Python日记 -- 百度OCR翻译
  17. 计算机网络中常用的互联设备,计算机网络的互联技术
  18. 易飞9安装和授权视频
  19. kcl方程独立性的图论证明
  20. Dynamics 365 On-premises V9的UCI界面下如何显示Audit History

热门文章

  1. 实数系的基本定理_七大实数理论与互推
  2. c语言zip 库,c语言调用libzip库遍历zip文件
  3. JavaEE 面试题总结
  4. 10款最佳跨浏览器测试工具,建议收藏
  5. 聊聊华为,不得不说华为确实很牛逼
  6. 英雄传说空之轨迹人物介绍
  7. 自动驾驶传感器产业链
  8. 高德开放平台 - 学习/实践
  9. 30岁买房只需记住一句话,永远不会出错!
  10. elasticsearch 官方文档一个小错误