一.CONNECT报文

客户端与服务器建立网络连接后,客户端发送给服务器的第一个报文必须是CONNECT报文。在一个连接上,客户端只能发送一次CONNECT报文,如果客户端又再一次发送了CONNECT报文,服务器会把它当违规并断开客户端。有效载荷包含一个或多个的字段,包括客户端标识符,Will主题,Will消息,用户名和密码,这些除了客户端标识符之外,其它的字段都是可选的,基于标志位来决定可变报头中是否需要包含这些字段。

1、固定报头
MQTT协议(二)
2、可变报头
CONNECT报文的可变报头包含四个字段:协议名(Protocol Name),协议级别(Protocol Level),连接标志(Connect Flags)和保持连接(Keep Alive)

(1)  协议名(Protocol Name)
MQTT协议(二)
协议名是UTF-8编码的字符串。MQTT后续版本不会改变这个字符串和长度。如果协议名不正确服务器可以断开客户端的连接,也可以按照其它规范继续处理这个CONNECT报文

(2)  协议级别(Protocol Level)
MQTT协议(二)
客户端用8位的无符号值表示协议的版本。对于3.1.1协议,协议级别的值是4,如果发现不支持的协议级别,服务端必须给发送一个返回码为0x01(不支持该协议级别)的CONNACK报文响应CONNECT报文,然后断开客户端的连接

(3) 连接标志(Connect Flags)
MQTT协议(二)
连接标志包含一些用于指定MQTT连接行为的参数,它还指出有效载荷中的字段是否存在,服务器必须验证CONNECT控制报文的连接标志第0位是否为0,如果不为0必须断开客户端连接,具体说明如下:

Clean Session:这个标志位用于控制会话的生存时间。如果清理会话(CleanSession)标志被设置为0,并且和这个客户端标识符有关联的会话服务器必须恢复当前会话(使用客户端标识符识别)的状态与客户端的通信;如果还没有和这个客户端标识符有关联的会话,服务端必须创建一个新的会话,并如果在连接断开之后,客户端和服务端必须保存相关的会话信息,服务器必须将之后的QoS1和QoS2级别的消息会话状态保存起来,当然服务器也可以保存满足相同条件的QoS0级别的消息。如果清理会话(CleanSession)标志被设置为1,客户端和服务器必须丢弃之前的会话并开始一个新的会话,这时会话仅持续和网络连接一样长的时间

客户端的会话状态包括:

  • 已经发送给服务器,但是还没有完成确认的QoS1和QoS2级别的消息
  • 已从服务器接收,但是还没有完成确认的QoS2级别的消息

服务端的会话状态包括:

  • 会话是否存在,即使会话状态都是空
  • 客户端的订阅信息
  • 已经发送给客户端,但是还没有完成确认的QoS1和QoS2级别的消息
  • 即将传输给客户端的QoS1和QoS2级别的消息
  • 已从客户端接收,但是还没有完成确认的QoS2级别的消息
  • 可选,准备发送给客户端的QoS0级别的消息

PS:保留消息不是服务器会话状态的一部分,会话终止时不能删除保留消息,一般来说, 客户端连接时清理会话标志设置为0或1,清理会话标志设置为1的客户端不会收到旧的应用消息, 而且在每次连接成功后都需要重新订阅任何相关的主题。 清理会话标志设置为0的客户端会收到所有在它连接断开期间发布的QoS1和QoS2级别的消息。 因此, 要确保不丢失连接断开期间的消息, 需要使用QoS1或QoS2级别, 同时将清理会话标志设置为0。 当客户端决定之后不再使用这个会话时,应该将清理会话标志设置为1再连接一次, 然后断开连接,这时会把会话状态清除

Will Flag:如果遗嘱标志Will Flag被设置为1, 遗嘱消息Will Message必须被存储在服务器上,在之后客户端网络连接关闭时服务器必须发布这个遗嘱消息, 除非服务器收到DISCONNECT报文时删除了这个遗嘱消息。如果遗嘱标志被设置为1, 连接标志中的Will QoS和Will Retain字段会被服务器使用到, 同时CONNECT报文中的有效载荷中必须包含Will Topic和Will Message字段, 遗嘱消息一旦被发布或者服务器收到了客户端发送的DISCONNECT报文, 遗嘱消息就必须从存储的会话状态中移除。如果遗嘱标志被设置为0, 连接标志中的Will QoS和Will Retain字也必须设置为0, 并且CONNECT报文中的有效载荷中不能包含Will Topic和Will Message字段。服务端应该迅速发布遗嘱消息,但在关机或故障的情况下,服务器可以推迟遗嘱消息的发布直到之后的重新启动

