文章目录
前言
本篇博文介绍,MQTT的另一个报文,即CONNECK,由服务端发送。
CONNACK – 确认连接请求
CONNACK报文由服务端所发送,作为对来自客户端的CONNECT报文的响应。
在MQTT5中,服务端在发送任何除AUTH以外的报文之前必须先发送包含原因码为0x00(成功)的CONNACK报文。服务端在一次网络连接中不能发送多个CONNACK报文。
如果客户端在合理的时间内没有收到服务端的CONNACK报文,客户端应该关闭网络连接。合理的时间取决于应用的类型和通信基础设施。
固定报头
图例 CONNACK 报文固定报头
剩余长度字段
用变长字节整数来编码,表示可变报头的长度。
可变报头
在MQTT5中,CONNACK报文的可变报头按顺序包含以下字段:连接确认标志(Connect Acknowledge Flags),连接原因码(Reason Code),属性(Properties)。
MQTT3.1.1中没有属性
MQTT5则无此示例图
连接确认标志
第1个字节是 连接确认标志,位7-1是保留位且必须设置为0。
第0 ()位 是当前会话(Session Present)标志。
当前会话
位置:连接确认标志的第0位。
【MQTT3.1.1】
如果服务端收到清理会话(CleanSession)标志为1的连接,除了将CONNACK报文中的返回码设置为0之外,还必须将CONNACK报文中的当前会话设置(Session Present)标志为0 。
==如果服务端收到一个Clean Session为0的连接,当前会话标志的值取决于服务端是否已经保存了ClientId对应客户端的会话状态。如果服务端已经保存了会话状态,它必须将CONNACK报文中的当前会话标志设置为1 。如果服务端没有已保存的会话状态,它必须将CONNACK报文中的当前会话设置为0。还需要将CONNACK报文中的返回码设置为0 ==。
当前会话标志使服务端和客户端在是否有已存储的会话状态上保持一致。
一旦完成了会话的初始化设置,已经保存会话状态的客户端将期望服务端维持它存储的会话状态。如果客户端从服务端收到的当前的值与预期的不同,客户端可以选择继续这个会话或者断开连接。客户端可以丢弃客户端和服务端之间的会话状态,方法是,断开连接,将清理会话标志设置为1,再次连接,然后再次断开连接。
如果服务端发送了一个包含非零返回码的CONNACK报文,它必须将当前会话标志设置为0。
【MQTT 5】
会话存在(Session Present)标志通知客户端,服务端是否正在使用此客户标识符之前连接的会话状态(Session State)。会话存在标志使服务端和客户端在是否有已存储的会话状态上保持一致。
如果服务端接受一个新开始(Clean Start)为1的连接,服务端在CONNACK报文中除了把原因码设置为0x00(成功)之外,还必须把会话存在标志设置为0。
如果服务端接受一个新开始(Clean Start)为0的连接,并且服务端已经保存了此客户标识符(ClientID)的会话状态(Session State),服务端在CONNACK报文中必须把会话存在标志设置为1。否则,服务端必须把会话存在标志设置为0。无论如何,服务端在CONNACK报文中必须把原因码设置为0x00(成功)。
如果客户端从服务端接收到的会话存在标志值与预期的不同,客户端做如下处理:
- 如果客户端没有保存的会话状态,但收到会话存在标志为1,客户端必须关闭网络连接
- 如果希望重新开始一个新的会话,客户端可以使用新开始(Clean Start)为1并重新连接服务端。
- 如果客户端保存了会话状态,但收到的会话存在标志为0,客户端若要继续此网络连接,它必须丢弃其保存的会话状态
- 如果服务端发送的CONNACK报文中原因码非0,它必须把会话存在标志设置为0
连接返回码(MQTT 3)
位置:可变报头的第2个字节。
连接返回码字段使用一个字节的无符号值,在 下表 中列出。如果服务端收到一个合法的CONNECT报文,但出于某些原因无法处理它,服务端应该尝试发送一个包含非零返回码(表格中的某一个)的CONNACK报文。如果服务端发送了一个包含非零返回码的CONNACK报文,那么它必须关闭网络连接。
MQTT3.1.1的连接返回码和MQTT5的原因码有些类似,MQTT5 的原因码大多在0x80及以上。
如果认为上表中的所有连接返回码都不太合适,那么服务端必须关闭网络连接,不需要发送CONNACK报文。
连接原因码(MQTT 5)
位置:可变报头的第2个字节。
可变报头中第2个字节是连接原因码(Reason Code)。
连接原因代码的值如下所示。 如果服务器接收到格式正确的CONNECT数据包,但是服务器无法完成连接,则服务器可以发送一个CONNACK数据包,其中包含来自该表的适当的Connect Reason代码。 如果服务器发送的CONNACK数据包包含原因码为128或更高,则必须关闭网络连接。
服务端发送的CONNACK报文必须设置一种原因码
原因码0x80(未指明的错误)可以被用作:服务器知道失败的原因但是并不希望透露给客户端,或者没有其他适用的原因码
出于安全考虑,发现CONNECT出错时服务端可以选择不发送CONNACK报文而关闭网络连接。例如,在公网中向未被授权的网络连接告知自身MQTT服务端身份并不明智。
CONNACK属性(MQTT 5)
属性长度
CONNACK报文可变报头中的属性长度,编码为变长字节整数
会话过期间隔
17 (0x11)Byte,会话过期间隔(Session Expiry Interval)标识符。
跟随其后的是用四字节整数表示的以秒为单位的会话过期间隔(Session Expiry Interval)。包含多个会话过期间隔(Session Expiry Interval)将造成协议错误(Protocol Error)。
如果会话过期间隔(Session Expiry Interval)值未指定,则使用CONNECT报文中指定的会话过期时间间隔。服务端使用此属性通知客户端它使用的会话过期时间间隔与客户端在CONNECT中发送的值不同。
接收最大值
33 (0x21)Byte,接收最大值(Receive Maximum)描述符。
跟随其后的是由双字节整数表示的最大接收值。包含多个接收最大值或接收最大值为0将造成协议错误(Protocol Error)。
服务端使用此值限制服务端愿意为该客户端同时处理的QoS为1和QoS为2的发布消息最大数量。没有机制可以限制客户端试图发送的QoS为0的发布消息。
如果没有设置最大接收值,将使用默认值65535。
最大服务质量
36 (0x24)Byte,最大服务质量(Maximum QoS)标识符。
跟随其后的是用一个字节表示的0或1。包含多个最大服务质量(Maximum QoS)或最大服务质量既不为0也不为1将造成协议错误。如果没有设置最大服务质量,客户端可使用最大QoS为2。
如果服务端不支持Qos为1或2的PUBLISH报文,服务端必须在CONNACK报文中发送最大服务质量以指定其支持的最大QoS值。即使不支持QoS为1或2的PUBLISH报文,服务端也必须接受请求QoS为0、1或2的SUBSCRIBE报文。
如果从服务端接收到了最大QoS等级,则客户端不能发送超过最大QoS等级所指定的QoS等级的PUBLISH报文。服务端接收到超过其指定的最大服务质量的PUBLISH报文将造成协议错误(Protocol Error)。这种情况下应使用包含原因码为0x9B(不支持的QoS等级)的DISCONNECT报文进行处理。
如果服务端收到包含遗嘱的QoS超过服务端处理能力的CONNECT报文,服务端必须拒绝此连接。服务端应该使用包含原因码为0x9B(不支持的QoS等级)的CONNACK报文进行错误处理,随后必须关闭网络连接。
客户端不必支持QoS为1和2的PUBLISH报文。客户端只需将其发送的任何SUBSCRIBE报文中的QoS字段限制在其支持的最大服务质量以内即可。
保留可用
37 (0x25)Byte,保留可用(Retain Available)标识符。
跟随其后的是一个单字节字段,用来声明服务端是否支持保留消息。值为0表示不支持保留消息,为1表示支持保留消息。如果没有设置保留可用字段,表示支持保留消息。包含多个保留可用字段或保留可用字段值不为0也不为1将造成协议错误(Protocol Error)。
如果服务端收到一个包含保留标志位1的遗嘱消息的CONNECT报文且服务端不支持保留消息,服务端必须拒绝此连接请求,且应该发送包含原因码为0x9A(不支持保留)的CONNACK报文,随后必须关闭网络连接。
从服务端接收到的保留可用标志为0时,客户端不能发送保留标志设置为1的PUBLISH报文。如果服务端收到这种PUBLISH报文,将造成协议错误(Protocol Error),此时服务端应该发送包含原因码为0x9A(不支持保留)的DISCONNECT报文。
最大报文长度
39 (0x27)Byte,最大报文长度(Maximum Packet Size)标识符。
跟随其后的是由四字节整数表示的服务端愿意接收的最大报文长度(Maximum Packet Size)。如果没有设置最大报文长度,则按照协议由固定报头中的剩余长度可编码最大值和协议报头对数据包的大小做限制。
包含多个最大报文长度(Maximum Packet Size),或最大报文长度为0将造成协议错误(Protocol Error)。
最大报文长度是MQTT控制报文的总长度。服务端使用最大报文长度通知客户端其所能处理的单个报文长度限制。
客户端不能发送超过最大报文长度(Maximum Packet Size)的报文给服务端。收到长度超过限制的报文将导致协议错误,此时服务端应该发送包含原因码0x95(报文过长)的DISCONNECT报文给客户端。
分配客户标识符
18 (0x12)Byte,分配客户标识符(Assigned Client Identifier)标识符。
跟随其后的是UTF-8编码的分配客户标识符(Assigned Client Identifier)字符串。包含多个分配客户标识符将造成协议错误(Protocol Error)。 服务端分配客户标识符的原因是CONNECT报文中的客户标识符长度为0。
如果客户端使用长度为0的客户标识符(ClientID),服务端必须回复包含分配客户标识符(Assigned Client Identifier)的CONNACK报文。分配客户标识符必须是没有被服务端的其他会话所使用的新客户标识符。
主题别名最大值
34 (0x22)Byte,主题别名最大值(Topic Alias Maximum)标识符。
跟随其后的是用双字节整数表示的主题别名最大值(Topic Alias Maximum)。包含多个主题别名最大值(Topic Alias Maximum)将造成协议错误(Protocol Error)。没有设置主题别名最大值属性的情况下,主题别名最大值默认为零。
此值指示了服务端能够接收的来自客户端的主题别名(Topic Alias)最大值。服务端使用此值来限制本次连接可以拥有的主题别名的值。客户端在一个PUBLISH报文中发送的主题别名值不能超过服务端设置的主题别名最大值(Topic Alias Maximum)。值为0表示本次连接服务端不接受任何主题别名(Topic Alias)。如果主题别名最大值(Topic Alias)没有设置,或者设置为0,则客户端不能向此服务端发送任何主题别名(Topic Alias)。
原因字符串
31 (0x1F)Byte,原因字符串(Reason String)标识符。
跟随其后的是UTF-8编码的字符串,表示此次响应相关的原因。此原因字符串(Reason String)是为诊断而设计的可读字符串,不应该被客户端所解析。
服务端使用此值向客户端提供附加信息。如果加上原因字符串之后的CONNACK报文长度超出了客户端指定的最大报文长度,则服务端不能发送此原因字符串。包含多个原因字符串将造成协议错误(Protocol Error)。
客户端对原因字符串的恰当使用包括:抛出异常时使用此字符串,或者将此字符串写入日志。
用户属性
38 (0x26)Byte,用户属性(User Property)标识符。 跟随其后的是UTF-8字符串对。此属性可用于向客户端提供包括诊断信息在内的附加信息。如果加上用户属性之后的CONNACK报文长度超出了客户端指定的最大报文长度,则服务端不能发送此属性。用户属性(User Property)允许出现多次,以表示多个名字/值对,且相同的名字可以多次出现。
用户属性的内容和意义不做定义。CONNACK报文的接收端可以选择忽略此属性。
通配符订阅可用
40 (0x28)Byte,通配符订阅可用(Wildcard Subscription Available)标识符。
跟随其后的是一个单字节字段,用来声明服务器是否支持通配符订阅(Wildcard Subscriptions)。值为0 表示不支持通配符订阅,值为1表示支持通配符订阅。如果没有设置此值,则表示支持通配符订阅。包含多个通配符订阅可用属性,或通配符订阅可用属性值不为0也不为1将造成协议错误(Protocol Error)。
如果服务端在不支持通配符订阅(Wildcard Subscription)的情况下收到了包含通配符订阅的SUBSCRIBE 报文,将造成协议错误(Protocol Error)。此时服务端将发送包含原因码为0xA2(通配符订阅不支持)的DISCONNECT报文。
服务端在支持通配符订阅的情况下仍然可以拒绝特定的包含通配符订阅的订阅请求。这种情况下,服务端可以发送一个包含原因码为0xA2(通配符订阅不支持)的SUBACK报文。
订阅标识符可用
41 (0x29)Byte,订阅标识符可用(Subscription Identifier Available)标识符。
跟随其后的是一个单字节字段,用来声明服务端是否支持订阅标识符(Subscription Identifiers)。值为0表示不支持订阅标识符,值为1表示支持订阅标识符。如果没有设置此值,则表示支持订阅标识符。包含多个订阅标识符可用属性,或订阅标识符可用属性值不为0也不为1将造成协议错误(Protocol Error)。
如果服务端在不支持订阅标识符(Subscription Identifier)的情况下收到了包含订阅标识符的SUBSCRIBE 报文,将造成协议错误(Protocol Error)。此时服务端将发送包含原因码为0xA1(订阅标识符不支持)的DISCONNECT报文。
共享订阅可用
42 (0x2A)Byte,共享订阅可用(Shared Subscription Available)标识符。
跟随其后的是一个单字节字段,用来声明服务端是否支持共享订阅(Shared Subscription)。值为0表示不支持共享订阅,值为1表示支持共享订阅。如果没有设置此值,则表示支持共享订阅。包含多个共享订阅可用(Shared Subscription Available),或共享订阅可用属性值不为0也不为1将造成协议错误(Protocol Error)。
如果服务端在不支持共享订阅(Shared Subscription)的情况下收到了包含共享订阅的SUBSCRIBE报文,将造成协议错误(Protocol Error)。此时服务端将发送包含原因码为0x9E(共享订阅不支持)的DISCONNECT报文。
服务端保活
19 (0x13)Byte,服务端保活(Server Keep Alive)标识符。
跟随其后的是由服务端分配的双字节整数表示的保活(Keep Alive)时间。如果服务端发送了服务端保活(Server Keep Alive)属性,客户端必须使用此值代替其在CONNECT报文中发送的保活时间值 。如果服务端没有发送服务端保活属性,服务端必须使用客户端在CONNECT报文中设置的保活时间值。包含多个服务端保活属性将造成协议错误(Protocol Error)。
服务器保持保活的主要用途是使服务器通知客户端,它将比客户端指定的保活状态更早地断开客户端的连接。
响应信息
26 (0x1A)Byte,响应信息(Response Information)标识符。
跟随其后的是一个以UTF-8编码的字符串,作为创建响应主题(Response Topic)的基本信息。关于客户端如何根据响应信息(Response Information)创建响应主题不在MQTT规范中的定义范围内。包含多个响应信息将造成协议错误(Protocol Error)。
如果客户端发送的请求响应信息(Request Response Information)值为1,则服务端在CONNACK报文中发送响应信息(Response Information)为可选项。
响应信息通常被用来传递主题订阅树的一个全局唯一分支,此分支至少在该客户端的会话生命周期内为该客户端所保留。请求客户端和响应客户端的授权需要使用它,所以它通常不能仅仅是一个随机字符串。一般把此分支作为特定客户端的订阅树根节点。通常此信息需要正确配置,以使得服务器能返回信息。使用此机制时,具体的信息一般由服务端来进行统一配置,而非由各个客户端自己配置。
服务端参考列表
28 (0x1C)Byte,服务端参考列表(Server Reference)标识符。
跟随其后的是一个以UTF-8编码的字符串,可以被客户端用来标识其他可用的服务端。包含多个服务端参考(Server Reference)将造成协议错误(Protocol Error)。
服务端在包含了原因码为0x9C(使用其他服务端)或0x9D(服务端已移动)的CONNACK报文或DISCONNECT报文中设置服务端参考列表。
认证方法
21 (0x15)Byte,认证方法(Authentication Method)标识符。
跟随其后的是一个以UTF-8编码的字符串,包含了认证方法(Authentication Method)名。包含多个认证方法将造成协议错误(Protocol Error)。
认证数据
22 (0x16)Byte,认证数据(Authentication Data)标识符。
跟随其后的是包含认证数据(Authentication Data)的二进制数据。此数据的内容由认证方法和已交换的认证数据状态定义。包含多个认证数据将造成协议错误(Protocol Error)。
CONNACK载荷
CONNACK报文没有有效载荷。
总结
CONNACK相对简单,下一次便是PUBLISH报文的介绍。