物联网之MQTT3.1.1和MQTT5协议 (2) CONNECT报文

前言

本篇博文针对MQTT控制报文进行阐述,将MQTT5和MQTT3.1.1的不同部分进行细分。

CONNECT –连接请求

客户端与服务器建立网络连接后,从客户端发送至服务器的第一个数据报文必须为CONNECT数据报文

客户端只能通过网络连接发送一次CONNECT数据报文。 服务器必须处理从客户端发送的第二个CONNECT数据报文,作为协议冲突,并断开客户端的连接

有效载荷包含一个或多个编码字段。 它们为客户端指定唯一的客户端标识符,“遗嘱”主题,“遗嘱消息”,“用户名”和“密码”。 除客户端标识符外,所有标识符都是可选的,它们的存在是根据可变报头中的标志位确定的。

固定报头

图例 CONNECT报文的固定报头

在这里插入图片描述

剩余长度字段

剩余长度等于可变报头的长度加上有效载荷的长度。编码方式为可变字节整数。

可变报头

CONNECT 报文的可变报头按下列次序包含四个字段:协议名(Protocol Name),协议级别(Protocol Level),连接标志(Connect Flags),保持连接(Keep Alive)。MQTT5.0版本则在此基础加上属性(Properties)字段

协议名

图例 协议名字节构成
在这里插入图片描述

协议名称是UTF-8编码的字符串,代表协议名称“ MQTT”。 字符串,其偏移量和长度将不会在MQTT规范的未来版本中更改。

如果协议名称不正确,服务器可以断开客户端的连接,或者可以继续按照其他规范处理CONNECT数据报文。 在后一种情况下,服务器不得根据MQTT的规范继续处理CONNECT数据报文

针对MQTT5.0版本变化

支持多种协议的服务器使用协议名称来确定数据是否为MQTT。 协议名称必须为UTF-8字符串“ MQTT”。 如果服务器不希望接受CONNECT,并希望表明它是MQTT服务器,则可以发送原因码为0x84(不支持的协议版本)的CONNACK数据报文,然后必须关闭网络连接

数据报文检测工具,例如防火墙,可以使用协议名来识别MQTT流量。

协议级别(版本)

在这里插入图片描述

客户端用8位的无符号值表示协议的修订版本。对于3.1.1版协议,协议级别字段的值是4(0x04)。MQTT v5.0的协议版本字段为5(0x05)。

支持多版本MQTT协议的服务端使用协议版本字段判定客户端正使用的MQTT协议版本

针对MQTT3.1.1版本如果发现不支持的协议级别,服务端必须给发送一个返回码为0x01(不支持的协议级别)的CONNACK报文响应CONNECT报文,然后断开客户端的连接

针对MQTT5.0版本如果协议版本不是5且服务端不愿意接受此CONNECT报文,可以发送包含原因码0x84(不支持的协议版本)的CONNACK报文,然后必须关闭网络连接

连接标志

连接标志字节包含一些用于指定MQTT连接行为的参数。它还指出有效载荷中的字段是否存在。

图例 连接标志位

在这里插入图片描述

服务端必须验证CONNECT控制报文的保留标志位(第0位)是否为0,如果不为0说明是无效报文,并关闭连接

清理会话(MQTT3.3.1,Clean Session)

位置:连接标志字节的第1位

这个二进制位指定了会话状态的处理方式。

客户端和服务器可以存储会话状态,以使可靠的消息传递能够在一系列网络连接中继续进行。 该位用于控制会话状态的生存期。

如果Clean Session设置为0,则服务器务必根据当前会话的状态(由客户端标识符标识)恢复与客户端的通信。 如果没有与客户端标识符相关的会话,则服务器必须创建一个新的会话。 客户端和服务器断开连接后,客户端和服务器必须存储会话。 在将Clean Session设置为0的会话断开连接后,服务器务必须将与断开连接时客户端具有的所有订阅相匹配的其他QoS 1和QoS 2消息作为会话状态的一部分。 还可以存储满足相同条件的QoS 0消息。

客户端中的会话状态包括

  • 已发送到服务器但尚未完全确认的QoS 1和QoS 2消息。
  • 已从服务器收到但未完全确认的QoS 2消息。

