【EMQX 5.0】1.3 MQTT协议数据包结构
1. MQTT数据包
官方文档:
http://mqtt.org/documentation
MQTT数据包
由以下
三部分构成
:
- 固定头(Fixed header)。存在于所有MQTT数据包中,表示数据包类型及数据包的分组类标识, 如连接,发布,订阅,心跳等。
- 可变头(Variable header)。存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其 具体内容。
- 消息体(Payload)。存在于部分MQTT数据包中,表示客户端收到的具体内容。
3.1 固定头(Fixed header)
固定头包含两部分
- 首字节 ( = 数据包类型 + 标识 [类型控制字段],1字节 )
- 剩余长度 ( = 当前包中剩余内容长度的字节数,1 - 4字节)
剩余长度
是当前包中剩余内容长度的字节数,包括变量头和有效负载中的数据)。剩余
长度不包含用来编码剩余长度的字节。
剩余长度使用了一种可变长度的结构来编码,这种结构使用单一字节表示0-127
的值。大于
127
的值如下处 理。
每个字节的低7位用来编码数据,最高位用来表示是否还有后续字节
。因此每个字节可以编码
128
个值,再加 上一个标识位。剩余长度最多可以用四个字节来表示。
MQTT 最大传输数据:
计算数据包最大
传输256MB数据
:
- 一个字节2的7次方Byte,最高位用来表示是否还有后续字节
- 4个字节 = 2的28次方Byte = 2的18次方KByte = 2的8次方MByte = 256MB
3.1.1 数据包类型
位置:第一个字节(Byte 1)
中的
7-4个bit位
(Bit[7-4])
,表示
4
位无符号值
- 第一个字节的高4位确定消息报文的类型
- 4个bit位能确定16种类型
- 0000和1111是保留字段。
MQTT消息报文类型如下:
报文类型 | 字段值 | 数据方向 | 描述 | 7-4 bit值 |
保留 | 0 | 禁用 | 保留 | 0000 |
CONNECT | 1 | Client --> Server | 客户端连接到服务器 | 0001 |
CONNACK | 2 | Serer --> Client | 连接确认 | 0010 |
PUBLISH | 3 | Client<--> Server | 发布消息 | 0011 |
PUBACK | 4 | Client<--> Server | 发布确认(QoS1) | 0100 |
PUBREC | 5 | Client<--> Server | 消息已接收(Qos2第一阶段) | 0101 |
PUBREL | 6 | Client<--> Server | 消息释放(QoS2第二阶段) | 0110 |
PUBCOMP | 7 | Client<--> Server | 发布结束(QoS2第三阶段) | 0111 |
SUBSCRIBE | 8 | Client --> Server | 客户端订阅请求 | 1000 |
SUBACK | 9 | Serer --> Client | 服务端订阅确认 | 1001 |
UNSUBACRIBE | 10 | Client --> Server | 客户端取消订阅 | 1010 |
UNSUBACK | 11 | Serer --> Client | 服务端取消订阅确认 | 1011 |
PINGREQ | 12 | Client --> Server | 客户端发送心跳 | 1100 |
PINGRESP | 13 | Serer --> Client | 服务端回复心跳 | 1101 |
DISCONNECT | 14 | Client --> Server | 客户端断开连接请求 | 1110 |
AUTH(V5.0使用) | 15 | Client<--> Server | 认证数据交换 | 1111 |
3.1.2 标志位
位置:第一个字节中的0-3
个
bit
位
(Bit[3-0])
。意思是字节位
Bit[3-0]
用作报文的标识。
- 首字节的低4位(bit3~bit0)用来表示某些报文类型的控制字段,实际上只有少数报文类型有控制位,如下图
(1):其中
Bit[3] 为 DUP字段
,如果该值为
1
,表明这个数据包是一条
重复的消息
;否则该数据包就是第一次发布的消息。
(2):
Bit[2-1]
为
Qos字段
:
- QoS 0:Bit 1= 0 Bit 2=0 ,至多一次;(消息发送)
- QoS 1:Bit 1= 1 Bit 2=0 ,至少一次;(消息发送)
- QoS 2:Bit 1= 0 Bit 2=1 ,只有一次;(消息发送)
- 非法:Bit 1= 1 Bit 2=1 ,服务器认为非法,会关闭当前连接。
目前
Bit[3-0]只在PUBLISH协议中使用有效
,并且表中指明了是
MQTT 3.1.1/5.0版本
。对于其它
MQTT
协议版本,内容可能不同。所有固定头标记为"
保留
"
的协议类型,
Bit[3-0]
必须保持与表中保持一致,如
SUBSCRIBE 协议,其Bit 1
必须为
1
。
如果接收方接收到非法的消息,会强行关闭当前连接。
3.1.3 MQTT消息QoS
MQTT发布
消息服务质量保证
(
QoS
)不是端到端的,是客户端与服务器之间的。订阅者收到
MQTT
消息的
QoS级别,
最终取决于发布消息的QoS和主题订阅的QoS
。(
标志位
0-2,
取最低标志位
)
- Qos0:至多一次;(消息发送)
场景:如环境参数上报,一两次丢失不影响
- QoS 1:至少一次;(消息发送)
禁用场景:如订单,付费等
- QoS 2:只有一次;(消息发送)
Notity(Msg):通知上层应用消费消息
场景:如订单,付费等
3.1.4 PUBLISH 的 保留标识
- Bit[0] 为 RETAIN字段,发布保留标识
- Bit[0] = 1:表示服务器要保留这次推送的信息,如果有新的订阅者出现, 就把这消息推送给它,如果没有那么推送至当前订阅者后释放。
3.2 可变头(Variable Header)
可变头的意思是可变化的消息头部
。
有些报文类型包含可变头部,有些报文则不包含
。可变头部在固定头部和消息内容之间,其内容根据报文类型不同而不同。
3.2.1 协议名(protocol name)
协议名是表示协议名MQTT的UTF-8编码的字符串
。
MQTT
规范的后续版本不会改变这个字符串的偏移和长度。
支持多种协议的服务端使用协议名字段判断数据是否为MQTT报文。协议名必须是UTF-8字符串“MQTT”。如果服务端不愿意接受CONNECT但希望表明其MQTT服务端身份,可以发送包含原因码为0x84(不支持的协议版本)的CONNACK报文,然后必须关闭网络连接
3.2.2 协议版本 (protocol version)
- 8位无符号值表示客户端的版本等级。
- 3.1.1版本的协议等级是4
- MQTT v5.0的协议版本字段为5(0x05)
3.2.3 MQTT会话(Clean Session)
MQTT客户端向服务器发起
CONNECT
请求时,可以通过
’Clean Session’
标志设置会话。
- ‘Clean Session’ 设置为 0,表示创建一个持久会话,在客户端断开连接时,会话仍然保持并保存离线消息,直到会话超时注销。
- ‘Clean Session’ 设置为 1,表示创建一个新的临时会话,在客户端断开时,会话自动销毁。
3.2.4 Will Flag/Will Qos/Will Retain (遗言机制)
如果
Will Flag
被设置为
1
,这意味着,如果连接请求被接受,
服务端必须存储一个Will Message
,并和网络连接关联起来。
之后在网络连接断开时候必须发布Will Message,除非服务端收到DISCONNECT包删掉了Will Message
Will Message会在某些情况下发布,包括但不限于:
- 服务端发现I/O错误或网络失败。
- 客户端在Keep Alive时间内通信失败。
- 客户端没有发送DISCONNECT包就关闭了网络连接。
- 服务端因协议错误关闭了网络连接。
A. 如果
Will Flag
被设置为
1
,连接标识中的
Will QoS
和
Will Retain
字段将会被服务端用到
- Will QoS 这两个bit表示发布Will Message时使用QoS的等级
- Will Retain 这个bit表示Will Message在发布之后是否需要保留。
B.
Will Flag设置为0,那么Will Retain必须是0
C.
Will Flag
设置为
1
:
- 如果Will Retain设置为0,那么服务端必须发布Will Message,不必保存
- 如果Will Retain设置为1,那么服务端必须发布Will Message,并保存
3.2.5 User Name Flag
- 如果User Name Flag设置为0,那么用户名不必出现在载荷中
- 如果User Name Flag设置为1,那么用户名必须出现在载荷中
3.2.6 Password Flag
- 如果Password Flag设置为 0,那么密码不必出现在载荷中
- 如果Password Flag设置为 1,那么密码必须出现在载荷中
- 如果User Name Flag设置为 0,那么Password Flag必须设置为0
3.2.7 MQTT连接 保活心跳
KeepAlive:它指的是客户端从发送完成一个控制包到开始发送下一个的最大时间间隔。
A. 心跳的作用:PINGREQ包 (心跳包)
PINGREQ
包 从客户端发往服务端,可以用来:
- 在没有其他控制包从客户端发送给服务端的时候,告知服务端客户端的存活状态。
- 请求服务端响应,来确认服务端是否存活。
- 确认网络连接的有效性。
PINGRESP包从服务端发送给客户端来响应PINGREQ包。它代表服务端是存活的。
B. Keep Alive 保活周期
- 通过KeepAlive参数设置保活周期。
- Keep Alive是以秒为单位的时间间隔
- 用 2字节表示
客户端可以在任何时间发送PINGREQ
包,不用关心
Keep Alive
的值,用
PINGRESP
来判断与服务端的网络连接是否正常。
如果
Keep Alive
的值
非0
,而且
服务端在1.5倍Keep Alive的周期内没有收到客户端的控制包
,服务端必须作为网络故障
断开网络连接
如果客户端在发送了PINGREQ
后,在一个合理的时间都没有收到
PINGRESP
包,客户端应该关闭和服务端的网络连接。
Keep Alive
的值为
0
,就关闭了维持的机制。这意味着,在这种情况下,
服务端不会断开静默的客户端
。
3.2.8 MQTT遗愿消息(Last Will)
MQTT客户端向服务器端CONNECT请求时
,
可以设置是否发送遗愿消息(Will Message)标志
,
和遗愿消息主题(Topic)与内容(Payload)。
MQTT客户端异常下线
时
(
客户端断开前未向服务器发送
DISCONNECT
消息
)
,
MQTT
消息
服务器会发布遗愿消息
。
3.2.9 Message ID(Packet Identififier > 消息ID)
- 包含2字节
- 高字节在前,低字节在后。
- 默认是从1开始并自增
- ID被用完(消息被消费-)后,这个消息ID可以被重用
- 客户端和服务端的消息ID是独立分配 , 客户端和服务端可以同时使用同一个消息ID
包含
Packet Identififier
的协议类型包括:
- PUBLISH( QoS > 0 ):发布消息
- PUBACK:发布确认(QoS1)
- PUBREC:消息已接收(Qos2 第一阶段)
- PUBREL:消息释放(QoS2 第二阶段)
- PUBCOMP:发布结束(QoS2 第三阶段)
- SUBSCRIBE:客户端订阅请求
- SUBACK:服务端订阅确认
- UNSUBSCRIBE:客户端取消订阅
- UNSUBACK:服务端取消订阅确认
总结:ACK 表示消息被消费
消息ID
默认是从
1
开始并自增,如果一个消息
ID
被用完后,这个消息
ID
可以被重用。对于
PUBLISH (QoS 1)
来 说,如果发送端接收到PUBACK
,那么这个消息
ID
就用完了。对于
PUBLISH(QoS 2)
,如果接收方收到
PUBCOMP
, 那么这个消息ID
就用完了。对于
SUBSCRIBE
和
UNSUBSCRIBE
,消息
ID
使用完成的标记是发送方收到了对应的SUBACK和
UNSUBACK
。
另外客户端和服务端的消息ID
是独立分配的,客户端和服务端可以同时使用同一个消息
ID
。
3.3 消息体(Payload)
Payload
意思是
消息载体
的意思
- 如PUBLISH的Payload就是指消息内容(应用程序发布的消息内容)。
- 而CONNECT的Payload则包含Client Identififier,Will Topic,Will Message,Username,Password等信息。
包含
payload
的报文类型如下:
报文类型 | 字段值 | 描述 |
是否需要payload
|
保留 | 0 | 保留 |
-
|
CONNECT | 1 | 客户端连接到服务器 | Required |
CONNACK | 2 | 连接确认 | None |
PUBLISH | 3 | 发布消息 |
Optional
|
PUBACK | 4 | 发布确认(QoS1) |
None
|
PUBREC | 5 | 消息已接收(Qos2第一阶段) |
None
|
PUBREL | 6 | 消息释放(QoS2第二阶段) |
None
|
PUBCOMP | 7 | 发布结束(QoS2第三阶段) | None |
SUBSCRIBE | 8 | 客户端订阅请求 |
Required
|
SUBACK | 9 | 服务端订阅确认 |
Required
|
UNSUBACRIBE | 10 | 客户端取消订阅 |
Required
|
UNSUBACK | 11 | 服务端取消订阅确认 | Required |
PINGREQ | 12 | 客户端发送心跳 |
None
|
PINGRESP | 13 | 服务端回复心跳 |
None
|
DISCONNECT | 14 | 客户端断开连接请求 |
None
|
AUTH(V5.0使用) | 15 | 认证数据交换 | - |
总结:
我们介绍了MQTT
协议的消息格式,
MQTT消息格式包含Fixed Header, Variable Header和Payload
。 因为MQTT
消息格式非常精简,所以可以高效的传输数据。
Fixed Header中包含首字节,高
4
位用来表示报文类型,低
4
位用于类型控制。
目前只有PUBLISH使用了类型控制字段
。其它控制字段被保留并且必须与协议定义保持一致。
Fixed Header同时包含
Remaining Length
,这是剩余消息长度,最大长度为
4
字节,理论上一条
MQTT
最大可以传输256MB数据。Remaining Length=Variable Header+Payload长度
。
Variable Header是可变头部,有些报文类型中需要包含可变头部,可变头部根据报文类型不同而不同。比如 Packet Identififier在发布,订阅
/
取消订阅等报文中都使用到。
Payload是消息内容,也只在某些报文类型中出现,其内容和格式也根据报文类型不同而不同。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?