MQTT协议结构
前言
最近在了解MQTT协议相关的内容,内容有点多,特此把MQTT协议,以及其从服务端到客户端的流程整理出来,记录如下。
一,MQTT协议介绍
1.1 术语 Terminology
本规范中用到的关键字 必须 MUST,不能 MUST NOT,要求 REQUIRED,将会 SHALL,不会 SHALL NOT,应该 SHOULD,不应该 SHOULD NOT,推荐 RECOMMENDED,可以 MAY,可选 OPTIONAL 都是按照 IETF RFC 2119 [RFC2119] 中的描述解释。
网络连接 Network Connection
MQTT使用的底层传输协议基础设施。
- 客户端使用它连接服务端。
- 它提供有序的、可靠的、双向字节流传输
1.2MQTT协议介绍
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,它是一种轻量级的、基于代理的“发布/订阅”模式的消息传输协议。其具有协议简洁、小巧、可扩展性强、省流量、省电等优点,而且已经有PHP,JAVA,Python,C,C#,Go等多个语言版本,基本可以使用在任何平台上,几乎可以把所有联网物品和外部连接起来,所以特别适合用来当做物联网的通信协议,实用的场景有遥感数据、汽车、智能家居、智慧城市、医疗医护等等。
MQTT特点
MQTT协议是为*大量计算能力有限*,且工作在*低带宽*、***不可靠的网络***的远程传感器和控制***设备通讯***而设计的协议,它具有以下主要的几项特性:
1、使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合;
2、对负载内容屏蔽的消息传输;
3、使用 TCP/IP 提供网络连接;
4、有三种消息发布服务质量:
“至多一次”,消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
“至少一次”,确保消息到达,但消息重复可能会发生。
“只有一次”,确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果。
5、小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量;
6、使用 Last Will (遗嘱)和 Testament 特性通知有关各方客户端异常中断的机制;
1.消息模型
MQTT是一种基于代理的发布/订阅的消息协议。与请求/回答这种同步模式不同,发布/订阅模式解耦了发布消息的客户(发布者)与订阅消息的客户(订阅者)之间的关系,这意味着发布者和订阅者之间并不需要直接建立联系。一个发布者可以对应多个订阅者,当发布者发生变化的时候,他可以将消息一一通知给所有的订阅者。这种模式提供了更大的网络扩展性和更动态的网络拓扑
个人理解为一个对象可以有多个订阅者,只负责推送消息,接受对象凭借是否订阅为接受条件
此外运用MQTT协议,设备可以很方便地连接到物联网云服务,管理设备并处理数据,最后应用到各种业务场景,如下图所示
2.服务质量
MQTT提供三种质量的服务:
至多一次(qos = 0),可能会出现丢包的现象。使用在对实时性要求不高的情况。这一级别可应用于如下情景,如环境传感器数据,丢失一次读记录无所谓,因为很快下一次读记录就会产生。
至少一次(qos = 1),保证包会到达目的地,但是可能出现重包。
正好一次(qos = 2),保证包会到达目的地,且不会出现重包的现象。这一级别可用于如计费系统等场景,在计费系统中,消息丢失或重复可能会导致生成错误的费用。
3.主题与通配符
主题名称(Topic name)用来标识已发布消息的信息的渠道。订阅者用它来确定接收到所关心的信息。它是一个分层的结构,用斜线“/”作为分隔符(这个有点类似于restful风格)。主题还可以通过通配符进行过滤。其中,+可以过滤一个层级,而#只能出现在主题最后表示过滤任意级别的层级。值得注意的是***MQTT允许使用通配符订阅主题,但是并不允许使用通配符广播***。
举个例子:
building-b/floor-5:代表B楼5层的设备。
+/floor-5:代表任何一个楼的5层的设备。
building-b/#:代表B楼所有的设备。
4.遗嘱
当一个客户端断开连接的时候,它希望客户端可以发送它指定的消息。该消息和普通消息的结构相同。通过设置该位并填入和信息相关的内容即可(后面会有介绍)。
5.消息类型
消息类型 | 类型 | 编码说明 |
---|---|---|
reserved | 0 | 保留 |
connect | 1 | 客户端到服务端的连接请求 |
connACK | 2 | 服务端对连接请求的响应 |
publish | 3 | 发布消息 |
puback | 4 | 新发布消息确认,是QoS 1给PUBLISH消息的回复 |
pubRec | 5 | QoS 2消息流的第一部分,表示消息发布已记录 |
pubRel | 6 | QoS 2消息流的第二部分,表示消息发布已释放 |
pubComp | 7 | QoS 2消息流的第三部分,表示消息发布完 |
subscribe | 8 | 客户端订阅某个主题 |
subBack | 9 | 对于SUBSCRIBE消息的确认 |
unsubscribe | 10 | 客户端终止订阅的消息 |
unsubBack | 11 | 对于UNSUBSCRIBE消息的确认 |
pingReq | 12 | 心跳 |
pingResp | 13 | 确认心跳 |
disconnect | 14 | 客户端终止连接前通知MQTT代理 |
reserved | 15 | 保留 |
二,MQTT控制报文格式 MQTT Control Packet format
2.1 MQTT控制报文的结构 Structure of an MQTT Control Packet
MQTT协议通过交换预定义的MQTT控制报文来通信。这一节描述这些报文的格式。
MQTT控制报文由三部分组成,按照 图例 2.1 –MQTT控制报文的结构 描述的顺序:
图例 2.1 –MQTT控制报文的结构
Fixed header | 固定报头,所有控制报文都包含 |
---|---|
Variable header | 可变报头,部分控制报文包含 |
Payload | 有效载荷,部分控制报文包含 |
2.2 固定报头 Fixed header
每个MQTT控制报文都包含一个固定报头。图例 2.2 -固定报头的格式 描述了固定报头的格式。
图例 2.2 -固定报头的格式
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
byte 1 | MQTT控制报文的类型 | 用于指定控制报文类型的标志位 | ||||||
byte 2… | 剩余长度 |
2.2.1 MQTT控制报文的类型 MQTT Control Packet type
**位置:**第1个字节,二进制位7-4。
表示为4位无符号值,这些值的定义见 表格 2.1 -控制报文的类型
表格 2.1 -控制报文的类型
名字 | 值 | 报文流动方向 | 描述 |
---|---|---|---|
Reserved | 0 | 禁止 | 保留 |
CONNECT | 1 | 客户端到服务端 | 客户端请求连接服务端 |
CONNACK | 2 | 服务端到客户端 | 连接报文确认 |
PUBLISH | 3 | 两个方向都允许 | 发布消息 |
PUBACK | 4 | 两个方向都允许 | QoS 1消息发布收到确认 |
PUBREC | 5 | 两个方向都允许 | 发布收到(保证交付第一步) |
PUBREL | 6 | 两个方向都允许 | 发布释放(保证交付第二步) |
PUBCOMP | 7 | 两个方向都允许 | QoS 2消息发布完成(保证交互第三步) |
SUBSCRIBE | 8 | 客户端到服务端 | 客户端订阅请求 |
SUBACK | 9 | 服务端到客户端 | 订阅请求报文确认 |
UNSUBSCRIBE | 10 | 客户端到服务端 | 客户端取消订阅请求 |
UNSUBACK | 11 | 服务端到客户端 | 取消订阅报文确认 |
PINGREQ | 12 | 客户端到服务端 | 心跳请求 |
PINGRESP | 13 | 服务端到客户端 | 心跳响应 |
DISCONNECT | 14 | 客户端到服务端 | 客户端断开连接 |
Reserved | 15 | 禁止 | 保留 |
2.2.2 标志 Flags
保证消息可靠传输,默认为0,只占用一个字节,表示第一次发送。不能用于检测消息重复发送等。只适用于客户端或服务器端尝试重发***PUBLISH***(发送消息), *PUBREL*(QoS 2消息流的第二部分,表示消息发布已释放), *SUBSCRIBE*(客户端订阅某个主题) 或 UNSUBSCRIBE(客户端终止订阅的消息)消息,注意需要满足以下条件:当 QoS > 0时消息需要回复确认,此时,在可变头部需要包含消息ID。当值为1时,表示当前消息先前已经被传送过。
QoS(Quality of Service,服务质量)
使用两个二进制表示PUBLISH类型消息:
RETAIN(保持)
仅针对PUBLISH消息。不同值,不同含义:
0:仅仅为当前订阅者推送此消息。
1:表示发送的消息需要一直持久保存(不受服务器重启影响),不但要发送给当前的订阅者,并且以后新来的订阅了此Topic name的订阅者会马上得到推送。
另外对于新加入的订阅者,只会取出最新的一个RETAIN flag = 1的消息推送。
Remaining Length(剩余长度)
在当前消息中剩余的byte(字节)数,包含可变头部和负荷(称之为内容/body,更为合适)。单个字节最大值:01111111,16进制:0x7F,10进制为127。单个字节为什么不能是11111111(0xFF)呢?因为MQTT协议规定,第八位(最高位)若为1,则表示还有后续字节存在。
同时MQTT协议最多允许4个字节表示剩余长度。那么最大长度为:0xFF,0xFF,0xFF,0x7F。
**二进制表示为:**11111111,11111111,11111111,01111111
**十进制:**268435455 byte=261120KB=256MB=0.25GB
四个字节之间值的范围:
2.2.3 剩余长度 Remaining Length
**位置:**从第2个字节开始。
剩余长度(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。
表格 2.4剩余长度字段的大小展示了剩余长度字段所表示的值随字节增长。
表格 2.4剩余长度字段的大小 Size of Remaining Length field
字节数 | 最小值 | 最大值 |
---|---|---|
1 | 0 (0x00) | 127 (0x7F) |
2 | 128 (0x80, 0x01) | 16 383 (0xFF, 0x7F) |
3 | 16 384 (0x80, 0x80, 0x01) | 2 097 151 (0xFF, 0xFF, 0x7F) |
4 | 2 097 152 (0x80, 0x80, 0x80, 0x01) | 268 435 455 (0xFF, 0xFF, 0xFF, 0x7F) |
分别表示(每个字节的低7位用于编码数据,最高位是标志位):
- 1个字节时,从0(0x00)到127(0x7f)
- 2个字节时,从128(0x80,0x01)到16383(0Xff,0x7f)
- 3个字节时,从16384(0x80,0x80,0x01)到2097151(0xFF,0xFF,0x7F)
- 4个字节时,从2097152(0x80,0x80,0x80,0x01)到268435455(0xFF,0xFF,0xFF,0x7F)
非规范评注
非负整数X使用变长编码方案的算法如下:
```do encoded