服务器中的会话状态包括:

  • 会话的存在,即使会话的其余状态为空。
  • 客户的订阅。
  • 已发送到客户端但尚未完全确认的QoS 1和QoS 2消息。
  • 等待传输到客户端的QoS 1和QoS 2消息。
  • 已经从客户端收到但尚未完全确认的QoS 2消息。
  • (可选)等待传输到客户端的QoS 0消息。

保留消息不构成服务器中会话状态的一部分,在会话结束时不得删除它们

当Clean Session设置为1时,客户端和服务器无需自动处理状态删除。

为了确保发生故障时状态保持一致,客户端应重复尝试将Clean Session设置为1进行连接,直到成功连接为止。

通常,客户端将始终使用设置为0的CleanSession或设置为1的CleanSession进行连接,并且不交替使用这两值。选择将取决于应用程序。使用Clean Session设置为1的客户端将不会收到旧的应用程序消息,并且必须在每次连接时重新订阅其感兴趣的任何主题。使用Clean Session设置为0的客户端将收到断开连接时发布的所有QoS 1或QoS 2消息。因此,为确保断开连接时不会丢失消息,请使用Clean Session设置为0的QoS 1或QoS 2。

当客户端将Clean Session设置为0连接时,它要求服务器在断开连接后保持其MQTT会话状态。如果客户端打算在以后的某个时间点重新连接到服务器,则客户端应仅将Clean Session设置为0进行连接。当客户端确定该会话不再使用该客户端时,应在Clean Session设置为1的情况下进行最终连接,然后断开连接。

新开始(MQTT5.0,Clean Start)

位置:连接标志字节的第1位

该位指定连接是启动新会话还是现有会话的延续。

如果在Clean Start设置为1的情况下接收到CONNECT数据包,则客户端和服务器务必丢弃任何现有会话并开始新的会话。 因此,如果“Clean Start”设置为1,则CONNACK中的“Session Present”标志始终设置为0。

如果接收到的CONNECT数据包的Clean Start设置为0,并且有一个与客户端标识符相关联的会话,则服务器必须根据现有会的状态恢复与客户端的通信。 如果接收到的CONNECT数据包的Clean Start设置为0,并且没有与客户端标识符关联的会话,则服务器必须创建一个新的会话

备注:把Clean Start标志设置为1且会话过期间隔设置为0,等同于在MQTT v3.1.1中把清理会话(Clean Session)设置为1。会话过期间隔时间设置是在属性列表中存在。–qiwei.tan

遗嘱标志

位置:连接标志字节的第2位

如果将Will Flag设置为1

【MQTT3.1.1】

表示如果接受了连接请求,则遗嘱(Will Message)消息必须被存储在服务端并且与这个网络连接关联。除非网络在收到DISCONNECT数据包后删除了遗嘱消息,否则必须在随后关闭网络连接时发布遗嘱消息

【MQTT 5.0】

表示遗嘱消息必须已存储在服务端与此客户标识符相关的会话中。遗嘱必须在网络连接被关闭、遗嘱延时间隔到期或者会话结束之后被发布,除非服务端收到包含原因码为0x00(正常关闭)的DISCONNECT报文之后删除了遗嘱消息(Will Message),或者一个关于此客户标识符的新的网络连接在遗嘱延时间隔(Will Delay Interval)超时之前被创建

备注:遗嘱延时间隔是属性的一种,在MQTT5.0中定义。

MQTT5.0标注

遗嘱消息包含遗嘱属性,遗嘱主题和遗嘱载荷字段。

发布遗嘱消息的情况包括但不限于:

  • 服务器检测到I / O错误或网络故障。

  • 客户端在保持活动时间内无法通信。

  • 客户端没有先发送DISCONNECT数据报文直接关闭网络连接。【MQTT3.1.1】

    MQTT 5.0: 客户端在没有发送包含原因码0x00(正常关闭)的DISCONNECT 报文的情况下关闭了网络连接

  • 由于协议错误,服务器关闭了网络连接。【MQTT3.1.1】

    MQTT 5.0: 服务端在没有收到包含原因码0x00(正常关闭)的DISCONNECT 报文的情况下关闭了网络连接。与MQTT3.1.1关闭连接的情况不一样,要先决条件

