最近学习MQTT协议和与Onenet平台的数据交互。MQTT中有几个方法,想借此机会将每个方法的流程梳理一下
在MQTT协议中,一个MQTT数据包由:固定头(Fixed header)、可变头(Variable header)、消息体(payload)三部分构成。MQTT数据包结构如下:
(1)固定头(Fixed header)。存在于所有MQTT数据包中,表示数据包类型及数据包的分组类标识。
(2)可变头(Variable header)。存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其具体内容。
(3)消息体(Payload)。存在于部分MQTT数据包中,表示客户端收到的具体内容。
Bit |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
byte 1 |
MQTT Packet Type |
0 |
0 |
0 |
0 |
|||
byte2 - 5 |
Remaining Length(该字段占用1-4个字节) |
MQTT数据包类型:位于Byte1的第7~4位
名字 |
值 |
流向 |
描述 |
CONNECT |
1 |
C->S |
客户端请求与服务端建立连接 |
CONNACK |
2 |
S->C |
服务端确认连接建立 |
PUBLISH |
3 |
双向 |
发布消息 |
PUBACK |
4 |
双向 |
收到发布消息确认 |
PUBREC |
5 |
双向 |
发布消息收到 |
PUBREL |
6 |
双向 |
发布消息释放 |
PUBCOMP |
7 |
双向 |
发布消息完成 |
SUBSCRIBE |
8 |
C->S |
订阅请求 |
SUBACK |
9 |
S->C |
订阅确认 |
UNSUBSCRIBE |
10 |
C->S |
取消订阅 |
UNSUBACK |
11 |
S->C |
取消订阅确认 |
PING |
12 |
C->S |
客户端发送PING(连接保活)命令 |
PINGRSP |
13 |
S->C |
PING命令回复 |
DISCONNECT |
14 |
C->S |
断开连接 |
从上面我们可以看到CONNECT应该是1
|
Description |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
byte 1-2 |
ProtocolName Length |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
||
byte 3 |
‘M’ |
0 |
1 |
0 |
0 |
1 |
1 |
0 |
1 |
byte 4 |
‘Q’ |
0 |
1 |
0 |
1 |
0 |
0 |
0 |
1 |
byte 5 |
‘T’ |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
0 |
byte 6 |
‘T’ |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
0 |
Byte7 |
Protocol Level |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
Byte8 |
Connect Flag |
User flag |
Password flag |
WillRetain Flag |
WillQos Flag |
WillFlag |
CleanSession Flag |
Reserve |
|
Byte9-10 |
KeepAlive |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Clean Session
0,表示如果订阅的客户机断线了,要保存为其要推送的消息(QoS为1和QoS为2),若其重新连接时,需将这些消息推送(若客户端长时间不连接,需要设置一个过期值)。
session需保持的内容包含:
1,断线服务器即清理相关信息,重新连接上来之后,会再次订阅。
Will Flag
定义了客户端(没有主动发送DISCONNECT消息)出现网络异常导致连接中断的情况下,服务器需要做的一些措施。
简而言之,就是客户端预先定义好,在自己异常断开的情况下,所留下的最后遗愿(Last Will),也称之为遗嘱(Testament)。 这个遗嘱就是一个由客户端预先定义好的主题和对应消息,附加在CONNECT的可变头部中,在客户端连接出现异常的情况下,由服务器主动发布此消息。
只有在Will Flag位为1时,Will Qos和Will Retain才会被读取,此时消息体payload中要出现Will Topic和Will Message具体内容,否则,Will QoS和Will Retain值会被忽略掉。
Will Qos
两位表示,和PUBLISH消息固定头部的QoS level含义一样。这里先掠过,到PUBLISH消息再回过头来看看,会更明白些。
若标识了Will Flag值为1,那么Will QoS就会生效,否则会被忽略掉。
Will RETAIN
如果设置Will Flag,Will Retain标志就是有效的,否则它将被忽略。
当客户端意外断开服务器发布其Will Message之后,服务器是否应该继续保存。这个属性和PUBLISH固定头部的RETAIN标志含义一样,这里先掠过。
User name 和 password Flag:
用于授权,两者要么为0要么为1,否则都是无效。否则认为协议错误,平台将会断开连接。都为0,表示客户端可自由连接/订阅,都为1,表示连接/订阅需要授权。
|
Description |
是否必须存在 |
格式 |
Field1 |
Client Identifier |
是 |
2字节字串长度 + utf8字串 |
Field2 |
UserName |
是 |
2字节字串长度 + utf8字串 |
Field3 |
UserPassword |
是 |
2字节字串长度 + utf8字串 |
与鉴权相关的字段包含client id,username和password,支持鉴权方式。
设备ID + APIKey(项目ID也需要填写)
字段设置 |
消息示例 |
client_id设置为平台创建设备时的设备id username设置为“项目ID” password设置为“鉴权信息(auth_info)” |
client_id=”123” username=”433223” password=Api Key |
项目ID:在平台添加项目时平台生成的ID
APIKey:在平台上创建产品时生成的APIKey
2.4 代码流程
MQTT_PacketConnect()是组Connect数据包的过程。按照上面说的各个不同字段意思进行组建。
接收到CONNECT消息之后,服务器应该返回一个CONNACK消息作为响应。
Bit |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
byte 1 |
MQTT Packet Type |
0 |
0 |
0 |
0 |
|||
byte2 - 5 |
Remaining Length(该字段占用1-4个字节) |
|
Description |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
byte 1 |
Acknowledge Flags |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
Sp |
byte 2 |
Return Code |
x |
x |
x |
x |
x |
x |
x |
x |
返回码说明:
返回码 |
描述 |
0 |
成功 |
1 |
协议版本错误 |
2 |
非法的clientid |
3 |
服务不可用 |
4 |
用户名或密码错误 |
5 |
非法链接(比如token非法) |
失败:
如果connect包不符合协议内容约束,则直接断掉连接,而不需要发送connack包.
如果鉴权或授权失败,回复一个带非0错误码的connack包.
成功:
必须断掉重复的clientid.
执行cleansession 对应的操作.
必须回复一个connack,回复码为0.
开始消息传递,并加入keepalive的监视.
PS:客户端需要等到服务端的connack报文,才能发送后续的数据包.
当和平台连接成功后,每隔一段时间clint要向server发送pingreq,证明还连接着。一共两个字节。
客户端会在一个心跳周期内发送一条PINGREQ消息到服务器端。
服务器收到PINGREQ请求之后,会立即响应一个两个字节固定格式的PINGRESP消息
服务器一般若在1.5倍的心跳周期内接收不到客户端发送的PINGREQ,可考虑关闭客户端的连接描述符。此时的关闭连接的 行为和接收到客户端发送DISCONNECT消息的处理行为一致,但对客户端的订阅不会产生影响(不会清除客户端订阅数 据).
若客户端发送PINGREQ之后的一个心跳周期内接收不到PINGRESP消息,可考虑关闭TCP/IP套接字连接。