对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):
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 | 断开连接,客户端通知服务端,需要断开当前网络连接 | 不需要 |
如有写得不对的地方,敬请指出!
标签:协议,字节,订阅,报文,COAP,MQTT,服务端,客户端 来源: https://blog.csdn.net/weixin_43208630/article/details/122714138