【MQTT3.1.1】

如果将Will Flag设置为1,则服务器将使用Connect Flag中的Will QoS和Will Retain字段,并且有效负载中必须存在Will Topic和Will Message字段。一旦发布了Will消息,或者服务器从客户端接收到DISCONNECT数据包,则必须将遗嘱消息从服务器中的已存储会话状态中删除

如果将Will标志设置为0,则必须将连接标志中的Will QoS和Will Retain字段设置为零,并且在有效载荷中不得出现Will Topic和Will Message字段

如果将Will标志设置为0,则在此网络连接结束时不得发布遗嘱消息

服务器应该及时发布遗嘱消息。在服务器关闭或失败的情况下,服务器可以将Will Messages的发布推迟到随后的重启。如果发生这种情况,则在服务器出现故障与发布Will消息之间可能会有延迟。

【MQTT 5.0】

如果遗嘱标志(Will Flag)被设置为1,遗嘱属性(Will Property)、遗嘱主题(Will Topic)和遗嘱载荷(Will Payload)字段必须存在于报文有效载荷中。一旦遗嘱消息(Will Message)被发布或者服务端收到来自于客户端发送的包含原因码为0x00(正常关闭)的DISCONNECT报文,遗嘱消息(Will Message)必须从服务端的会话中删除

在网络连接关闭且经过“ Will Delay Interval”之后或会话结束时,服务器应立即发布“ 遗嘱”消息。 在服务器关闭或失败的情况下,服务器可以将“ 遗嘱”消息的发布推迟到随后的重启。 如果发生这种情况,则在服务器出现故障的时间与发布“遗嘱消息”之间可能会有延迟。

客户端可以通过将“ Will Delay Interval”设置为比“ Session Expiry Interval”更长,然后发送“ DISCONNECT”(原因码为0x04)(“与Will Message断开连接”),以安排“ Will Message”通知会话已经到期。

遗嘱QoS

位置:连接标志的第4位和第3位。

用于指定发布遗嘱消息时要使用的QoS等级。

如果将Will Flag设置为0,则Will QoS必须设置为0(0x00)

如果将Will Flag设置为1,则Will QoS的值可以为0(0x00),1(0x01)或2(0x02)。而3(0x03)是格式错误的数据报文。

遗嘱保留

位置:连接标志的第5位

此位指定遗嘱消息(Will Message)在发布时是否会被保留。

  • 如果遗嘱标志被设置为0,遗嘱保留(Will Retain)标志也必须设置为0
  • 如果遗嘱标志被设置为1
    • Will Retain设置为0,则服务器必须将遗嘱消息发布为非保留消息
    • Will Retain设置为1,则服务器务必发布Will消息作为保留消息

用户名标志

位置:连接标志字节的第7位

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

位置:连接标志的第6位。

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

【MQTT3.1.1】

如果用户名标志被设置为0,密码标志也必须设置为0

【MQTT 5.0】

允许在没有用户名的情况下发送密码。这表明密码除了作为口令之外还可以有其他用途。

保活(Keep Alive)

在这里插入图片描述

保活(Keep Alive)是一个以秒为单位的时间间隔,表示为一个16位的字,它是指在客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。客户端负责保证控制报文发送的时间间隔不超过保活的值。如果没有任何其它的控制报文可以发送,客户端必须发送一个PINGREQ报文

MQTT5 还额外要求Keep Alive不为零时,发送一个PINGREQ报文

【MQTT5.0】如果服务端返回的CONNACK报文中包含服务端保活(Server Keep Alive)值,客户端必须使用此值代替其发送的保活的值

不管保活的值是多少,客户端任何时候都可以发送PINGREQ报文,并且使用PINGRESP报文判断网络和服务端的活动状态。

  • 如果保持连接的值非零,并且服务端在1.5倍的保活时间内没有收到客户端的控制报文,它必须断开客户端的网络连接,认为网络连接已断开

  • 客户端发送了PINGREQ报文之后,如果在合理的时间内仍没有收到PINGRESP报文,它应该关闭到服务端的网络连接。

  • 保活值为零表示关闭保活功能。这意味着,服务端不需要因为客户端不活跃而断开连接。注意:不管保持连接的值是多少,任何时候,只要服务端认为客户端是不活跃或无响应的,可以断开客户端的连接。在MQTT5.0增加:如果保活值为零,客户端不必按照任何特定的时间发送MQTT控制报文

