文章目录
- 系列文章目录
- 前言
-
- 一、MQTT控制包格式
- 二、固定包头
-
- 控制包类型
- 控制包类型标识
- 剩余长度
- 三、可变包头
-
- 数据包标识
- 四、载荷
系列文章目录
MQTT协议详解 一、MQTT简介
MQTT协议详解 二、MQTT控制包格式
前言
本章接详细介绍MQTT的控制包数据组成。一、MQTT控制包格式
MQTT控制包由一下三个部分组成
顺序 | 名称 | 描述 |
---|---|---|
1 | 固定包头 | 所有MQTT包中 |
2 | 可变包头 | 某些MQTT包中 |
3 | 载荷 | 某些MQTT包中 |
二、固定包头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte1 | 控制包类型 | 控制包类型标识 | ||||||
byte2 | 剩余长度 |
控制包类型
位置:第一个字节
名称 | 值 | 传输方向 | 描述 |
---|---|---|---|
Reserved | 0 | 保留 | |
CONNECT | 1 | 客户端 --> 服务端 | 客户端连接请求 |
CONNACK | 2 | 服务端 --> 客户端 | 服务器响应客户端连接请求 |
PUBLISH | 3 | 客户端 --> 服务端 OR 服务端 --> 客户端 | 发布消息 |
PUBACK | 4 | 客户端 --> 服务端 OR 服务端 --> 客户端 | 发布消息响应 |
PUBREC | 5 | 客户端 --> 服务端 OR 服务端 --> 客户端 | 发布消息到达 (交付第一步 ) |
PUBREL | 6 | 客户端 --> 服务端 OR 服务端 --> 客户端 | 发布消息释放 (交付第二步) |
PUBCOMP | 7 | 客户端 --> 服务端 OR 服务端 --> 客户端 | 发布消息完成 (交付第三步) |
SUBSCRIBE | 8 | 客户端 --> 服务端 | 客户端订阅请求 |
SUBACK | 9 | 服务端 --> 客户端 | 服务器响应客户端订阅请求 |
UNSUBSCRIBE | 10 | 客户端 --> 服务端 | 取消订阅请求 |
UNSUBACK | 11 | 服务端 --> 客户端 | 响应取消订阅请求 |
PINGREQ | 12 | 客户端 --> 服务端 | 心跳请求 |
PINGRESP | 13 | 服务端 --> 客户端 | 心跳响应 |
DISCONNECT | 14 | 客户端 --> 服务端 | 客户端断开连接 |
Reserved | 15 | 保留 |
控制包类型标识
控制包类型 | 固定标志 | bit3 | bit2 | bit1 | bit0 |
---|---|---|---|---|---|
CONNECT | 保留 | 0 | 0 | 0 | 0 |
CONNACK | 保留 | 0 | 0 | 0 | 0 |
PUBLISH | 根据实际传输情况设置 | DUP | QoS | QoS | RETAIN |
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 |
DUP = 重复发送PUBLISH包
QoS = PUBLISH包消息质量
RETAIN = PUBLISH 保留标识
剩余长度
位置:第二个字节
剩余长度是指当前包中的剩余字节,包括可变包头的数据以及载荷。剩余长度不包含用来编码剩余长度的字节。
剩余长度怎么计算呢?说实话,我看了官方的描述很懵逼。但是看代码后就很清晰了。
占用字节 | 起始数值 | 结束数值 |
---|---|---|
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) |
下面是编码部分代码,可以直接放在—》C语言在线编辑器上试一下
#include <stdio.h>
int main(void) {
int encodedByte = 0;
int X = 2097152;//想要进行编码的长度
int index = 1;
do{
encodedByte = X % 128;
X = X / 128;
if ( X > 0 )
encodedByte = encodedByte | 128;
printf("编码第%d字节=%X(HEX)\n",index++,encodedByte);
}while ( X > 0 );
return 0;
}
接下来是解码部分代码,也可以在—》C语言在线编辑器上试一下
#include <stdio.h>
int main(void) {
int multiplier = 1;
int buff[4] = { 0x80,0x80,0x80,0x01};
int value = 0;
int temp = 0;
int index = 0;
do{
temp = buff[index++];//index先运算,再累加
value += ( temp & 127) * multiplier;
multiplier *= 128;
}while((temp & 128) != 0 );
printf("MQTT控制包长度%d字节",value);
return 0;
}
三、可变包头
某些类型的MQTT控制包包含一个可变包头结构。位于固定包头和载荷之间。可变包头的内容取决于包的类型。可变包头中的包标识符字段在大多类型的包中比较常见。
数据包标识
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte1 | 包标识最高有效位 | |||||||
byte2 | 包标识最低有效位 |
许多控制数据包类型的可变报头组件都包含一个2字节的数据包标识符字段。这些控制数据包是PUBLISH(QoS> 0),PUBACK,PUBREC,PUBREL,PUBCOMP,SUBSCRIBE,SUBACK,UNSUBSCRIBE和UNSUBACK。
SUBSCRIBE,UNSUBSCRIBE和PUBLISH(在QoS> 0的情况下)控制包必须包含一个非零的16位包标识符 。每当客户端发送一个新的数据包(以上四种类型)时,客户端必须给该数据包分配一个当前未使用的数据包标识符。如果客户端重新发送一个特定的控制包,则它必须在该包的后续重新发送中使用相同的包标识符。在客户端处理了相应的确认数据包后,数据包标识符就可以重用。在QoS 1 PUBLISH的情况下,这是对应的PUBACK;在QoS 2的情况下,它是PUBCOMP。对于SUBSCRIBE或UNSUBSCRIBE,它是对应的SUBACK或UNSUBACK 。 当服务器发送QoS> 0 的PUBLISH时,同样的条件也适用于服务器。
如果其QoS值设置为0,则PUBLISH数据包不得包含数据包标识符。因为Qos=0时,是不会得到响应的。
PUBACK,PUBREC或PUBREL数据包必须包含与最初发送的PUBLISH数据包相同的数据包标识符。类似地,SUBACK和UNSUBACK必须包含分别在相应的SUBSCRIBE和UNSUBSCRIBE数据包中使用的数据包标识符。
控制包类型 | 是否包含标识符字段 |
---|---|
CONNECT | 否 |
CONNACK | 否 |
PUBLISH | 是(当Qos>0时) |
PUBACK | 是 |
PUBREC | 是 |
PUBREL | 是 |
PUBCOMP | 是 |
SUBSCRIBE | 是 |
SUBACK | 是 |
UNSUBSCRIBE | 是 |
UNSUBACK | 是 |
PINGREQ | 否 |
PINGRESP | 否 |
DISCONNECT | 否 |
客户端和服务端各自独立分配唯一标识。但是,一对客户端和服务端交换数据的时候可以使用相同的唯一标识。
四、载荷
有些MQTT控制包的最后一部分会包含载荷。其实负载就是想要传递的数据。
控制包类型 | 负载 |
---|---|
CONNECT | 必须 |
CONNACK | 无 |
PUBLISH | 可选(可以空载) |
PUBACK | 无 |
PUBREC | 无 |
PUBREL | 无 |
PUBCOMP | 无 |
SUBSCRIBE | 必须 |
SUBACK | 必须 |
UNSUBSCRIBE | 必须 |
UNSUBACK | 无 |
PINGREQ | 无 |
PINGRESP | 无 |
DISCONNECT | 无 |