MQTT Client&Broker链接分析
连接流程
MQTT协议,Client需要与Broker进行数据通信,如订阅或者发布信息时,需要主动发起通信建立连接。如下图所示,流程简单,双方建立TCP连接后:
- Client向Broker发送CONNECT报文。
- Broker接收到Connect报文后,向Client发送CONACK报文。
CONNECT报文组成
CONNECT报文共3个字段。
字段 | 描述 |
---|---|
Fixed Header | 固定包头 |
Variable Header | 可变包头 |
Payload | 有效载荷 |
Fixed Header
字段 | 描述 |
---|---|
byte1 | bit7-4: 0001 bit3-0:Reserved |
byte2 | 剩余长度,可变 |
Variable Header
CONNECT报文的可变报头共四个字段,协议名(Protocol Name),协议级别
(Protocol Level),连接标志(Connect Flags)和保持连接(Keep Alive)。
字段 | 描述 |
---|---|
Protocol Name | 协议名 |
Protocol Level | 协议级别 |
Connect Flags | 链接标志 |
KeepAlive | 链接保活 |
Protocol Name
协议名采用UTF-8编码
字段 | 描述 |
---|---|
byte1 | MLB |
byte2 | SLB |
byte3… | 协议名:‘MQIsdp’(3.1)‘MQTT’(3.1.1) |
3.1版本,Protocol Name为’MQIsdp‘,3.1.1版本,Protocol Name为’MQTT‘。
接收到Connect报文的Broker,可检查该字段是否与自身兼容,不兼容可抛弃该报文,拒绝连接。
Protocol Level
字段 | 描述 |
---|---|
byte7(3.1.1版本,该字段为第7个字节) | 协议级别 |
3.1版本,协议级别为0x03。
3.1.1版本,协议级别为0x04。
Protocol Flags
连接标志字节包含一些用于指定MQTT连接行为的参数。它还指出有效载荷中的字段是否存在。
bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 |
---|---|---|---|---|---|---|---|
UserName Flag | Password Flag | Will Retain | Will QoS | Will QoS | Will Flag | Clean Session | Reserved |
x | x | x | x | x | x | x | 0 |
Clean Session
Clean Session指定了会话状态的处理方式,用于控制会话状态的生存时间。
-
CleanSession被设置为0,Broker如果已经存储在了相同Clientid(MQTT用clientid标识会话)的会话,必须基于该会话的状态恢复通信。如果没有相同Clientid的会话,Broker必须创建一个新的会话。
当清理会话标识为0的连接断开后,Broker端必须保存该会话信息,同时,Client已经订阅的的QoS1和QoS2级别的消息,也需要保存在会话状态中。
-
CleanSession被设置为1,Client端和Broker端必须丢弃之前的任何会话,并创建一个新的会话。会话仅持续和网络连接同样长的时间。
Client端的会话状态包括:
1 已经发送给Broker,但是还没有完成确认的QoS 1和QoS 2级别的消息
2 已从Broker接收,但是还没有完成确认的QoS 2级别的消息。
Broker端的会话状态包括:
1 会话是否存在,即使会话状态的其它部分都是空。
2 Client的订阅信息。
3 已经发送给Client,但是还没有完成确认的QoS 1和QoS 2级别的消息。
4 即将传输给Client的QoS 1和QoS 2级别的消息。
5 已从Client接收,但是还没有完成确认的QoS 2级别的消息。
6 可选,准备发送给Client的QoS 0级别的消息。
保留消息不是Broker会话状态的一部分,会话终止时不能删除保留消息 。Will Flag
-
遗嘱标志(Will Flag)被设置为1,表示如果连接请求被接受了,遗嘱(Will Message)消息必须被存储在Broker端,并且与这个会挂关联。当该会话关闭时,Broker端必须迅速发布这个遗嘱消息,除非Broker收到DISCONNECT报文时删除了这个遗嘱消息 。在Broker关机或者故障的情况下,可以在重启之后发布遗嘱消息。
遗嘱消息发布的条件,包括但不限于:
1 Broker检测到了一个I/O错误或者网络故障。
2 Client在保持连接(Keep Alive)的时间内未能通讯。
3 Client没有先发送DISCONNECT报文直接关闭了网络连接。
4 由于协议错误Broker关闭了网络连接。
5 如果遗嘱标志被设置为1,连接标志中的Will QoS和Will Retain字段会被Broker用到,同时有效载荷中必须包含Will Topic和Will Message字段 。
一旦被发布或者Broker收到了Client发送的DISCONNECT报文,遗嘱消息就必须从存储的会
话状态中移除 。 -
如果遗嘱标志被设置为0,连接标志中的Will QoS和Will Retain字段必须设置为0,并且有效载
荷中不能包含Will Topic和Will Message字段 。
Will QoS
这两位用于指定发布遗嘱消息时使用的服务质量等级。如果遗嘱标志被设置为0,遗嘱QoS也必须设置为0(0x00) 。如果遗嘱标志被设置为1,遗嘱QoS的值可以等于0(0x00),1(0x01),2(0x02),它的值不能等
于3 。
Will Retain
如果遗嘱消息被发布时需要保留,需要指定这一位的值。
如果遗嘱标志被设置为0,遗嘱保留(Will Retain)标志也必须设置为0 。
如果遗嘱标志被设置为1:
如果遗嘱保留被设置为0,Broker必须将遗嘱消息当作非保留消息发布 。
如果遗嘱保留被设置为1,Broker必须将遗嘱消息当作保留消息发布 。
Password Flag
如果密码(Password)标志被设置为0,有效载荷中不能包含密码字段 。
如果密码(Password)标志被设置为1,有效载荷中必须包含密码字段 。
如果用户名标志被设置为0,密码标志也必须设置为0 。
UserName Flag
如果用户名(User Name)标志被设置为0,有效载荷中不能包含用户名字段 。
如果用户名(User Name)标志被设置为1,有效载荷中必须包含用户名字段 。
KeepAlive
字段(3.1.1版本,该字段为第7、8个字节) | 描述 |
---|---|
byte8 | KeepAlive MLB |
byte9 | KeepAlive SLB |
保持连接(Keep Alive)是一个以秒为单位的时间间隔,指在Client发送的两个报文的最小间隔时间。如果任何其它的控制报文可以发送,Client必须发送一个PINGREQ报文 。不管保持KeepALive的值是多少,Client任何时候都可以发送PINGREQ报文,Broker则回应PINGRESP报文,Client可根据PINGRESP判断网络和Broker的活动状态。
如果KeepAlive的值非零,Broker在1.5倍的保持连接时间内没有收到Client的任何报文,则认为网络连接已经断开,必须断开该Client。Client发送了PINGREQ报文之后,如果在合理的时间内仍没有收到PINGRESP报文,则应该关闭到Broker的网络连接。
如果KeepALive的值为零,表示关闭保持连接功能。Broker不需要因为Client不活跃而断开连接。
注意:不管保持连接的值是多少,任何时候,只要Broker认为Client是不活跃或无响应的,都可断开Client的连接。
Payload
CONNECT报文的有效载荷(payload)包含一个或多个UTF-9编码的字符字段,可变报头中的Protocol Flag决定是否包含这些字段。如果包含的话,必须按这个顺序出现:clientid(Client标识符),Will Topic(遗嘱主题),Will Message(遗嘱消息),User Name(用户名),Password(密码) 。
Client Identifier
Broker使用ClientId 识别Client,连接Broker的每个Client都有唯一的标识符(ClientId)。Client和Broker都必须使用ClientId识别两者之间的MQTT会话相关的状态 .CONNECT报文中必须包含ClientId字段,必须是CONNECT报文Payload的第一个字段 。
Client标识符必须是UTF-8编码字符队列。
Broker必须允许1到23个字节长的UTF-8编码的Clientid,Clientid只能包含以下字符:“0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ”(大
写字母,小写字母和数字)。Broker也可以允许编码后超过23个字节的Client标识符 (ClientId)。Broker也可以允许包含不是上面列表字符的Client标识符 (ClientId)。
Broker可以允许Client提供一个零字节的Client标识符 (ClientId) ,如果出现这种情况,Broker必须作为特殊情况并分配唯一的Client标识符给那个Client,且必须假设Client提供了那个唯一的Client标识符,正常处理这个CONNECT报文 。
如果Client提供了一个零字节的Client标识符,它必须同时将清理会话标志设置为1 。
如果Client提供的ClientId为零字节且清理会话标志为0,Broker必须发送返回码为0x02(表
示标识符不合格)的CONNACK报文,然后关闭网络连接。
如果Broker拒绝了这个ClientId,它必须发送返回码为0x02(表示标识符不合格)的
CONNACK报文,然后关闭网络连接 。
Will Topic
如果遗嘱标志被设置为1,有效载荷的下一个字段是遗嘱主题(Will Topic)。遗嘱主题必须是UTF-8编码字符串 。
Will Message
如果遗嘱标志被设置为1,有效载荷的下一个字段是遗嘱消息。遗嘱消息定义了将被发布到遗嘱主题的消息。这个字段由一个两字节的长度和遗嘱消息的有效载荷组成,表示为零字节或多个字节序列。
遗嘱消息被发布到遗嘱主题时,它的有效载荷只包含这个字段的数据部分,不包含开头的两个长度字节。
User Name
如果用户名(User Name)标志被设置为1,有效载荷的下一个字段就是用户名。用户名必须是UTF-8编码字符串 。Broker可以将它用于身份验证和授权。
Password
如果密码(Password)标志被设置为1,有效载荷的下一个字段就是它。密码字段包含一个两字节的长度字段,长度表示字段数据的字节数(不包含长度字段本身占用的两个字节),后面跟着0到65535字节的字段数据。
字段 | 描述 |
---|---|
byte1 | MLB |
byte2 | SLB |
byte3… | 数据部分 |
CONACK报文组成
Broker发送CONNACK报文以响应收到的CONNECT报文。Broker发送给Client的第一个报文必须是CONNACK 。
如果Client在合理的时间内没有收到Broker的CONNACK报文,Client应该关闭网络连接,合理 的时间取决于应用的类型和通信基础设施。CONNACK报文无有效载荷。
字段 | 描述 |
---|---|
Fixed Header | 固定包头 |
Variable Header | 可变报头 |
Fixed Header
字段 | 描述 |
---|---|
byte1 | bit7-4: 0002 bit3-0:Reserved |
byte2 | 剩余长度,对于conack报文,剩余长度为2 |
Variable Header
字段 | 描述 |
---|---|
byte1 | 连接确认标识,bit7-bit1:Reserved bit0: Session Present |
byte2 | 连接返回码 |
连接确认标识
byte1,连接确认标识,只有bit0被使用,为Session Present,即当前会话标识。
如果Broker收到清理会话(CleanSession)标志为1的连接,除了将CONNACK报文中的返回码设置为0之外,还必须将CONNACK报文中的当前会话设置(Session Present)标志设为0。
如果Broker收到一个CleanSession为0的连接,当前会话标志的值取决于Broker是否已经保存了ClientId对应Client的会话状态。如果Broker已经保存有相同clientid的会话,它必须将CONNACK报文中的当前会话标志设置为1 。如果Broker没有已保存的会话状态,它必须将CONNACK报文中的当前会话设置为0,还需要将CONNACK报文中的返回码设置为0 。
当前会话标志使Broker和Client在是否有已存储的会话状态上保持一致。一旦完成了会话的初始化设置,已经保存会话状态的Client将期望Broker维持它存储的会话状态。如果Client从Broker收到的当前的值与预期的不同,Client可以选择继续这个会话或者断开连接。Client可以丢弃Client和Broker之间的会话状态,方法是,断开连接,将清理会话标志设置为1,再次连接,然后再次断开连接。如果Broker发送了一个包含非零返回码的CONNACK报文,它必须将当前会话标志设置为0。
连接返回码
连接返回码字段使用一个字节的无符号值。如果Broker收到一个合法的CONNECT报文,但出于某些原因无法处理它,Broker应该发送一个包含非零返回码的CONNACK报文。如果Broker发送了一个包含非零返回码的CONNACK报文,那么必须关闭网络连接 。
返回码 | 描述 |
---|---|
0 | 连接被Broker接收 |
1 | 不支持Client的MQTT协议级别 |
2 | 不合格的clientid,Broker不允许使用 |
3 | 网络连接已建立,但MQTT服务不可用 |
4 | 用户名或者密码的数据格式无效 |
5 | Client未被授权连接到此Broker |
6-255 | 保留 |
引用
1 《MQTT协议中文版本》:https://mcxiaoke.gitbooks.io/mqtt-cn/content/
2 《MQTT Version 3.1.1》