服务端可能因为其他原因断开客户端连接,比如服务端将要关闭服务。设置保活不保证客户端将一直保持连接状态。

保活实际值是由应用指定的,一般是几分钟。允许的最大值是18小时12分15秒。

CONNECT属性(MQTT5.0)

属性长度

CONNECT报文可变报头中的属性(Properties)长度被编码为变长字节整数。

会话过期间隔

17 (0x11)Byte,会话过期间隔(Session Expiry Interval)标识符。

跟随其后的是用四字节整数表示的以秒为单位的会话过期间隔(Session Expiry Interval)。包含多个会话过期间隔(Session Expiry Interval)将造成协议错误(Protocol Error)。

如果会话过期间隔(Session Expiry Interval)值未指定,则使用0。如果设置为0或者未指定,会话将在网络连接(Network Connection)关闭时结束。

如果会话过期间隔(Session Expiry Interval)为0xFFFFFFFF (UINT_MAX),则会话永不过期。

如果网络连接关闭时会话过期间隔(Session Expiry Interval)大于0,则客户端与服务端必须存储会话状态

客户端或服务端可能会因为中断运行导致会话时钟某些时间未运行。这将导致会话的删除被延迟。

当会话过期时,客户端和服务端无需以原子操作的方式删除会话状态。

把新开始(Clean Start)标识设置为1且会话过期间隔(Session Expiry Interval)设置为0,等同于在MQTT v3.1.1中把清理会话(CleanSession)设置为1。把新开始(Clean Start)标识设置为0且不设置会话过期间隔(Session Expiry Interval),等同于在MQTT v3.1.1中把清理会话标志设置为0。

当希望只处理连接上服务端之后才发布的消息,客户端应该把新开始(Clean Start)设置为1且会话过期间隔(Session Expiry Interval)设置为0,这样客户端就不会收到它连接之前被服务端所发布的消息,并且需要每次连接上服务端时重新订阅其感兴趣的主题。

某些客户端使用的网络可能只能提供断断续续的连接,这种客户端可以使用较短的会话过期间隔(Session Expiry Interval)以便在网络再次可用后重新连接到服务端时获得持续的消息交付。如果客户端不再重新连接,且允许会话过期,应用消息将会丢失。

某个客户端设置较长的会话过期间隔(Session Expiry Interval)或设置会话不过期,即要求服务端为其保持会话到其下一次连接上服务端之后。只有打算在一段时间之后将会重连服务端时,客户端才应该设置较长的会话过期间隔(Session Expiry Interval)。当客户端认定其将来不会使用本次会话时,应该在断开时把会话过期间隔(Session Expiry Interval)设置为0。

客户端应当使用CONNACK报文中的会话存在(Session Present)来判定服务端是否存储了其会话。

客户端应当以服务端返回的会话存在(Session Present)标志来判定会话是否已过期,而不是客户端自己实现的会话过期状态。如果客户端自己实现会话过期状态,则需要将会话应当被删除的时间作为会话状态的一部分而存储。

接收最大值

33 (0x21)Byte,接收最大值(Receive Maximum)标识符。

跟随其后的是由双字节整数表示的最大接收值。包含多个接收最大值或接收最大值为0将造成协议错误(Protocol Error)。

客户端使用此值限制客户端愿意同时处理的QoS等级1和QoS等级2的发布消息最大数量。没有机制可以限制服务端试图发送的QoS为0的发布消息。

接收最大值只将被应用在当前网络连接。如果没有设置最大接收值,将使用默认值65535。

最大报文长度

39 (0x27)Byte,最大报文长度(Maximum Packet Size)标识符。

跟随其后的是由四字节整数表示的客户端愿意接收的最大报文长度(Maximum Packet Size),如果没有设置最大报文长度(Maximum Packet Size),则按照协议由固定报头中的剩余长度可编码最大值和协议报头对数据包的大小做限制。

包含多个最大报文长度(Maximum Packet Size)或者最大报文长度(Maximum Packet Size)值为0将造成协议错误。

