对MQTT协议以及COAP协议理解剖析并实现(一)

MQTT简介:

        MQTT协议是在1999年,由IBM的Andy Stanford-Clark 和 Arcom 的 Arlen Nipper为了通过卫星网络连接输油管道的项目开发的,为了满足低电量以及低网络带宽的需求,MQTT协议在设计的初期囊括了以下一些特点:

       1、网络开销小,消息头最小只有2字节,这相比HTTP大大降低了网络流量。
  2、是可保持的会话,为实现服务端及时推消息提供了条件。
  3、是异步消息机制,不会阻塞占用资源。
  4、具备异常中断通知机制,可以获得硬件在线信息变化,及时得到掉线消息。
  5、使用发布/订阅消息模式,一对多或多对一的消息传输,实现与应用程序的解耦。
  6、传输可靠性可控,及三种服务质量,分别是至多一次、至少一次、只有一次。指的是发布者发出的信息,代理服务和订阅者是否收到的情况。
  7、客户端程序够轻量,可在很多嵌入式设备中运行。
  8、可满足低带宽、高延迟、不稳定的网络环境,可传输任意类型的数据。

    在MQTT中,无论是服务端还是客户端,他们都可以有三种身份:发布者、代理、订阅者。

        在MQTT的通信实现方式是:发布/订阅,而发布与订阅又是基于主题。

        因为在MQTT协议中,端与端之间是不能直接进行收发信息的,必须通过服务端进行管理分配,而这个服务端就相当于一个“代理”的角色。这个代理的角色所起的作用:进行消息的存储与转发。

        基于MQTT协议发送消息示例:

        ①发布者连接上MQTT代理

        ②订阅者连接上MQTT代理

        ③订阅者A向MQTT代理订阅TopicA

        ④发布者将一则主题为TopicA的消息发扫描后给MQTT代理

        ⑤MQTT代理收到了发布者的消息,并检查到订阅者A需要接收这个消息,然后把该消息转发给订阅者A

        ⑥订阅者A从MQTT代理接收到消息

对MQTT协议以及COAP协议理解剖析并实现(一)

 注意:在一个端上,他既可以作为发布者,也能作为订阅者,但一个主题只有一个发布者,但这个主题可以同时被多个订阅者进行订阅,一个客户端也可以同时订阅多个主题。

        

MQTT协议数据包:

        MQTT协议数据包的消息格式:固定头(必须有)+ 可变头(可有可无)+ 消息(可有可无)

结构体 解释 数据格式
固定报头 报文最开始部分,所有报文都必须有 类型 + 标志位 + 剩余长度
可变报头 固定报文的附加部分,有些报文没有这个部分 报文标识符 + 类型配置
消息体(负载) 携带的消息内容,有些报文没有这个部分 自定义消息

1、固定报头(Fixed header):

Bit 7 6 5 4 3 2 1 0
Byte1(第一个字节) MQTT控制报文的类型 控制报文的标志位,相当于属性参数
Byte2(第二个字节) 剩余长度,当前报文剩余部分的字节数,包括可变报头和消息体

        1.1、控制报文类型

                固定报文第一字节的高四位(7 -  4号bit)是代表控制报文的类型,其具体含义如下:

7-4号bit 十进制值 报文类型 报文允许发起的方向 报文描述
0000 0 RESERVED 禁止 保留,不可用
0001 1 CONNET 客户端—→服务端 客户端请求连接到服务端的代理服务
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 RESERVER 禁止 保留,不可用

         1.2、标志位

                固定报文第1个字节的低4位(3 - 0号bit)是代表每个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

       

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

                固定报头的报文类型高4位与标志位的低4位结合起来,最终第一个自己的具体值就是其报文类型所代表的的数值,具体值如下:

报文类型 报文作用 十六进制
CONNECT 连接服务端 0x10
CONNACK 连接成功确认 0x20
PUBLISH 新发布等级0不保存

0x30

PUBLISH 新发布等级0需保存 0x31
PUBLISH 新发布等级1不保存 0x32
PUBLISH 新发布等级1需保存 0x33
PUBLISH 新发布等级2不保存 0x34
PUBLISH 新发布等级2需保存 0x35
PUBLISH 重发等级2不保存 0x38
PUBLISH 重发等级2需保存 0x39
PUBACK 等级1发布成功

0x40