Will Qos:一共两位,用于指定发布遗嘱消息时使用的服务质量等级。如果遗嘱标志被设置为0, 遗嘱QoS也必须设置为0 。如果遗嘱标志被设置为1, 遗嘱QoS的值可以等于0、1、 2

Will Retain:如果遗嘱消息被发布时需要保留需要指定这一位的值,如果遗嘱标志被设置为0, 遗嘱保留Will Retain标志也必须设置为0。如果遗嘱保留被设置为0,服务器必须将遗嘱消息当作非保留消息发布,如果遗嘱保留被设置为1,服务器必须将遗嘱消息当作保留消息发布

User Name Flag:如果用户名( User Name) 标志被设置为0,有效载荷中不能包含用户名字段,如果用户名( User Name) 标志被设置为1,有效载荷中必须包含用户名字段

Password Flag:如果密码( Password) 标志被设置为0, 有效载荷中不能包含密码字段,如果密码( Password) 标志被设置为1, 有效载荷中必须包含密码字段,如果用户名标志被设置为0, 密码标志也必须设置为0

(4) 保持连接(Keep Alive)
MQTT协议(二)
保持连接(Keep Alive)是一个以秒为单位的时间间隔, 它是指在客户端传输两个控制报文的之间允许的最大时间间隔。 客户端要保证控制报文发送的时间间隔不超过保持连接的值。 如果没有任何其它的控制报文可以发送, 客户端必须发送一个PINGREQ报文给服务器,而且客户端任何时候都可以发送PINGREQ报文,并且可以使用PINGRESP报文判断网络和服务器的活动状态。如果保持连接的值非零,并且服务器在一点五倍的保持连接时间内没有收到客户端的任何控制报文,它必须断开客户端网络连接, 认为网络连接已断开 。客户端发送了PINGREQ报文之后,如果在合理的时间内仍没有收到PINGRESP报文,它应该关闭到服务器的网络连接。保持连接的值为零表示关闭保持连接功能, 这意味着, 服务器不需要因为客户端不活跃而断开连接。

注意: 不管保持连接的值是多少, 任何时候只要服务端认为客户端是不活跃或无响应的,可以断开客户端的连接。保持连接的实际值是由应用指定的, 一般是几分钟,允许的最大值是18小时12分15秒

示例
MQTT协议(二)
3、有效载荷
CONNECT报文的有效载荷payload包含一个或多个以长度为前缀的字段, 可变报头中的标志决定是否包含这些字段: 客户端标识符, 遗嘱主题, 遗嘱消息, 用户名, 密码

(1) 客户端标识符:服务器使用客户端标识符 (ClientId) 识别客户端。 连接服务器的每个客户端都有唯一的客户端标(ClientId),客户端和服务器都使用ClientId识别两者之间的MQTT会话状态。客户端标识符 (ClientId) 必须存在而且必须是CONNECT报文有效载荷的第一个字段,服务器允许1到23个字节长的UTF-8编码的客户端标识符, 在客户端标识符只能包含这些字符: 大字母和数字,服务器可以允许客户端提供一个零字节的客户端标识符 (ClientId) ,这时服务器会为客户端分配唯一的客户端标识符并正常处理这个CONNECT报文,如果客户端提供了一个零字节的客户端标识符, 它必须同时将清理会话标志设置为1,如果客户端提供的ClientId为零字节且清理会话标志为0, 服务器必须发送返回码为0x02(表示标识符不合格)的CONNACK报文来响应客户端, 然后关闭网络连接,另一方面如果服务器拒绝了这个ClientId,它必须发送返回码为0x02(表示标识符不合格)CONNACK报文响应客户端然后关闭网络连接。总之,客户端实现可以提供一个方便的方法用于生成随机的ClientId,但当清理会话标志被设置为0时应该主动放弃使用这种方法

(2) 遗嘱主题:如果遗嘱标志被设置为1,有效载荷的下一个字段是遗嘱主题(Will Topic),遗嘱主题必须是UTF-8编码字符串