客户端如果选择了限制最大报文长度,应该为最大报文长度设置一个合理的值。

最大报文长度是MQTT控制报文的总长度。客户端使用最大报文长度通知服务端其所能处理的单个报文长度限制。

服务端不能发送超过最大报文长度(Maximum Packet Size)的报文给客户端。收到长度超过限制的报文将导致协议错误,客户端发送包含原因码0x95(报文过大)的DISCONNECT报文给服务端。

当报文过大而不能发送时,服务端必须丢弃这些报文,然后当做应用消息发送已完成处理

共享订阅的情况下,如果一条消息对于部分客户端来说太长而不能发送,服务端可以选择丢弃此消息或者把消息发送给剩余能够接收此消息的客户端。

服务端可以把那些没有发送就被丢弃的报文放在死信队列(dead letter queue)上,或者执行其他诊断操作。

主题别名最大值

34 (0x22)Byte,主题别名最大值(Topic Alias Maximum)标识符。

跟随其后的是用双字节整数表示的主题别名最大值(Topic Alias Maximum)。包含多个主题别名最大值(Topic Alias Maximum)将造成协议错误(Protocol Error)。没有设置主题别名最大值属性的情况下,主题别名最大值默认为零。

此值指示了客户端能够接收的来自服务端的主题别名(Topic Alias)最大数量。客户端使用此值来限制本次连接可以拥有的主题别名的数量。服务端在一个PUBLISH报文中发送的主题别名不能超过客户端设置的主题别名最大值(Topic Alias Maximum)。值为零表示本次连接客户端不接受任何主题别名(Topic Alias)。如果主题别名最大值(Topic Alias)没有设置,或者设置为零,则服务端不能向此客户端发送任何主题别名(Topic Alias)

请求响应信息

25 (0x19)Byte,请求响应信息(Request Response Information)标识符。

跟随其后的是用一个字节表示的0或1。包含多个请求响应信息(Request Response Information),或者请求响应信息(Request Response Information)的值既不为0也不为1会造成协议错误(Protocol Error)。如果没有请求响应信息(Request Response Information),则请求响应默认值为0。

客户端使用此值向服务端请求CONNACK报文中的响应信息(Response Information)。值为0,表示服务端不能返回响应信息。值为1,表示服务端可以在CONNACK报文中返回响应信息。

即使客户端请求响应信息(Response Information),服务端也可以选择不发送响应信息(Response Information)。

请求问题信息

23 (0x17)Byte,请求问题信息(Request Problem Information)标识符。

跟随其后的是用一个字节表示的0或1。包含多个请求问题信息(Request Problem Information),或者请求问题信息(Request Problem Information)的值既不为0也不为1会造成协议错误(Protocol Error)。如果没有请求问题信息(Request Problem Information),则请求问题默认值为1。

客户端使用此值指示遇到错误时是否发送原因字符串(Reason String)或用户属性(User Properties)。

如果请求问题信息的值为0,服务端可以选择在CONNACK或DISCONNECT报文中返回原因字符串(Reason String)或用户属性(User Properties),但不能在除PUBLISH,CONNACK或DISCONNECT之外的报文中发送原因字符串(Reason String)或用户属性(User Properties)。如果此值为0,并且在除PUBLISH,CONNACK或DISCONNECT之外的报文中收到了原因字符串(Reason String)或用户属性(User Properties),客户端将发送一个包含原因码0x82(协议错误)的DISCONNECT报文给服务端。

用户属性

38 (0x26)Byte,用户属性(User Property)标识符。

跟随其后的是UTF-8字符串键值对。

用户属性(User Property)可以出现多次,表示多个名字/值对。相同的名字可以出现多次。

CONNECT报文中的用户属性可以被用来发送客户端到服务端的连接相关的属性。

认证方法

21 (0x15)Byte,认证方法(Authentication Method)标识符。

跟随其后的是一个UTF-8编码的字符串,包含了扩展认证的认证方法(Authentication Method)名称。包含多个认证方法将造成协议错误(协议错误)。

如果没有认证方法,则不进行扩展验证。

如果客户端在CONNECT报文中设置了认证方法,则客户端在收到CONNACK报文之前不能发送除AUTH或DISCONNECT之外的报文

