文章目录

  • 系列文章目录
  • 前言

  • 一、MQTT控制包格式
  • 二、固定包头
  • 控制包类型
  • 控制包类型标识
  • 剩余长度
  • 三、可变包头
  • 数据包标识
  • 四、载荷



系列文章目录



MQTT协议详解 一、MQTT简介

MQTT协议详解 二、MQTT控制包格式

MQTT协议详解 三、MQTT控制包(CONNECT)


前言


本章接详细介绍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。

SUBSCRIBEUNSUBSCRIBEPUBLISH(在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