概述
文章目录
- 相关文章
- 1.前言
- 2.MQTT协议实现方式
- 3.MQTT 控制报文格式
- 3.1 固定报头的格式
- 3.2 可变报头
- 3.3 有效载荷 Payload
- 4.MQTT 控制报文
- 5.参考资料
相关文章
1.《MQTT协议分析总结(一)》
2.《MQTT协议分析总结(二)》
3.《【IoT】如何连接到百度IoT云》
4.《【FreeRTOS】基于STM32移植LWIP 2.1.2之MQTT》
1.前言
MQTT 3 (当前版本3.1.1
)是目前使用的最为广泛的MQTT协议标准。尽管MQTT 5标准已经发布,并且带来了一些令人振奋的新特性,但是在整个应用场景上,业界从版本 3 到 5 的过渡可能会持续一段时间。MQTT最大的优点在于可以以极少的代码和有限的带宽,为远程设备提供实时可靠的消息服务。做为一种低开销、低带宽占用的即时通讯协议,MQTT在物联网、小型设备、移动应用等方面有广泛的应用。
2.MQTT协议实现方式
实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者(Publish)、代理(Broker 服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。
MQTT传输的消息分为:主题(Topic)和负载(payload)两部分:
- (1)
Topic
,可以理解为消息的类型,订阅者订阅(Subscribe)
后,就会收到该主题的消息内容(payload)
; - (2)
payload
,可以理解为消息的内容,是指订阅者具体要使用的内容
3.MQTT 控制报文格式
MQTT协议工作在TCP之上,端和代理之间通过交换预先定义的控制报文来完成通信。MQTT报文有 3 个部分组成,并按下表顺序出现:
Fixed header | 固定报头,所有控制报文都包含 |
---|---|
Variable header | 可变报头,部分控制报文包含 |
Payload | 有效载荷,部分控制报文包含 |
3.1 固定报头的格式
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文的类型 | 用于指定控制报文类型的标志位 | ||||||
byte 2... | 剩余长度 |
- MQTT控制报文的类型
名字 | 值 | 报文流动方向 | 描述 |
---|---|---|---|
Reserved | 0 | 禁止 | 保留 |
CONNECT | 1 | 客户端到服务端 | 客户端请求连接服务端 |
CONNACK | 2 | 服务端到客户端 | 连接报文确认 |
PUBLISH | 3 | 两个方向都允许 | 发布消息 |
PUBACK | 4 | 两个方向都允许 | QoS 1消息发布收到确认 |
PUBREC | 5 | 两个方向都允许 | 发布收到(保证交付第一步) |
PUBREL | 6 | 两个方向都允许 | 发布释放(保证交付第二步) |
PUBCOMP | 7 | 两个方向都允许 | QoS 2消息发布完成(保证交互第三步) |
SUBSCRIBE | 8 | 客户端到服务端 | 客户端订阅请求 |
SUBACK | 9 | 服务端到客户端 | 订阅请求报文确认 |
UNSUBSCRIBE | 10 | 客户端到服务端 | 客户端取消订阅请求 |
UNSUBACK | 11 | 服务端到客户端 | 取消订阅报文确认 |
PINGREQ | 12 | 客户端到服务端 | 心跳请求 |
PINGRESP | 13 | 服务端到客户端 | 心跳响应 |
DISCONNECT | 14 | 客户端到服务端 | 客户端断开连接 |
Reserved | 15 | 禁止 | 保留 |
-
剩余长度
位置: 从第2个字节开始。剩余长度(Remaining Length) 表示当前报文剩余部分的字节数,包括可变报头和负载的数据。剩余长度不包括用于编码剩余长度字段本身的字节数。
剩余长度字段使用一个变长度编码方案,对小于128的值它使用单字节编码。更大的值按下面的方式处理。低7位有效位用于编码数据,最高有效位用于指示是否有更多的字节。因此每个字节可以编码128个数值和一个延续位(continuation bit)。剩余长度字段最大4个字节。
字节数 | 最小值 | 最大值 |
---|---|---|
1 | 0 (0x00) | 127 (0x7F) |
2 | 128 (0x80, 0x01) | 16 383 (0xFF, 0x7F) |
3 | 16 384 (0x80, 0x80, 0x01) | 2 097 151 (0xFF, 0xFF, 0x7F) |
4 | 2 097 152 (0x80, 0x80, 0x80, 0x01) | 268 435 455 (0xFF, 0xFF, 0xFF, 0x7F) |
分别表示(每个字节的低7位用于编码数据,最高位是标志位):
- 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)
非负整数X使用 变长编码方案 的算法如下:
do
encodedByte = X MOD 128
X = X DIV 128
// if there are more data to encode, set the top bit of this byte
if ( X > 0 )
encodedByte = encodedByte OR 128
endif
'output' encodedByte
while ( X > 0 )
MOD是模运算,DIV是整数除法,OR是位操作或(C语言中分别是%,/,|)
对应的C语言代码如下:
/**
* Encodes the message length according to the MQTT algorithm
* @param buf the buffer into which the encoded data is written
* @param length the length to be encoded
* @return the number of bytes written to buffer
*/
int MQTTPacket_encode(unsigned char* buf, int length)
{
int rc = 0;
FUNC_ENTRY;
do
{
char d = length % 128;
length /= 128;
/* if there are more digits to encode, set the top bit of this digit */
if (length > 0)
d |= 0x80;
buf[rc++] = d;
} while (length > 0);
FUNC_EXIT_RC(rc);
return rc;
}
剩余长度字段的 变长解码方案 算法如下:
multiplier = 1
value = 0
do
encodedByte = 'next byte from stream'
value += (encodedByte AND 127) * multiplier
if (multiplier > 128*128*128)
throw Error(Malformed Remaining Length)
multiplier *= 128
while ((encodedByte AND 128) != 0)
AND是位操作与(C语言中的&)
这个算法终止时,value包含的就是剩余长度的值。
对应的C语言代码如下:
/**
* Decodes the message length according to the MQTT algorithm
* @param getcharfn pointer to function to read the next character from the data source
* @param value the decoded length returned
* @return the number of bytes read from the socket
*/
int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value)
{
unsigned char c;
int multiplier = 1;
int len = 0;
#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
FUNC_ENTRY;
*value = 0;
do
{
int rc = MQTTPACKET_READ_ERROR;
if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
{
rc = MQTTPACKET_READ_ERROR; /* bad data */
goto exit;
}
rc = (*getcharfn)(&c, 1);
if (rc != 1)
goto exit;
*value += (c & 127) * multiplier;
multiplier *= 128;
} while ((c & 128) != 0);
exit:
FUNC_EXIT_RC(len);
return len;
}
3.2 可变报头
某些MQTT控制报文包含一个可变报头部分。它在固定报头和负载之间。可变报头的内容根据报文类型的不同而不同。可变报头的报文标识符(Packet Identifier
)字段存在于在多个类型的报文里。
备注:可变报头不仅指报文标识符,下面只是用它来举例。MQTT 报文类型的不同,就有不同的可变报头。
报文标识符字节,如下:
Bit | 7 - 0 |
---|---|
byte 1 | 报文标识符 MSB |
byte 2 | 报文标识符 LSB |
很多控制报文的可变报头部分包含一个两字节的报文标识符字段。需要报文标识符的控制报文在下面的表格中列出:
控制报文 | 报文标识符字段 |
---|---|
CONNECT | 不需要 |
CONNACK | 不需要 |
PUBLISH | 需要(如果QoS > 0) |
PUBACK | 需要 |
PUBREC | 需要 |
PUBREL | 需要 |
PUBCOMP | 需要 |
SUBSCRIBE | 需要 |
SUBACK | 需要 |
UNSUBSCRIBE | 需要 |
UNSUBACK | 需要 |
PINGREQ | 不需要 |
PINGRESP | 不需要 |
DISCONNECT | 不需要 |
客户端和服务端彼此独立地分配报文标识符。因此,客户端服务端组合使用相同的报文标识符可以实现并发的消息交换。举例如下:
3.3 有效载荷 Payload
某些MQTT控制报文在报文的最后部分包含一个有效载荷,对于PUBLISH
来说有效载荷就是应用消息。
有效载荷的控制报文 Control Packets that contain a Payload
控制报文 | 有效载荷 |
---|---|
CONNECT | 需要 |
CONNACK | 不需要 |
PUBLISH | 可选 |
PUBACK | 不需要 |
PUBREC | 不需要 |
PUBREL | 不需要 |
PUBCOMP | 不需要 |
SUBSCRIBE | 需要 |
SUBACK | 需要 |
UNSUBSCRIBE | 需要 |
UNSUBACK | 不需要 |
PINGREQ | 不需要 |
PINGRESP | 不需要 |
DISCONNECT | 不需要 |
4.MQTT 控制报文
具体的每个报文在下面的文章中介绍:《MQTT协议分析总结(二)》
5.参考资料
MQTT 5.0协议中文版:
https://github.com/twoFiveOneTen/MQTT_V5
MQTT Version 5.0:
http://docs.oasis-open.org/mqtt/mqtt/v5.0/cs02/mqtt-v5.0-cs02.htm
MQTT 3.1.1协议中文版:
https://github.com/mcxiaoke/mqtt
MQTT Version 3.1.1:
http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html
最后
以上就是怕黑台灯为你收集整理的MQTT协议分析总结(一)相关文章1.前言2.MQTT协议实现方式3.MQTT 控制报文格式4.MQTT 控制报文5.参考资料的全部内容,希望文章能够帮你解决MQTT协议分析总结(一)相关文章1.前言2.MQTT协议实现方式3.MQTT 控制报文格式4.MQTT 控制报文5.参考资料所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复