认证数据

22 (0x16)Byte,认证数据(Authentication Data)标识符。

跟随其后的是二进制的认证数据。没有认证方法却包含了认证数据(Authentication Data),或者包含多个认证数据(Authentication Data)将造成协议错误(Protocol Error)。

认证数据的内容由认证方法定义。

可变报头非规范示例

在这里插入图片描述

在这里插入图片描述

有效载荷

CONNECT报文的有效载荷(payload)包含由可变报头(Variable Header)中的标志确定的一个或多个以长度为前缀的字段

【MQTT3.1.1】

如果包含的话,必须按这个顺序出现:客户端标识符,遗嘱主题,遗嘱消息,用户名,密码

【MQTT 5.0】

必须按照客户标识符(Client Identifier)、遗嘱属性(Will Properties)、遗嘱主题(Will Topic)、遗嘱载荷(Will Payload)、用户名(User Name)、密码(Password)的顺序出现

客户端标识符(ClientID)

服务端使用客户端标识符 (Client Id) 识别客户端。连接服务端的每个客户端都有唯一的客户端标识符(Client Id)。客户端和服务端都必须使用Client Id识别两者之间的MQTT会话相关的状态

  • 必须存在ClientID,并且它是CONNECT数据包有效载荷中的第一个字段

  • ClientID必须是UTF-8编码的字符串

  • 服务器务必允许ClientID的长度介于1到23个UTF-8编码字节之间,并且仅包含字符

    0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

  • 服务器可以允许包含超过23个编码字节的ClientID。 服务器可以允许ClientID包含上面所列列表中未包含的字符

  • 服务器可以允许客户端提供一个长度为零字节的客户端ID,但是如果这样做,则服务器必须将其作为一种特殊情况,并为该客户端分配一个唯一的客户端ID 。 然后,它必须像客户端已经提供了唯一的ClientID一样处理CONNECT包,并且必须在CONNACK包中返回已分配的客户端标识符【MQTT3.1.1】中如果客户端提供了一个零字节的客户端标识符,它必须同时将清理会话标志设置为1。如果客户端提供的ClientId为零字节且清理会话标志为0,服务端必须发送返回码为0x02(表示标识符不合格)的CONNACK报文响应客户端的CONNECT报文,然后关闭网络连接

  • 如果服务器拒绝了ClientID,则它可以使用原因代码0x85(客户端标识符无效)以CONNACK响应CONNECT数据包,然后必须关闭网络连接。【MQTT5】

客户端实现可以提供一种方便的方法来生成随机的ClientID。 使用此方法的客户应注意避免创建寿命长的孤立会话。在MQTT3.1.1时,当清理会话标志被设置为0时应该主动放弃使用这种方法。

遗嘱属性(MQTT 5)

如果遗嘱标志(Will Flag)被设置为1,有效载荷的下一个字段是遗嘱属性(Will Properties)。遗嘱属性字段定义了遗嘱消息(Will Message)将何时被发布,以及被发布时的应用消息(Application Message)属性。遗嘱属性包括属性长度和属性。

属性长度

遗嘱属性(Will Properties)中的属性长度被编码为可变长字节整数。

遗嘱延时间隔

24 (0x18)Byte,遗嘱延时间隔(Will Delay Interval)标识符。

跟随其后的是由四字节整数表示的以秒为单位的遗嘱延时间隔(Will Delay Interval)。包含多个遗嘱延时间隔将造成协议错误(Protocol Error)。如果没有设置遗嘱延时间隔,遗嘱延时间隔默认值将为0,即不用延时发布遗嘱消息(Will Message)。

服务端将在遗嘱延时间隔(Will Delay Interval)到期或者会话(Session)结束时发布客户端的遗嘱消息(Will Message),取决于两者谁先发生。如果某个会话在遗嘱延时间隔到期之前创建了新的网络连接,则服务端不能发送遗嘱消息

遗嘱延时间隔的一个用途是避免在频繁的网络连接临时断开时发布遗嘱消息,因为客户端往往会很快重新连上网络并继续之前的会话。