(3) 遗嘱消息:如果遗嘱标志被设置为1, 有效载荷的下一个字段是遗嘱消息,遗嘱消息定义了将被发布到遗嘱主题的应用消息,  这个字段由长度为两字节和遗嘱消息的有效载荷组成, 长度给出了跟在后面的数据的字节数, 不包含长度字段本身占用的两个字节。遗嘱消息被发布到遗嘱主题时, 它的有效载荷只包含这个字段的数据部分, 不包含开头的两个长度的字节

(3) 用户名:如果用户名(User Name)标志被设置为1, 有效载荷的下一个字段就是它, 用户名必须是UTF-8编码字符串,服务器可以将它用于身份验证和授权

(3) 密码:如果密码(Password)标志被设置为1, 有效载荷的下一个字段就是它,密码字段包含两字节的长度字段, 长度表示二进制数据的字节数( 不包含长度字段本身占用的两个字节),后面跟着0到65535字节的二进制数据
MQTT协议(二)

4、连接后的响应Response
如果服务器确定协议是MQTT 3.1.1, 那么它按照下面的方法验证连接请求。
(1) 网络连接建立后, 如果服务端在合理的时间内没有收到CONNECT报文, 服务端应该关闭这个连接。
(2) 服务端必须按照要求验证CONNECT报文, 如果报文不符合规范, 服务端不发送CONNACK报文直接关闭网络连接
(3) 服务端可以检查CONNECT报文的内容是不是满足任何进一步的限制, 可以执行身份验证和授权检查,如果任何一项检查过, 它应该发送一个适当的、返回码非零的CONNACK响应, 并且必须关闭这个网络连接。

如果验证成功, 服务端会执行下列步骤。
(1) 如果ClientId表明客户端已经连接到这个服务端, 那么服务端必须断开原有的客户端连接
(2) 服务端必须执行清理会话的过程
(3) 服务端必须发送返回码为零的CONNACK报文作为CONNECT报文的确认响应
(4) 开始消息分发和保持连接状态监视

PS:允许客户端在发送CONNECT报文之后立即发送其它的控制报文; 客户端不需要等待服务端的CONNACK报文。 如果服务端拒绝了CONNECT, 它不能处理客户端在CONNECT报文之后发送的任何数据客户端通常会等待一个CONNACK报文。 然而客户端有权在收到CONNACK之前发送控制报文, 由于不需要维持连接状态, 这可以简化客户端的实现

二.CONNACK报文

服务器发送CONNACK报文响应客户端CONNECT报文。 服务器发送给客户端的第一个报文必须是CONNACK。如果客户端在合理的时间内没有收到服务端的CONNACK报文, 客户端应该关闭网络连接

1、固定报头
MQTT协议(二)
剩余长度字段表示可变报头的长度, 对于CONNACK报文这个值等于2

2、可变报头
MQTT协议(二)
(1) 连接确认标志 Connect Acknowledge Flags:位7-1是保留位且必须设置为0,第0位(SP)是当前会话(Session Present)标志。如果服务器收到清理会话(CleanSession)标志为1的连接,除了将CONNACK报文中的返回码设置为0之外,还必须将CONNACK报文中的当前会话设置(Session Present) 标志为0,如果服务器收到一个CleanSession为0的连接, 当前会话标志的值取决于服务端是否已经保存了ClientId对应客户端的会话状态。如果服务器已经保存了会话状态, 它必须将CONNACK报文中的当前会话标志设置为1 。如果服务器没有已保存的会话状态,它必须将CONNACK报文中的当前会话设置为0,当然还需要将CONNACK报文中的返回码设置为0 ,如果服务端发送了一个包含非零返回码的CONNACK报文,它必须将当前会话标志设置为0

(2) 连接返回码 Connect Return code:连接返回码字段使用一个字节的无符号值。如果服务器收到一个合法的CONNECT报文, 但出于某些原因无法处理它, 服务器应该尝试发送一个包含非零返回码(表格中的某一个) 的CONNACK报文,如果服务器发送了一个包含非零返回码的CONNACK报文, 那么它必须关闭网络连接
MQTT协议(二)
如果认为上表中的所有连接返回码都不太合适, 那么服务端必须关闭网络连接, 并不需要发送CONNACK报文

相关文章: