MQTT协议史上最全解析(纯干货分享)
目录
MQTT是什么?一些术语解释MQTT特点MQTT控制报文格式十四种控制报文1号控制报文:CONNECT – 连接到服务端2号控制报文:CONNACK – 确认连接请求3号控制报文:PUBLISH – 发布消息4号控制报文:PUBACK – 发布确认5号控制报文:PUBREC – 发布收到(QoS 2 ,第一步)6号控制报文:PUBREL – 发布 释放(QoS 2 ,第二步)7号控制报文:PUBCOMP – 发布 完成(QoS 2 ,第三步)8号控制报文:SUBSCRIBE - 订阅主题9号控制报文:SUBACK – 订阅确认10号控制报文:UNSUBSCRIBE – 取消订阅11号控制报文:UNSUBACK – 取消订阅确认12号控制报文:PINGREQ – 心跳请求13号控制报文:PINGRESP – 心跳响应14号控制报文:DISCONNECT – 断开连接
关于CONNECT控制报文中的连接标志关于CONNECT控制报文中的保持连接时间
MQTT是什么?
MQTT协议全称是(Message Queuing Telemetry Transport),即消息队列遥测传输协议。
是一种基于发布/订阅(Publish/Subscribe)模式的轻量级通讯协议,并且该协议构建于TCP/IP协议之上,我们知道TCP协议本身就具有高可靠性的特点,因此基于其上的MQTT协议同样也是具有高可靠、低开销的特点,之所以低开销,是以为MQTT协议传输的最小的报文也只有两个字节。
在物联网开发中,MQTT不是唯一的选择,与MQTT互相竞争的协议有XMPP和CoAP协议等。
关于发布和订阅的概念我们拿抖音平台来举个例子,我们每一个用户就都是一个客户端,而抖音就是MQTT协议中的服务器,当我们(用户一)关注某一个视频发布者(用户二)时,这样一个关注的行为就可以理解为订阅;同时用户二也可以关注你,那么这就是相互订阅。当用户二发布作品的时候,这个作品是发布到了抖音平台,也就是我们现在的服务器,这个过程就是消息的发布。
在这里需要注意的是:用户二(客户端)发布的消息并不是直接发布给了用户一,而是发布到了抖音平台(服务器),由于用户一订阅了用户二的消息(相当于点了关注),所以抖音平台(服务器)就会向用户一推送这个消息(注意发布和推送的区别)。这就是MQTT协议订阅&发布的一个简单比喻。
上面是一个比喻,再来对比一下实际的MQTT协议通信模型: 实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。
MQTT传输的消息分为:主题(Topic)和负载(payload)两部分
Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload)payload,可以理解为消息的内容,是指订阅者具体要使用的内容
一些术语解释
网络连接 Network Connection
MQTT 使用的底层传输协议(TCP)基础设施。
客户端使用它连接服务端。它提供有序的、可靠的、双向字节流传输。
应用消息 Application Message
MQTT 协议通过网络传输应用数据。应用消息通过 MQTT 传输时,它们有关联的服务质量(QoS) 和主题(Topic) 。
客户端 Client
使用 MQTT 的程序或设备。客户端总是通过网络连接到服务端。它可以
发布应用消息给其它相关的客户端。订阅以请求接受相关的应用消息取消订阅以移除接受应用消息的请求。从服务端断开连接。
服务端 Server
一个程序或设备,作为发送消息的客户端和请求订阅的客户端之间的中介。服务端
接受来自客户端的网络连接。接受客户端发布的应用消息。处理客户端的订阅和取消订阅请求。转发应用消息给符合条件的已订阅客户端。
订阅 Subscription
订阅包含一个主题过滤器(Topic Filter)和一个最大的服务质量(QoS)等级。订阅与单个会话(Session)关联。会话可以包含多于一个的订阅。会话的每个订阅都有一个不同的主题过滤器。
主题名 Topic Name
附加在应用消息上的一个标签,服务端已知且与订阅匹配。服务端发送应用消息的一个副本给每一个匹配的客户端订阅。
主题过滤器 Topic Filter
订阅中包含的一个表达式,用于表示相关的一个或多个主题。主题过滤器可以使用通配符。
会话 Session
客户端和服务端之间的状态交互。一些会话持续时长与网络连接一样,另一些可以在客户端和服务端的多个连续网络连接间扩展。
控制报文 MQTT Control Packet
通过网络连接发送的信息数据包。MQTT规范定义了十四种不同类型的控制报文,其中一个(PUBLISH报文) 用于传输应用消息。
MQTT特点
1.客户端使用它连接服务端。 2.它提供有序的、可靠的、双向字节流传输。 3.使用发布/订阅消息模式,提供了一对多的消息分发和应用之间的解耦。 4.消息传输不需要知道负载内容。 5.提供三种等级的服务质量QoS : 6.很小的传输消耗和协议数据交换,最大限度减少网络流量 7.异常连接断开发生时,能通知到相关各方。
消息质量(QoS)
MQTT消息质量有三个等级,QoS 0,QoS 1和 QoS 2。
QoS 0:最多分发一次。消息的传递完全依赖底层的TCP/IP网络,协议里没有定义应答和重试,消息要么只会到达服务端一次,要么根本没有到达。消息可能会丢失。例如,这个等级可用于环境传感器数据,单次的数据丢失没关系,因为不久之后会再次发送。
QoS 1:至少分发一次。服务器的消息接收由PUBACK消息进行确认,如果通信链路或发送设备异常,或者指定时间内没有收到确认消息,发送端会重发这条在消息头中设置了DUP位的消息。(保证交付功能)
QoS 2:只分发一次。这是最高级别的消息传递,消息丢失和重复都是不可接受的,使用这个服务质量等级会有额外的开销。例如,这个等级可用在一个计费系统中,这里如果消息重复或丢失会导致不正确的收费。
eg. 目前流行的共享单车智能锁,智能锁可以定时使用QoS level 0质量消息请求服务器,发送单车的当前位置,如果服务器没收到也没关系,反正过一段时间又会再发送一次。之后用户可以通过App查询周围单车位置,找到单车后需要进行解锁,这时候可以使用QoS level 1质量消息,手机App不断的发送解锁消息给单车锁,确保有一次消息能达到以解锁单车。最后用户用完单车后,需要提交付款表单,可以使用QoS level 2质量消息,这样确保只传递一次数据,否则用户就会多付钱了。
MQTT控制报文格式
MQTT 控制报文的结构
Fixed header固定报头,所有控制报文都包含Variable header可变报头,部分控制报文包含Payload有效载荷,部分控制报文包含
注:固定报头必须有,可变报头和有效负载可以没有。
【固定报头】:第一个字节高4位是MQTT控制报文的类型 + 第一个字节低4位用于指定控制报文类型的标志位(只在PUBLISH发布消息时用到) + 剩余长度1~4个字节(剩余长度字段最大4个字节) 剩余长度包括可变报头和负载的数据。 剩余长度不包括用于编码剩余长度字段本身的字节数。 剩余长度字段使用一个变长度编码方案,对小于 128 的值它使用单字节编码。更大的值按下面的方式处理。 低 7 位有效位用于编码数据,最高有效位用于指示是否有更多的字节。因此每个字节可以编码 128 个数值和一个延续位(continuation bit) 。 剩余长度字段最大4个字节。
【可变报头】:可变报头的内容根据报文类型的不同而不同。 某些MQTT控制报文包含一个可变报头部分。它在固定报头和负载之间。可变报头的报文标识符(Packet Identifier) 字段存在于在多个类型的报文里。 报文标志符(2字节):包含报文标识符的9个控制报文有:PUBLISH(QoS>0),PUBACK ,PUBREC ,PUBREL ,PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK
报文标志符的注意项: 1.客户端和服务端彼此独立地分配报文标识符。因此,客户端服务端组合使用相同的报文标识符可以实现并发的消息交换。 2. SUBSCRIBE,UNSUBSCRIBE 和 PUBLISH(QoS 大于 0)控制报文 必须包含一个非零的 16 位报文标识符(Packet Identifier) 3. 客户端每次发送一个新的这些类型的报文时都必须分配一个当前未使用的报文标识符。如果一个客户端要重发这个特殊的控制报文,在随后重发那个报文时,它必须使用相同的标识符。当客户端处理完这个报文对应的确认后,这个报文标识符就释放可重用。 4. QoS 设置为 0 的 PUBLISH 报文 不能包含报文标识符。 5.PUBACK, PUBREC, PUBREL 报文 必须包含与最初发送的 PUBLISH 报文相同的报文标识符。类似地,SUBACK 和 UNSUBACK 必须包含在对应的 SUBSCRIBE 和 UNSUBSCRIBE 报文中使用的报文标识符。 6. 作用:基于标志位来决定可变报头中是否需要包含这些字段
【有效载荷】:某些 MQTT 控制报文在报文的最后部分包含一个有效载荷,例如对于 PUBLISH 来说有效载荷就是应用消息。 可能包含有效载荷的控制报文有:CONNECT,PUBLISH,SUBSCRIBE,SUBACK,UNSUBSCRIBE
十四种控制报文
注:C->S指的是客户端到服务端,S->C指的是服务端到客户端,C<->S指的是两个方向都允许 0和15类型值保留无效。 表格中的值指的是固定报头第一个字节的高4位即MQTT控制报文的类型的值 PUBREC和PUBREL和PUBCOMP只有在QoS2等级时才存在
1号控制报文:CONNECT – 连接到服务端
注:客户端到服务端的网络连接建立后,客户端发送给服务端的第一个报文 必须是 CONNECT 报文 在一个网络连接上,客户端只能发送一次 CONNECT 报文。服务端 必须将客户端发送的第二个 CONNECT报文当作协议违规处理并断开客户端的连接。 CONNECT 报文的可变报头按下列次序包含四个字段:协议名(Protocol Name),协议级别(Protocol Level),连接标志(Connect Flags)和保持连接(Keep Alive) 注:客户端用 8 位的无符号值表示协议的修订版本。对于 3.1.1 版协议,协议级别字段的值是 4(0x04)。如果发 现不支持的协议级别,服务端 必须给客户端发送一个返回码为 0x01(不支持的协议级别)的 CONNACK报文响应CONNECT 报文,然后断开客户端的连接 注:连接标志字节包含一些用于指定 MQTT 连接行为的参数。它还指出有效载荷中的字段是否存在。此连接标志将预示后面有效载荷的内容 注:服务端 必须验证 CONNECT 控制报文的保留标志位(第 0 位)是否为 0,如果不为 0 必须断开客户端连接
CONNECT控制报文的内容: 固定报头 MQTT 4位报文类型加4位保留位即10 ,剩余长度字节由后续的数据长度决定 可变报头 固定协议名6字节 长度00 04 + MQTT UTF-8编码 00 04 4D 51 54 54 协议级别1字节 04 代表3.1.1 版协议 连接标志1字节 C2 置位密码标志,用户名标志,清理会话标志 保持连接时间2字节 00 78 代表120秒 有效载荷 客户端ID数据长度2字节 + 客户端ID ASC码 + 用户名数据长度2字节 + 用户名 ASC码 + 密码长度2字节 + 密码 ASC码
注意!CONNECT 报文的有效载荷(payload)包含一个或多个以长度为前缀的字段,可变报头中的连接标志决定是否 包含这些字段。如果包含的话, 必须按这个顺序出现:客户端标识符,遗嘱主题,遗嘱消息,用户名,密码
报文举例: 10 | 25 | 00 04 4D 51 54 54 | 04 | C2 | 00 78 | 00 09 35 32 38 39 38 36 38 37 35 | 00 06 32 34 38 34 39 33 | 00 06 6B 66 62 73 6B 64
固定报头0x10表示客户端请求连接服务端 固定报头0x25表示可变报头和有效负载一共37个字节数据,本身不算在内 协议名00 04 4D 51 54 54 ,00 04表示4个字节 ,4D 51 54 54是MQTT 的 UTF-8 编码的字符串(也是ASC码值) 协议级别04客户端用 8 位的无符号值表示协议的修订版本。对于 3.1.1 版协议,协议级别字段的值是 4(0x04) 连接标志C2 保持连接 00 78 即120秒 00 09 35 32 38 39 38 36 38 37 35 设备ID ,9个字节,528986875的ASC码,以下相同 00 06 32 34 38 34 39 33 产品ID 248493 00 06 6B 66 62 73 6B 64 鉴权信息 kfbskd
2号控制报文:CONNACK – 确认连接请求
注意:服务端发送 CONNACK 报文响应从客户端收到的 CONNECT 报文。服务端发送给客户端的第一个报文 必须是 CONNACK 报文 如果客户端在合理的时间内没有收到服务端的 CONNACK 报文,客户端 应该关闭网络连接。合理的时间取决于应用的类型和通信基础设施。 CONNACK控制报文的内容: 固定报头 MQTT 4位报文类型加4位保留位即20 ,剩余长度字节由后续的数据长度决定 可变报头 两字节:连接确认标志(前7位必须为0,最低位取决于连接标志中的清理会话标志)+连接返回码(正常为00,否则指示故障) 有效载荷 无
报文举例: 20 02 01 00 固定报头20 02:20标志报文类型2,02剩余长度 可变报头01 00 有效负载无
3号控制报文:PUBLISH – 发布消息
PUBLISH 控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。 客户端使用 PUBLISH 报文发送应用消息给服务端,目的是分发到其它订阅匹配的客户端。 服务端使用 PUBLISH 报文发送应用消息给每一个订阅匹配的客户端。
报文内容组成: 固定报头 第一个字节高4位报文类型3 ,低4位分别是DUP重发标志-QoS等级高位-QoS等级低位-保留位,第2个字节开始是剩余长度 可变报头 可变报头按顺序包含主题名(UTF-8编码)和报文标识符。主题名之前有两字节主题名的长度,报文标志符是两字节。 注意报文标志符只在QoS等级1和2的报文中有,等级0没有直接忽略 有效负载 有效载荷包含将被发布的应用消息。数据的内容和格式是应用特定的。长度计算是剩余长度-可变报头的长度
关于DUP重发标志:如果 DUP 标志被设置为 0,表示这是客户端或服务端第一次请求发送这个 PUBLISH 报文。如果 DUP 标志 被设置为 1,表示这可能是一个早前报文请求的重发。 客户端或服务端请求重发一个 PUBLISH 报文时, 必须将 DUP 标志设置为 1 。对于 QoS 0 的消息,DUP 标志 必须设置为 0 。
关于QoS等级标志位:PUBLISH 报文不能将QoS所有的位设置为 1。如果服务端或客户端收到QoS所有位都为1的 PUBLISH报文,它必须关闭网络连接
关于保留位:如果客户端发给服务端的 PUBLISH 报文的保留(RETAIN)标志被设置为 1,服务端 必须存储这个应用消息和它的服务质量等级(QoS),以便它可以被分发给未来的主题名匹配的订阅者。 如果客户端发给服务端的 PUBLISH 报文的保留标志位 0,服务端 不能存储这个消息也 不能移除或替换任何现存的保留消息。
报文举例: 30 0E 00 09 6B 66 62 5F 74 6F 70 69 63 31 32 33 QoS等级0 数据:123,等级0没有回复
32 10 00 09 6B 66 62 5F 74 6F 70 69 63 00 01 31 32 33 等级1 数据:123
34 10 00 09 6B 66 62 5F 74 6F 70 69 63 00 01 31 32 33 等级2 数据:123
这里面的主题名称是kfb_topic: 6B 66 62 5F 74 6F 70 69 63,主题名之前有两字节主题名的长度00 09 报文标志符只在QoS等级1和2的报文中有,例子中报文标识符是00 01
4号控制报文:PUBACK – 发布确认
PUBACK 报文是对 QoS 1 等级的 PUBLISH 报文的响应。
报文内容组成: 固定报头 报文类型40+剩余长度02 可变报头 包含等待确认的 PUBLISH 报文的报文标识符两个字节。具体内容取决于之前的 PUBLISH 报文中的报文标志符。 有效负载 无
报文举例: 40 02 00 01 等级1的回复,表示签收 固定报头40 02:40标志报文类型4,02剩余长度 可变报头00 01 与等待确认的PUBLISH报文的报文标识符一致 有效负载无
5号控制报文:PUBREC – 发布收到(QoS 2 ,第一步)
PUBREC 报文是对 QoS 等级 2 的 PUBLISH 报文的响应。(通常由接收方回复)它是 QoS 2 等级协议交换的第二个报文。
报文内容组成: 固定报头 报文类型50+剩余长度02 可变报头 包含等待确认的 PUBLISH 报文的报文标识符两个字节。具体内容取决于之前的 PUBLISH 报文中的报文标志符。 有效负载 无
报文举例: 50 02 00 01 等级2的第一次服务器的回复,等级2有两次回复,客户端再次发送62 02 00 01,得到服务器的二次回复70 02 00 01表示成功 固定报头50 02:40标志报文类型5,02剩余长度 可变报头00 01 与等待确认的PUBLISH报文的报文标识符一致 有效负载无
6号控制报文:PUBREL – 发布 释放(QoS 2 ,第二步)
PUBREL 报文是对 PUBREC 报文的响应。(通常是发送方发出)它是 QoS 2 等级协议交换的第三个报文。 注意!PUBREL 控制报文固定报头的第 3,2,1,0 位是保留位, 必须被设置为 0,0,1,0。服务端 必须将其它的任何值都当做是不合法的并关闭网络连接。
报文内容组成: 固定报头 报文类型62+剩余长度02 可变报头 包含等待确认的 PUBLISH 报文的报文标识符两个字节。具体内容取决于之前的 PUBLISH 报文中的报文标志符。 有效负载 无
报文举例: 62 02 00 01 等级2 再次询问
7号控制报文:PUBCOMP – 发布 完成(QoS 2 ,第三步)
PUBCOMP 报文是对 PUBREL 报文的响应。(通常由接收方回复)它是 QoS 2 等级协议交换的第四个也是最后一个报文。
报文内容组成: 固定报头 报文类型70+剩余长度02 可变报头 包含等待确认的 PUBLISH 报文的报文标识符两个字节。具体内容取决于之前的 PUBLISH 报文中的报文标志符。 有效负载 无
报文举例: 70 02 00 01 等级2的第二次回复,表示彻底完成
8号控制报文:SUBSCRIBE - 订阅主题
客户端向服务端发送 SUBSCRIBE 报文用于创建一个或多个订阅。 为了将应用消息转发给与那些订阅匹配的主题,服务端发送 PUBLISH 报文给客户端。 SUBSCRIBE报文也(为每个订阅)指定了最大的 QoS 等级,服务端根据这个发送应用消息给客户端。
报文内容组成: 固定报头 报文类型82+剩余长度 可变报头 报文标识符两个字节。 有效负载 主题过滤器长度两字节+主题过滤器(+【可包含多个】后一个主题过滤器长度两字节+主题过滤器+)服务质量等级1字节仅低两位有效
SUBSCRIBE 报文的有效载荷 必须包含至少一对主题过滤器 和 QoS 等级字段组合。 每一个过滤器后面跟着一个字节,这个字节被叫做服务质量要求(Requested QoS)。它给出了服务端向客户端发送应用消息所允许的最大 QoS 等级。
报文举例: 82 0E 00 0A 00 09 61 70 70 5F 74 6F 70 69 63 00 等级0 82 0E 00 0B 00 09 61 70 70 5F 74 6F 70 69 63 01 等级1 订阅的主题名为app_topic:61 70 70 5F 74 6F 70 69 63 ,前面的00 09是主题名长度
9号控制报文:SUBACK – 订阅确认
服务端发送 SUBACK 报文给客户端,用于确认它已收到并且正在处理 SUBSCRIBE 报文。 SUBACK 报文包含一个返回码清单,它们指定了 SUBSCRIBE 请求的每个订阅被授予的最大 QoS 等级。
报文内容组成: 固定报头 报文类型90+剩余长度 可变报头 可变报头包含等待确认的 SUBSCRIBE 报文的报文标识符。两个字节。 有效负载 针对上一个订阅主题报文的等级回复。字节数与上一个SUBSCRIBE报文中的主题过滤器数量相同。 有效载荷包含一个返回码清单。每个返回码对应等待确认的 SUBSCRIBE 报文中的一个主题过滤器。返回码的顺序必须和 SUBSCRIBE 报文中主题过滤器的顺序相同。 允许的返回码值: 0x00 - 最大 QoS 0 0x01 - 成功 – 最大 QoS 1 0x02 - 成功 – 最大 QoS 2 0x80 - Failure 失败
报文举例: 90 03 | 00 0A | 00 对应上面订阅第一条的报文标识符 90 03 | 00 0B | 01 对应上面订阅第二条的报文标识符
10号控制报文:UNSUBSCRIBE – 取消订阅
客户端发送 UNSUBSCRIBE 报文给服务端,用于取消订阅主题。
报文内容组成: 固定报头 报文类型A2+剩余长度 可变报头 可变报头包含一个报文标识符。两个字节。 有效负载 UNSUBSCRIBE 报文的有效载荷包含客户端想要取消订阅的主题过滤器列表。长度两字节+主题过滤器(+长度两字节+主题过滤器) UNSUBSCRIBE 报文的有效载荷 必须至少包含一个消息过滤器。
报文举例: A2 0D 00 0C 00 09 61 70 70 5F 74 6F 70 69 63 00 0C是报文标识符 主题名app_topic,即61 70 70 5F 74 6F 70 69 63,前面的00 09是长度
11号控制报文:UNSUBACK – 取消订阅确认
服务端 必须发送 UNSUBACK 报文响应客户端的 UNSUBSCRIBE 请求。UNSUBACK 报文 必须包含和UNSUBSCRIBE 报文相同的报文标识符
报文内容组成: 固定报头 报文类型B0+剩余长度02 可变报头 可变报头包含等待确认的 UNSUBSCRIBE 报文的报文标识符。两个字节。 有效负载 无
报文举例: B0 02 00 0C 00 0C对应上一条取消订阅的报文标志符
12号控制报文:PINGREQ – 心跳请求
客户端发送 PINGREQ 报文给服务端的。用于:
在没有任何其它控制报文从客户端发给服务的时,告知服务端客户端还活着。请求服务端发送 响应确认它还活着。使用网络以确认网络连接没有断开。
报文内容组成: 固定报头 报文类型C0+剩余长度00 可变报头 无 有效负载 无 服务端 必须发送 PINGRESP 报文响应客户端的 PINGREQ 报文
报文: C0 00 (只有这一种)
13号控制报文:PINGRESP – 心跳响应
服务端发送 PINGRESP 报文响应客户端的 PINGREQ 报文。表示服务端还活着。
报文内容组成: 固定报头 报文类型D0+剩余长度00 可变报头 无 有效负载 无
报文: D0 00 (只有这一种)
14号控制报文:DISCONNECT – 断开连接
DISCONNECT 报文是客户端发给服务端的最后一个控制报文。表示客户端"正常"断开连接。
报文内容组成: 固定报头 报文类型E0+剩余长度00 可变报头 无 有效负载 无
报文: E0 00 (只有这一种)
注意:客户端发送 DISCONNECT 报文之后: 1.必须关闭网络连接。 2.不能通过那个网络连接再发送任何控制报文 。 服务端在收到 DISCONNECT 报文时: 1.必须丢弃任何与当前连接关联的未发布的遗嘱消息。 2.应该关闭网络连接,如果客户端 还没有这么做。
关于CONNECT控制报文中的连接标志
关于密码标志位: 如果密码(Password)标志被设置为 0,有效载荷中不能包含密码字段。 如果密码(Password)标志被设置为 1,有效载荷中必须包含密码字段。 如果用户名标志被设置为 0,密码标志也必须设置为 0。
关于用户名标志位: 如果用户名(User Name)标志被设置为 0,有效载荷中 不能包含用户名字段。 如果用户名(User Name)标志被设置为 1,有效载荷中 必须包含用户名字段。
关于遗嘱保留位: 如果遗嘱标志被设置为 0,遗嘱保留(Will Retain)标志也 必须设置为 0。 如果遗嘱标志被设置为 1: 1.如果遗嘱保留被设置为 0,服务端必须将遗嘱消息当作非保留消息发布。 2.如果遗嘱保留被设置为 1,服务端必须将遗嘱消息当作保留消息发布。
关于遗嘱QoS位: 这两位用于指定发布遗嘱消息时使用的服务质量等级。 如果遗嘱标志被设置为 0,遗嘱 QoS 也 必须设置为 0(0x00)。 如果遗嘱标志被设置为 1,遗嘱 QoS 的值可以等于 0(0x00),1(0x01),2(0x02)。它的值不能等于3。
关于遗嘱标志位: 遗嘱消息由服务端发布,通常是断开连接后紧接着发布的消息 遗嘱标志(Will Flag)被设置为 1,表示如果连接请求被接受了,遗嘱(Will Message)消息(在有效载荷中)必须被存储在服务端并且与这个网络连接关联。之后网络连接关闭时,服务端必须发布这个遗嘱消息,除非服务端收到DISCONNECT 报文时删除了这个遗嘱消息
遗嘱消息发布的条件,包括但不限于:
服务端检测到了一个 I/O 错误或者网络故障。客户端在保持连接(Keep Alive)的时间内未能通讯。客户端没有先发送 DISCONNECT 报文直接关闭了网络连接。由于协议错误服务端关闭了网络连接。
如果遗嘱标志被设置为 1,连接标志中的 Will QoS 和 Will Retain 字段会被服务端用到,同时有效载荷中必须包含 Will Topic 和 Will Message 字段。一旦被发布或者服务端收到了客户端发送的 DISCONNECT 报文,遗嘱消息就必须从存储的会话状态中移除。 如果遗嘱标志被设置为 0,连接标志中的 Will QoS 和 Will Retain 字段 必须设置为 0,并且有效载荷中 不能包含 Will Topic 和 Will Message 字段。网络连接断开时, 服务端更加不能发送遗嘱消息。 服务端应该在条件成立时迅速发布遗嘱消息。在关机或故障的情况下,服务端可以推迟遗嘱消息的发布直到之后的重启。 如果发生了这种情况,在服务器故障和遗嘱消息被发布之间可能会有一个延迟。
关于清理会话标志: 指定了会话状态的处理方式。客户端和服务端可以保存会话状态,以支持跨网络连接的可靠消息传输。这个标志位用于控制会话状态的 生存时间。 如果清理会话(CleanSession)标志被设置为 1,客户端和服务端 必须丢弃之前的任何会话并开始一个新的会话。会话仅持续和网络连接同样长的时间。与这个会话关联的状态数据不能被任何之后的会话重用。 清理会话标志设置为 1 的客户端不会收到旧的应用消息,而且在每次连接成功后都需要重新订阅任何相关的主题。 为了确保在发生故障时状态的一致性,客户端应该使用会话状态标志 1 重复请求连接,直到连接成功。 (通常使用置1即可)
关于CONNECT控制报文中的保持连接时间
保持连接(Keep Alive)是一个以秒为单位的时间间隔,表示为一个 16 位的字,它是指在客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。客户端负责保证控制报文发送的时间间隔不超过保持连接的值。如果没有任何其它的控制报文可以发送,客户端 必须发送一个PINGREQ 报文。 不管保持连接的值是多少,客户端任何时候都可以发送 PINGREQ 报文,并且使用 PINGRESP 报文判断网络和服务端的活动状态。 如果保持连接的值非零,并且服务端在1.5倍的保持连接时间内没有收到客户端的控制报文,它必须断开客户端的网络连接,认为网络连接已断开。 客户端发送了 PINGREQ 报文之后,如果在合理的时间内仍没有收到 PINGRESP 报文,它 应该关闭到服务端的网络连接。 保持连接的值为零表示关闭保持连接功能。这意味着,服务端不需要因为客户端不活跃而断开连接,即可以一直保持连接。注意: 不管保持连接的值是多少,任何时候,只要服务端认为客户端是不活跃或无响应的,可以断开客户端的连接。 保持连接的实际值是由应用指定的,一般是几分钟。允许的最大值是 18 小时 12 分 15 秒。