如果某个连接到服务端的网络连接使用已存在的客户标识符,此已存在的网络连接的遗嘱消息将会被发布,除非新的网络连接设置了新开始(Clean Start)为0并且遗嘱延时大于0。如果遗嘱延时为0,遗嘱消息将在网络连接断开时发布。如果新开始为1,遗嘱消息也将被发布,因为此会话已结束。

载荷格式指示

1 (0x01)Byte,载荷格式指示(Payload Format Indicator)标识符。

跟随载荷格式指示(Payload Format Indicator )之后的可能是:

  • 0 (0x00),表示遗嘱消息(Will Message)是未指定的字节,等同于不发送载荷格式指示。
  • 1 (0x01),表示遗嘱消息(Will Message)是UTF-8编码的字符数据。载荷中的UTF-8数据必须按照Unicode规范和RFC 3629中的申明进行编码。

包含多个载荷格式指示(Payload Format Indicator)将造成协议错误(Protocol Error)。服务端可以按照格式指示对遗嘱消息(Will Message)进行验证,如果验证失败发送一条包含原因码0x99(载荷格式无效)的CONNACK报文。

消息过期间隔

2 (0x02)Byte,消息过期间隔(Message Expiry Interval)标识符。

跟随其后的是表示消息过期间隔(Message Expiry Interval)的四字节整数。包含多个消息过期间隔将导致协议错误(Protocol Error)。

如果设定了消息过期间隔(Message Expiry Interval),四字节整数描述了遗嘱消息的生命周期(秒),并在服务端发布遗嘱消息时被当做发布过期间隔(Publication Expiry Interval)。

如果没有设定消息过期间隔,服务端发布遗嘱消息时将不发送消息过期间隔(Message Expiry Interval)。

内容类型

3(0x03)Byte,内容类型的标识符。

跟随其后的是一个以UTF-8格式编码的字符串,用来描述遗嘱消息(Will Message)的内容。包含多个内容类型(Content Type)将造成协议错误(Protocol Error)。内容类型的值由发送应用程序和接收应用程序确定。

响应主题

8 (0x08)Byte,响应主题(Response Topic)标识符。 跟随其后的是一个以UTF-8格式编码的字符串,用来表示响应消息的主题名(Topic Name)。包含多个响应主题(Response Topic)将造成协议错误。响应主题的存在将遗嘱消息(Will Message)标识为一个请求报文。

对比数据

9 (0x09)Byte,对比数据(Correlation Data)标识符。

跟随其后的是二进制数据。对比数据被请求消息发送端在收到响应消息时用来标识相应的请求。包含多个对比数据将造成协议错误(Protocol Error)。如果没有设置对比数据,则请求方(Requester)不需要任何对比数据。

对比数据只对请求消息(Request Message)的发送端和响应消息(Response Message)的接收端有意义。

用户属性

38 (0x26)Byte,用户属性(User Property)标识符。

跟随其后的是一个UTF-8字符串对。用户属性(User Property)可以出现多次,表示多个名字/值对。相同的名字可以出现多次。

服务端在发布遗嘱消息(Will Message)时必须维护用户属性(User Properties)的顺序

此属性旨在提供一种传递应用层名称-值标签的方法,其含义和解释仅由负责发送和接收它们的应用程序所有。

遗嘱主题

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

遗嘱消息(MQTT3.1.1)

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

遗嘱载荷(MQTT 5)

如果遗嘱标志(Will Flag)被设置为1,遗嘱载荷(Will Payload)为载荷中下一个字段。遗嘱载荷定义了将要发布到遗嘱主题(Will Topic)的应用消息载荷。

此字段为二进制数据。

用户名

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

密码

如果密码标志(Password Flag)被设置为1,密码(Password)为载荷中下一个字段。密码字段是二进制数据,尽管被称为密码,但可以被用来承载任何认证信息。

密码字段包含一个两字节的长度字段,长度表示二进制数据的字节数(不包含长度字段本身占用的两个字节),后面跟着0到65535字节的二进制数据。

在这里插入图片描述

关于密码字节的示例在MQTT5.0并没有像MQTT3.1.1显示出来

CONNECT行为

该节部分对应MQTT3.1.1所述的响应。

注意:服务器可以在同一个TCP端口或其他网络端点上支持多种协议(包括本协议的早期版本)。

