MQTT协议3.1.1中文版,对于中文文档,最新的版本,可以通过gitbook阅读。大家也可以下载以往版本的文档查看。
这里写这篇文章主要是为了后面的Netty应用(七) ---- mqtt服务开发做基础,所以想着这里做下铺垫,这里只是对基本组成和概念进行了解。
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅范式的“轻量级”消息协议,由 IBM 发布。
IoT 设备要运作,就必须连接到互联网,设备才能相互协作,以及与后端服务协同工作。而互联网的基础网络协议是 TCP/IP,MQTT 协议是基于 TCP/IP 协议栈而构建的。
MQTT 控制报文由三部分组成,如下图
每个 MQTT 控制报文都包含一个固定报头。固定报头的格式如下图
位置:第 1 个字节,二进制位 7-4。表示为 4 位无符号值,这些值的定义如下图
固定报头第1个字节的剩余的4位 [3-0]包含每个MQTT控制报文类型特定的标志,如下图,图中任何标记为“保留”的标志位,都是保留给以后使用的,必须设置为表格中列出的值。如果收到非法的标志,接收者必须关闭网络连接。
PUBLISH控制报文中的DUP, QoS和RETAIN标志的描述见 3.3节。
位置:从第2个字节开始。
剩余长度(Remaining Length)表示当前报文剩余部分的字节数,包括可变报头和负载的数据。剩余长度不包括用于编码剩余长度字段本身的字节数。
剩余长度字段使用一个变长度编码方案,对小于128的值它使用单字节编码。更大的值按下面的方式处理。低7位有效位用于编码数据,最高有效位用于指示是否有更多的字节。因此每个字节可以编码128个数值和一个延续位(continuation bit)。剩余长度字段最大4个字节。
允许应用发送最大256MB(268,435,455)大小的控制报文。这个数值在报文中的表示是:0xFF,0xFF,0xFF,0x7F。
下图中展示了剩余长度字段所表示的值随字节增长。
某些MQTT控制报文包含一个可变报头部分。它在固定报头和负载之间。可变报头的内容根据报文类型的不同而不同。可变报头的报文标识符(Packet Identifier)字段存在于在多个类型的报文里。
简单的来理解,报文标识符就是一个消息id,用于服务端和客户端消息确认。
很多控制报文的可变报头部分包含一个两字节的报文标识符字段。这些报文是PUBLISH(QoS > 0时), PUBACK,PUBREC,PUBREL,PUBCOMP,SUBSCRIBE, SUBACK,UNSUBSCRIBE,UNSUBACK。
SUBSCRIBE,UNSUBSCRIBE和PUBLISH(QoS大于0)控制报文必须包含一个非零的16位报文标识符(Packet Identifier)。客户端每次发送一个新的这些类型的报文时都必须分配一个当前未使用的报文标识符 。如果一个客户端要重发这个特殊的控制报文,在随后重发那个报文时,它必须使用相同的标识符。当客户端处理完这个报文对应的确认后,这个报文标识符就释放可重用。QoS 1的PUBLISH对应的是PUBACK,QoS 2的PUBLISH对应的是PUBCOMP,与SUBSCRIBE或UNSUBSCRIBE对应的分别是SUBACK或UNSUBACK 。发送一个QoS 0的PUBLISH报文时,相同的条件也适用于服务端。
QoS等于0的PUBLISH报文不能包含报文标识符。
PUBACK, PUBREC, PUBREL报文必须包含与最初发送的PUBLISH报文相同的报文标识符 。类似地,SUBACK和UNSUBACK必须包含在对应的SUBSCRIBE和UNSUBSCRIBE报文中使用的报文标识符。
需要报文标识符的控制报文在下图中列出。
客户端和服务端彼此独立地分配报文标识符。因此,客户端服务端组合使用相同的报文标识符可以实现并发的消息交换。
某些MQTT控制报文在报文的最后部分包含一个有效载荷,这将在第3节论述。对于PUBLISH来说有效载荷就是应用消息。下图列出了需要有效载荷的控制报文。
客户端到服务端的网络连接建立后,客户端发送给服务端的第一个报文必须是CONNECT报文。
在一个网络连接上,客户端只能发送一次CONNECT报文。服务端必须将客户端发送的第二个CONNECT报文当作协议违规处理并断开客户端的连接。
有效载荷包含一个或多个编码的字段。包括客户端的唯一标识符,Will主题,Will消息,用户名和密码。除了客户端标识之外,其它的字段都是可选的,基于标志位来决定可变报头中是否需要包含这些字段。
固定报头的格式都是一样的,这里不再重复叙述,主要看下可变报头和payload的差异。CONNECT报文的可变报头按下列次序包含四个字段:协议名(Protocol Name),协议级别(Protocol Level),连接标志(Connect Flags)和保持连接(Keep Alive)。
协议名是表示协议名 MQTT 的UTF-8编码的字符串。MQTT规范的后续版本不会改变这个字符串的偏移和长度。如果协议名不正确服务端可以断开客户端的连接,也可以按照某些其它规范继续处理CONNECT报文。对于后一种情况,按照本规范,服务端不能继续处理CONNECT报文。
客户端用8位的无符号值表示协议的修订版本。对于3.1.1版协议,协议级别字段的值是4(0x04)。如果发现不支持的协议级别,服务端必须给发送一个返回码为0x01(不支持的协议级别)的CONNACK报文响应CONNECT报文,然后断开客户端的连接。
连接标志字节包含一些用于指定MQTT连接行为的参数。它还指出有效载荷中的字段是否存在。
服务端必须验证CONNECT控制报文的保留标志位(第0位)是否为0,如果不为0必须断开客户端连接。
清理会话 Clean Session
位置:连接标志字节的第1位,这个二进制位指定了会话状态的处理方式。
如果清理会话(CleanSession)标志被设置为1,客户端和服务端必须丢弃之前的任何会话并开始一个新的会话。会话仅持续和网络连接同样长的时间。与这个会话关联的状态数据不能被任何之后的会话重用。
如果清理会话(CleanSession)标志被设置为0,服务端必须基于当前会话(使用客户端标识符识别)的状态恢复与客户端的通信。如果没有与这个客户端标识符关联的会话,服务端必须创建一个新的会话。当连接断开后,客户端和服务端必须保存会话信息 。
一般来说,客户端连接时总是将清理会话标志设置为0或1,并且不交替使用两种值。这个选择取决于具体的应用。清理会话标志设置为1的客户端不会收到旧的应用消息,而且在每次连接成功后都需要重新订阅任何相关的主题。清理会话标志设置为0的客户端会收到所有在它连接断开期间发布的QoS
1和QoS 2级别的消息。因此,要确保不丢失连接断开期间的消息,需要使用QoS 1或 QoS 2级别,同时将清理会话标志设置为0。
遗嘱标志 Will Flag
位置:连接标志的第2位。
如果遗嘱标志被设置为0,连接标志中的Will QoS和Will Retain字段必须设置为0,并且有效载荷中不能包含Will Topic和Will Message字段。
遗嘱标志(Will Flag)被设置为1,表示如果连接请求被接受了,遗嘱(Will Message)消息必须被存储在服务端并且与这个网络连接关联。之后网络连接关闭时,服务端必须发布这个遗嘱消息,除非服务端收到DISCONNECT报文时删除了这个遗嘱消息。如果遗嘱标志被设置为1,连接标志中的Will QoS和Will Retain字段会被服务端用到,同时有效载荷中必须包含Will Topic和Will Message字段,用于发布这个遗嘱消息。
遗嘱消息发布的条件,包括但不限于:
遗嘱QoS Will QoS
位置:连接标志的第4和第3位。这两位用于指定发布遗嘱消息时使用的服务质量等级。
如果遗嘱标志被设置为0,遗嘱QoS也必须设置为0(0x00) 。
如果遗嘱标志被设置为1,遗嘱QoS的值可以等于0(0x00),1(0x01),2(0x02)。它的值不能等于3。
遗嘱保留 Will Retain
位置:连接标志的第5位。如果遗嘱消息被发布时需要保留,需要指定这一位的值。
如果遗嘱标志被设置为0,遗嘱保留(Will Retain)标志也必须设置为0 。
如果遗嘱标志被设置为1:
用户名标志 User Name Flag
位置:连接标志的第7位。
如果用户名(User Name)标志被设置为0,有效载荷中不能包含用户名字段。
如果用户名(User Name)标志被设置为1,有效载荷中必须包含用户名字段。
密码标志 Password Flag
位置:连接标志的第6位。
如果密码(Password)标志被设置为0,有效载荷中不能包含密码字段。
如果密码(Password)标志被设置为1,有效载荷中必须包含密码字段。
如果用户名标志被设置为0,密码标志也必须设置为0
保持连接(Keep Alive)是一个以秒为单位的时间间隔,表示为一个16位的字,它是指在客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。客户端负责保证控制报文发送的时间间隔不超过保持连接的值。如果没有任何其它的控制报文可以发送,客户端必须发送一个PINGREQ报文 。
服务端发送CONNACK报文响应从客户端收到的CONNECT报文。服务端发送给客户端的第一个报文必须是CONNACK 。如果客户端在合理的时间内没有收到服务端的CONNACK报文,客户端应该关闭网络连接。
连接确认固定报头中的剩余长度字段为2,表示可变头长度为2个字节,没有有效载荷。
可变报头的结构如下图:
第1个字节是 连接确认标志,位7-1是保留位且必须设置为0。 第0 (SP)位 是当前会话(Session Present)标志。
当前会话 Session Present
位置:连接确认标志的第0位。
如果服务端收到清理会话(CleanSession,这里指客户端发送给服务端的Connect报文中的清理会话标志)标志为1的连接,除了将CONNACK报文中的返回码设置为0之外,还必须将CONNACK报文中的当前会话设置(Session Present)标志为0 。
如果服务端收到一个CleanSession为0的连接,当前会话标志的值取决于服务端是否已经保存了ClientId对应客户端的会话状态。如果服务端已经保存了会话状态,它必须将CONNACK报文中的当前会话标志设置为1,没有就保存则设置为0,并且还需要将CONNACK报文中的返回码设置为0。
当前会话标志使服务端和客户端在是否有已存储的会话状态上保持一致。
如果服务端发送了一个包含非零返回码的CONNACK报文,它必须将当前会话标志设置为0 。
位置:可变报头的第2个字节。
连接返回码字段使用一个字节的无符号值,如下图。如果服务端收到一个合法的CONNECT报文,但出于某些原因无法处理它,服务端应该尝试发送一个包含非零返回码的CONNACK报文。如果服务端发送了一个包含非零返回码的CONNACK报文,那么它必须关闭网络连接 。
PUBLISH控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。
发布消息固定报文中的控制报文类型特定的标志不是保留的。如下图,里面有四个属性。
位置:第1个字节,第3位
如果DUP标志被设置为0,表示这是客户端或服务端第一次请求发送这个PUBLISH报文,即不是重发的报文。如果DUP标志被设置为1,表示这可能是一个早前报文请求的重发。
对于QoS 0的消息,DUP标志必须设置为0
位置:第1个字节,第2-1位。
这个字段表示应用消息分发的服务质量等级保证。
PUBLISH报文不能将QoS所有的位设置为1,也就是Qos不能等于3。如果服务端或客户端收到QoS所有位都为1的PUBLISH报文,它必须关闭网络连接 。关于Qos详细的过程,在第4节介绍。
位置:第1个字节,第0位。
如果客户端发给服务端的PUBLISH报文的保留(RETAIN)标志被设置为1,服务端必须存储这个应用消息和它的服务质量等级(QoS),以便它可以被分发给未来的主题名匹配的订阅者。
如果客户端发给服务端的PUBLISH报文的保留标志位0,服务端不能存储这个消息也不能移除或替换任何现存的保留消息 。
主题名(Topic Name)用于识别有效载荷数据应该被发布到哪一个信息通道。
主题名必须是PUBLISH报文可变报头的第一个字段。PUBLISH报文中的主题名不能包含通配符。
服务端发送给订阅客户端的PUBLISH报文的主题名必须匹配该订阅的主题过滤器。
只有当QoS等级是1或2时,报文标识符(Packet Identifier)字段才能出现在PUBLISH报文中。
下图为一个要订阅a/b主题的发布消息报文的可变头结构
PUBACK报文是对QoS 1等级的PUBLISH报文的响应。
可变报头中只包含等待确认的PUBLISH报文的报文标识符。无有效载荷。
PUBREC报文是对QoS等级2的PUBLISH报文的响应。它是QoS 2等级协议交换的第二个报文。
可变报头包含等待确认的PUBLISH报文的报文标识符。没有有效载荷。
PUBREL报文是对PUBREC报文的响应。它是QoS 2等级协议交换的第三个报文。
可变报头包含与等待确认的PUBREC报文相同的报文标识符。没有有效载荷。
PUBCOMP报文是对PUBREL报文的响应。它是QoS 2等级协议交换的第四个也是最后一个报文。
可变报头包含与等待确认的PUBREL报文相同的报文标识符。无有效载荷。
客户端向服务端发送SUBSCRIBE报文用于创建一个或多个订阅。每个订阅注册客户端关心的一个或多个主题。为了将应用消息转发给与那些订阅匹配的主题,服务端发送PUBLISH报文给客户端。SUBSCRIBE报文也(为每个订阅)指定了最大的QoS等级,服务端根据这个发送应用消息给客户端。
可变报头包含报文标识符。主要看下有效载荷。
SUBSCRIBE报文的有效载荷包含了一个主题过滤器列表,它们表示客户端想要订阅的主题。SUBSCRIBE报文有效载荷中的主题过滤器列表必须是1.5.3节定义的UTF-8字符串。服务端应该支持包含通配符的主题过滤器。如果服务端选择不支持包含通配符的主题过滤器,必须拒绝任何包含通配符过滤器的订阅请求。每一个过滤器后面跟着一个字节,这个字节被叫做服务质量要求(Requested QoS)。它给出了服务端向客户端发送应用消息所允许的最大QoS等级。
SUBSCRIBE报文的有效载荷必须包含至少一对主题过滤器 和 QoS等级字段组合。没有有效载荷的SUBSCRIBE报文是违反协议的。
请求的最大服务质量等级字段编码为一个字节,它后面跟着UTF-8编码的主题名,那些主题过滤器 /和QoS等级组合是连续地打包。
服务端发送SUBACK报文给客户端,用于确认它已收到并且正在处理SUBSCRIBE报文。
SUBACK报文包含一个返回码清单,它们指定了SUBSCRIBE请求的每个订阅被授予的最大QoS等级。
可变报头包含等待确认的SUBSCRIBE报文的报文标识符。主要看有效载荷。
允许的返回码值:
客户端发送 UNSUBSCRIBE 报文给服务端,用于取消订阅主题。
可变报头包含一个报文标识符,主要看有效载荷。
UNSUBSCRIBE 报文的有效载荷包含客户端想要取消订阅的主题过滤器列表。UNSUBSCRIBE 报文的有效载荷必须至少包含一个消息过滤器。
服务端发送 UNSUBACK 报文给客户端用于确认收到 UNSUBSCRIBE 报文。
可变报头包含等待确认的 UNSUBSCRIBE 报文的报文标识符。无有效载荷。
客户端发送 PINGREQ 报文给服务端的。用于:
无可变报头,也无有效载荷。
服务端发送 PINGRESP 报文响应客户端的 PINGREQ 报文。表示服务端还活着。
无可变报头,也无有效载荷。
DISCONNECT 报文是客户端发给服务端的最后一个控制报文。表示客户端正常断开连接。
无可变报头,也无有效载荷。
客户端发送 DISCONNECT 报文之后:
? 必须关闭网络连接。
? 不能通过那个网络连接再发送任何控制报文。
服务端在收到 DISCONNECT 报文时:
? 必须丢弃任何与当前连接关联的未发布的遗嘱消息。
? 应该关闭网络连接,如果客户端 还没有这么做。
消息的分发依赖于底层网络的能力。接收者不会发送响应,发送者也不会重试。消息可能送达一次也可能根本没送达。
对于 QoS 0 的分发协议,发送者
对于 QoS 0 的分发协议,接收者
服务质量确保消息至少送达一次。QoS 1 的 PUBLISH 报文的可变报头中包含一个报文标识符,需要PUBACK 报文确认。
对于 QoS 1 的分发协议,发送者
对于 QoS 1 的分发协议,接收者
这是最高等级的服务质量,消息丢失和重复都是不可接受的。使用这个服务质量等级会有额外的开销。
QoS2 的消息可变报头中有报文标识符。QoS2 的 PUBLISH报文的接收者使用一个两步确认过程来确认收到。
对于 QoS2 的分发协议,发送者
对于QoS2的分发协议,接收者