转战物联网·基础篇07-深入理解MQTT协议之控制报文(数据包)格式
在MQTT协议中,一个控制报文(数据包)的结构按照前后顺序分如下三部分:
| 结构名 | 中文名 | 解释说明 |
|---|---|---|
| Fixed header | 固定报头 | 报文的最开始部分,所有报文都包含这个部分 |
| Variable header | 可变报头 | 固定报文的附加部分,有些报文没有这个部分 |
| Payload | 有效载荷 | 需要携带的信息内容,有些报文没有这个部分 |
下图是MQTT控制报文(数据包)格式的结构示意图:

1、固定报头(Fixed header):
固定报头存在于所有MQTT数据包中,表示数据包类型及控制类标志等。固定报头由至少2个字节组成,格式如下:
| Bit(位号) | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| 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 | 不需要 |
本节完,待续......
转战物联网·基础篇07-深入理解MQTT协议之控制报文(数据包)格式的更多相关文章
- 转战物联网·基础篇05-通俗理解MQTT协议的实现原理和异步方式
网络上搜索MQTT协议,会出现太多的解释,这里就不做官方标准释义的复制了.这一节我们从实战理解角度,通俗的将MQTT协议的作用及实现原理说一下,旨在可以快速理解MQTT协议.所以可能会出现很多看似 ...
- 转战物联网·基础篇06-深入理解MQTT协议之基本术语
通过上一节我们对MQTT协议已经有了初步的印象,这一节我们开始深入的理解一下MQTT协议,介绍常用的MQTT 3.1.1版本,5.0版本后面指介绍新增部分即可.这一节我们先介绍MQTT里常用的术语 ...
- 转战物联网·基础篇08-例说MQTT协议各控制报文
前面讨论了MQTT协议的控制报文的格式,下面分别举例探讨各个控制报文的详细内容. 01.CONNECT – 连接服务端 客户端到服务端的网络连接建立后,客户端发送给服务端的第一个报文必须是CO ...
- 转战物联网·基础篇09-选择MQTT协议还是CoAP协议
前面章节介绍过,MQTT协议和CoAP协议都是物联网中比较流行的协议,都对传输量做了很大的精简,传输开销小,以适应物理网的网络环境. XMPP协议也有人说是适合物联网通信的,但它是基于XML, ...
- 转战物联网·基础篇03-从JSON数据到短指令谈思维的转变
了解了物联网项目的大体结构之后,我们先从物联网的联网相关部分说起,这也是物联网项目中的关键环节.在联网环节中,不仅要考虑如何连接上,还要考虑连接后如何传输数据.换句话说数据是以什么格式进行传输,对 ...
- iOS系列 基础篇 07 Action动作和输出口
iOS系列 基础篇 07 Action动作和输出口 目录: 1. 前言及案例说明 2. 什么是动作? 3. 什么是输出口? 4. 实战 5. 结尾 1. 前言及案例说明 上篇内容我们学习了标签和按钮 ...
- 网络基础之网络协议篇---CS架构--网络通信--osi 协议---套接字socket--粘包
1 C\S 客户端/服务器架构: .硬件 C/S架构 (打印机) .软件 C/S 架构 互联网中处处是C/S架构 如黄色网站是服务端,你的浏览器是客户端(B/S架构也是C/S架构的一种) 腾讯作为服务 ...
- 物联网防火墙himqtt源码之MQTT协议分析
物联网防火墙himqtt源码之MQTT协议分析 himqtt是首款完整源码的高性能MQTT物联网防火墙 - MQTT Application FireWall,C语言编写,采用epoll模式支持数十万 ...
- Java多线程系列--“基础篇”07之 线程休眠
概要 本章,会对Thread中sleep()方法进行介绍.涉及到的内容包括:1. sleep()介绍2. sleep()示例3. sleep() 与 wait()的比较 转载请注明出处:http:// ...
随机推荐
- monkey命令解析详解
我面试时遇到过几次让背个monkey命令的,可以这样简单说一个:adb shell monkey -p(约束包名) -s 200 -v -v --throttle 300 1500000 > ...
- 61-如何使用 Weave 网络?
weave 是 Weaveworks 开发的容器网络解决方案.weave 创建的虚拟网络可以将部署在多个主机上的容器连接起来.对容器来说,weave 就像一个巨大的以太网交换机,所有容器都被接入这个交 ...
- 阿里巴巴供应链平台事业部2020届秋招-Java工程师
阿里巴巴供应链平台事业部,2020届秋季校园招聘开始啦!Java开发工程师虚位以待,机会难得,占坑抓紧. 入职就发师兄,一对一师兄辅导. 在这里,你将有机会接触阿里集团的所有数据库.中间件等基础设施. ...
- spring security 原理+实战
疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列 [博客园总入口 ] 架构师成长+面试必备之 高并发基础书籍 [Netty Zookeeper Redis 高并发实战 ] 前言 Crazy ...
- SpringCloudConfig对称加密 yml配置文件while parsing a block mapping
错误的配置!!! #-----------------db------------------ mybatis: type-aliases-package: com.book.product.pojo ...
- Zabbix理论知识
Zabbix 什么是Zabbix? Zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案. Zabbix作用 Zabbix能够监视各种服务器的健康性.网络的稳定性 ...
- PWA 学习笔记(五)
离线与缓存 资源请求的拦截代理: 1.资源请求的判断: (1)fetch 事件会拦截页面上所有的网络资源请求,但我们通常只对部分资源请求进行处理, 其余的请求会继续走浏览器默认的资源请求流程 (2)f ...
- Oracle对时间的相关操作
目录导航: 1. 年操作 2. 月操作 3. 周操作 4. 天操作 5. 时操作 6. 分操作 7. 秒操作 1.年操作 SELECT add_months(SYSDATE, -12) FROM du ...
- Python语法速查: 16. 时间日期处理
返回目录 (1)datetime模块 datetime模块可以处理时间和日期,其中包含以下类:date类.time对象.datetime对象.timedelt对象.tzinfo对象. ● date类 ...
- 阿里云ubuntu16.04搭建pptpd
一.搭建pptp vpn 需开放1723端口和gre协议 1.阿里云有个安全组需要开放端口才能访问,需添加新的安全组规则. 登陆阿里云服务器管理控制台,添加安全组规则 入/出方向都填写 端口范围为17 ...