如果服务器确定协议是MQTT,那么它按照下面的方法验证连接请求。

  1. 网络连接建立后,如果服务端在合理的时间内没有收到CONNECT报文,服务端应该关闭这个连接。
  2. 服务端必须验证CONNECT报文,如果报文不符合规范,MQTT3.1.1则要求服务端不发送CONNACK报文直接关闭网络连接,MQTT5则表示服务端可以在关闭网络连接之前发送包含0x80及以上原因码的CONNACK报文,但也须关闭网络连接
  3. 服务端可以检查CONNECT报文的内容是不是满足任何进一步的限制,可以执行身份验证和授权检查【在MQTT5中强调应该执行而非可以执行】。如果任何一项检查没通过,在MQTT3.1.1则应该发送一个适当的、返回码非零的CONNACK响应,并且必须关闭这个网络连接,而在MQTT5中,在关闭网络连接之前服务端可以发送一个合适的包含如0x80及以上原因码的CONNACK报文

如果验证成功,服务端会执行下列步骤。

  1. 如果ClientId表明客户端已经连接到这个服务端,那么服务端必须断开原有的客户端连接,MQTT5则除了断开原有网络连接外,还向原有的客户端发送一个包含原因码为0x8E(会话被接管)的DISCONNECT报文。如果原有客户端存在遗嘱消息(Will Message),并发布遗嘱消息

    如果原有网络连接包含遗嘱消息,且遗嘱延时间隔为0,则遗嘱消息会在此网络连接被关闭时发送。如果原有网络连接会话过期间隔为0,或者新网络连接新开始标志设置为1且原有网络连接包含遗嘱消息,则遗嘱消息会被发送,因为原有会话已结束。

  2. 在MQTT3.1.1中,服务端必须执行清理会话的过程,而MQTT5.0则是对clean start标志进行处理

  3. MQTT3.1.1中服务端必须发送返回码为零的CONNACK报文作为CONNECT报文的确认响应 ,而MQTT5.0中则是必须使用包含原因码为0x00(成功)的CONNACK报文对客户端的CONNECT报文进行确认

    如果服务端被用来处理商业关键数据,推荐对网络连接进行认证和授权。如果认证和授权成功,服务端可通过发送包含原因码为0x00(成功)的CONNACK报文进行响应,否则建议服务端根本不要发送CONNACK报文,因为这是一种潜在的对MQTT服务端的攻击,可以被用来进行拒绝服务因为这是一种潜在的对MQTT服务端的攻击,可以被用来进行拒绝服务

  4. 开始消息分发和保活状态监视。

允许客户端在发送CONNECT报文之后立即发送其它的控制报文;客户端不需要等待服务端的CONNACK报文。如果服务端拒绝了CONNECT,MQTT3.1.1版本中,它不能处理客户端在CONNECT报文之后发送的任何数据。在MQTT5版本中,则不能处理客户端在CONNECT报文之后发送的任何除AUTH以外的报文

客户端通常会等待一个CONNACK报文。然而客户端有权在收到CONNACK之前发送控制报文,由于不需要维持连接状态,这可以简化客户端的实现。【MQTT3.1.1】

客户端通常会等待CONNACK报文。然而,如果在收到CONNACK报文之前就自由的发送其它QTT控制报文将会简化客户端的实现,因为它不必监督连接的状态。如果连接被拒绝了,客户端在接收CONNACK报文之前发送的任何数据将不会被服务端所处理。【MQTT 5】

选择在收到CONNACK报文之前就发送MQTT控制报文的客户端将不知道服务端所存在的约束以及会话是否被使用。【MQTT 5】

服务端在对某个客户端完成认证之前,可以选择限制读取该客户端的网络数据或者关闭该客户端的网络连接。这是一种避免拒绝服务攻击的方法。

结语

CONNECT控制报文属于第一个MQTT控制报文,没有发送该报文,服务端一般需要拒绝连接,相应的broker如果没有处理这个逻辑,可能会抛出异常。另外,CONNECT发送成功后,一般需要服务端返回一个响应的报文进行响应,下一篇则是说明确认连接请求的报文,即CONNACK报文。

发布了189 篇原创文章 · 获赞 675 · 访问量 31万+

猜你喜欢

转载自blog.csdn.net/YuYunTan/article/details/102499376