MQTT Client&Broker链接分析

连接流程

MQTT协议,Client需要与Broker进行数据通信,如订阅或者发布信息时,需要主动发起通信建立连接。如下图所示,流程简单,双方建立TCP连接后:

  1. Client向Broker发送CONNECT报文。
  2. Broker接收到Connect报文后,向Client发送CONACK报文。

MQTT Client&Broker链接分析

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》