PUBREC 等级2发布收到 0x50
PUBREL 等级2发布释放 0x62
PUBCOMP 等级2发布完成 0x70
SUBSCRIBE 订阅主题 0x82
SUBACK 订阅完成确认 0x90
UNSUBSCRIBE 取消订阅 0xA2
UNSUBACK 取消完成确认 0xB0
PINGREQ 心跳包 0xC0
PINGRESP 心跳回复 0xD0
DISCONECT 断开网络连接 0xE0

        1.4、剩余长度

                剩余长度在MQTT的协议中,是从第二个字节开始,且最多允许占用4个字节。

                剩余长度描述的是本次发送的消息除去了第一个字节以外,其中的(可变头+消息)的所有字节大小。从固定头的第2字节开始是用于表示MQTT数据包剩余长度的字段,最少为一个字节,最大为四个字节。每一个字节的低7位用于表示剩余长度的大小,范围为0~127。最高的1位是标识位,用于说明是否有后续字节

                若标识为0,代表没有后续字节。

                若标识为1,代表后续还有一个字节用于标识包的长度。

                MQTT协议相应字节数对应的最小、最大包长度如下表所示:

                剩余长度的字节数最小包长度:10字节                (0x00)

                剩余长度的字节数最大包长度:268 435 455字节(0xFF,0xFF,0xFF,0x7F)

                MQTT协议中数据包的剩余长度的最大长度为268435455 字节,约 256M。

                注意:上述的剩余长度不包含固定头的大小,是指(可变头+消息)总长度

                关于剩余长度的变化规则示例:

                ①若剩余长度为100,那么我们可以使用1个字节表示,那么这个剩余长度二进制为 01100100。

                其中7号位的0表示没有剩余长度字节了,1100100则表示为(十进制)100

                ②若剩余长度为15000,那么我们则需要使用2个字节表示,那么这个剩余长度二进制为

01110101 10011000

                计算的根据是:

                        128 * 117 + 24 = 15000;

                        其中128来源是因为第二个字节的最大为0111 1111(127),由于进1的关系,每进1位为128,所以第三个字节的数据为128 * 117 = 14976时最逼近15000,剩余的24由第二个字节表示。

                ③第三个字节以此类推...

字节数 最大十进制/十六进制(高字节在前) 最大二进制(高字节在前)
1 127(0x7F) 01111111
2 16383(0x7F,0xFF) 01111111 11111111
3 2097151(0x7F,0xFF,0xFF) 01111111 11111111 11111111
4 268435455(0x7F,0xFF,0xFF,0xFF) 01111111 11111111 11111111 11111111

2、可变头(Variable Header)

        可变报文头主要包含协议名、协议版本、连接标志、心跳间隔时间、连接返回码、主题名等。

3、消息体(Payload)

        PUBLISH的有效负荷为应用消息,而其他控制报文是否需要包含有效负荷如下:

控制报文 报文描述 有效负荷
CONNECT 客户端请求连接到服务端的代理服务 需要
CONNACK 连接请求的回复确认报文 不需要
PUBLISH 发布主题信息 可选,可以为0
PUBCAK 发布确认,当QoS==1时,对PUBLISH的响应确认 不需要
PUBREC 发布收到,当QoS==2时,对PUBLISH的响应确认,是QoS==2实现的第一步 不需要
PUBREL 发布释放,当QoS==2时,对PUBREC的响应确认,是QoS==2实现的第二步 不需要
PUBCOMP 发布完成,当QoS==2时,对PUBREL的响应确认,是QoS==2实现的第三步 不需要
SUBSCRIBE 客户端订阅主题,可一次订阅一个或者多个主题(使用通配符) 需要
SUBACK 订阅完成确认,是对SUBSCRIBE的响应确认 不需要
UNSUBSCRIBE 取消订阅,客户端发起的取消某个主题的订阅 需要
UNSUBACK 取消订阅确认,是对UNSUBSCRIBE 不需要
PINGREQ 心跳,表示这个数据包是为了通知服务端客户端还在正常连接着 不需要
PINGRESP 心跳响应,表示服务端已经成功收到了客户端的心跳 不需要
DISCONNECT 断开连接,客户端通知服务端,需要断开当前网络连接 不需要

如有写得不对的地方,敬请指出!

上一篇:【MQTT入门】基础——发布和订阅


下一篇:国内公有云首个支持保留消息功能!百度智能云天工物联网核心套件技术再升级