其他分享
首页 > 其他分享> > 对MQTT协议以及COAP协议理解剖析并实现(一)

对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协议数据包:

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

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

1、固定报头(Fixed header):

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

        1.1、控制报文类型

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

7-4号bit十进制值报文类型报文允许发起的方向报文描述
00000RESERVED禁止保留,不可用
00011CONNET客户端—→服务端客户端请求连接到服务端的代理服务
00102CONNACK客户端←―服务端连接请求的回复确认报文
00113PUBLISH客户端←→服务端发布主题信息
01004PUBACK客户端←→服务端发布确认,当QoS==1时,对PUBLISH的响应确认
01015PUBREC客户端←→服务端发布收到,当QoS==2时,对PUBLISH的响应确认,是QoS==2实现的第一步
01106PUBREL客户端←→服务端发布释放,当QoS==2时,对PUBREC的响应确认,是QoS==2实现的第二步
01117PUBCOMP客户端←→服务端发布完成,当QoS==2时,对PUBREL的响应确认,是QoS==2实现的第三步
10008SUBSCRIBE客户端―→服务端客户端订阅主题,可一次订阅一个或者多个主题(使用通配符)
10019SUBACK客户端←―服务端订阅完成确认,是对SUBSCRIBE的响应确认
101010UNSUBSCRIBE客户端―→服务端取消订阅,客户端发起的取消某个主题的订阅
101111UNSUBACK客户端←―服务端取消订阅确认,是对UNSUBSCRIBE
110012PINGREQ客户端―→服务端心跳,表示这个数据包是为了通知服务端客户端还在正常连接着
110113PINGRESP客户端←―服务端心跳响应,表示服务端已经成功收到了客户端的心跳
111014DISCONNECT客户端―→服务端断开连接,客户端通知服务端,需要断开当前网络连接
111115RESERVER禁止保留,不可用

         1.2、标志位

                固定报文第1个字节的低4位(3 - 0号bit)是代表每个MQTT控制报文类型特定的标志,必须与控制报文类型配套对应使用,否则服务端代理服务会拒绝服务或断开连接,其具体含义如下:

报文类型Bit3BIt2BIt1BIt0
CONNECT0000
CONNACK0000
PUBLISH是否为重复发服务质量高位服务质量低位是否保存消息
PUBACK0000
PUBREC0000
PUBREL0010
PUBCOMP0000
SUBSCRIBE0010
SUBACK0000
UNSUBSCRIBE0010
UNSUBACK0000
PINGREQ0000
PINGRESP0000
DISCONNECT0000

       

        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由第二个字节表示。

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

字节数最大十进制/十六进制(高字节在前)最大二进制(高字节在前)
1127(0x7F)01111111
216383(0x7F,0xFF)01111111 11111111
32097151(0x7F,0xFF,0xFF)01111111 11111111 11111111
4268435455(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断开连接,客户端通知服务端,需要断开当前网络连接不需要

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

标签:协议,字节,订阅,报文,COAP,MQTT,服务端,客户端
来源: https://blog.csdn.net/weixin_43208630/article/details/122714138