本次博文即后续一系列博文,将就MQTT协议3.1.1和MQTT协议5.0进行介绍和描述,主要是依据官方文档进行翻译和再次编辑,将同步区别部分进行细化。另外还会推荐一些mqtt的实现,由于mqtt 5今年新出,mqtt 3版本的broker已经有许多优秀的开源项目,mqtt5完全实现的broker较少。
MQTT是物联网的协议,英文全称为,Message Queuing Telemetry Transport,被翻译为,消息队列遥测传输。目前为止,如今最常用的是MQTT3.1.1协议,因为是在2014年10月29日发布了最终稿,MQTT5.0的草案从2017年至今年上半年3月7号才最终确定,只是5.0的第一个版本。MQTT的初始第一版由IBM在1999年发布。
MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议,构建于TCP/IP或者其他有序、可靠、双向连接的网络连接上。
它的特点有:
使用发布/订阅消息模式,提供了一对多的消息分发和应用之间的解耦。
消息传输不需要知道负载内容
提供三种等级的服务质量(QoS)
很小的传输消耗和协议数据交换,最大限度减少网络流量
异常连接断开发生时,能通知到相关各方
MQTT使用的底层传输协议基础设施。
MQTT协议通过网络传输应用数据。
MQTT传输传输的应用消息通过,包含关联的服务质量(QoS)和主题(Topic)名。
当MQTT传输应用程序消息,包含有效负载数据(payload data),服务质量(QoS),属性的集合(Properties)和主题名(Topic)。
使用MQTT的程序或设备。客户端总是通过网络连接到服务端。它可以
一个程序或设备,作为发送应用消息的客户端和请求订阅的客户端之间的中介。服务端
订阅包含一个主题过滤器(Topic Filter)和一个最大的服务质量(QoS)等级。订阅与单个会话(Session)关联。会话可以包含多于一个的订阅。会话的每个订阅都有一个不同的主题过滤器。
订阅中包含的一个表达式,用于表示相关的一个或多个主题。主题过滤器可以包含通配符。
客户端和服务端之间的状态交互。一些会话持续时长与网络连接一样,另一些可以在客户端和服务端的多个连续网络连接间扩展。
通过网络连接发送的信息数据包。MQTT规范定义了十四种不同类型的控制报文,其中一个(PUBLISH报文)用于传输应用消息。
MQTT5.0增加至15种,其中增加了AUTH。即Authentication exchange
一个共享订阅包含一个主题过滤器(Topic Filter)和一个最大的服务质量(QoS)等级。一个共享订阅可以与多个订阅会话相关联,便于支持大范围消息交换模式。与共享订阅匹配的应用消息仅发送到与这些会话之一相关联的客户端。一个会话可以包括多个共享订阅,可以同时包含共享订阅与非共享订阅。
由于是个人理解,所以有误部分,欢迎指出
共享订阅针对场景应是数据的生产者远超出数据消费者数量,而且同一条数据(消息)只需要被任意其中一个消费者处理一次。主要是实现消费者数量处理消息的均衡负载。
比如有一个发布者发布四条消息,只需要处理一次,此时有四个订阅者共享这四条消息,实现MQTT的broker端只需要把消息分别发送到四个订阅者便可以实现消息的处理。
通配符订阅是具有包含一个或多个通配符的主题过滤器的订阅。 这允许订阅匹配多个主题名称。
附加到应用程序消息的标签,该标签与服务器已知的订阅相匹配。
根据规范不能被正确解析的控制报文。
解析数据包后发现错误,发现该错误包含协议不允许的数据或与客户端或服务器的状态不一致的数据。
在网络连接正常关闭的情况下,服务器在网络连接关闭后发布的应用程序消息。
不应包含在UTF-8编码字符串中的Unicode控制代码和Unicode非字符集。
代码点(code point)是指与一个编码表中的某个字符对应的代码值。
字节中的位从0到7。第7位是最高有效位,第0位是最低有效位。
双字节整数数值是使用大端序(big-endian,高位字节在低位字节前面)的16位无符号整数。这意味着一个16位的字在网络上表示为最高有效位(MSB),后面跟着最低有效位(LSB)。
当物理单位长度大于1个字节时,需要区分字节顺序。关于大小端序,可以点击查看这篇博客。
四字节整数数据值是按大端序(big-endian,高位字节在低位字节前面)的32位无符号整数:高位字节先于连续的低位字节。 这意味着一个32位字在网络上显示为最高有效字节(MSB),然后是下一个最高有效字节(MSB),然后是下一个最高有效字节(MSB),然后是最低有效字节(LSB)。
后续描述的MQTT控制报文中的文本字段编码为 UTF-8 格式的字符串。UTF-8 是Unicode字符的有效编码,优化ASCII字符的编码以支持基于文本的通信。
除非另有说明,否则所有UTF-8编码的字符串长度都可以在0到65535字节之间。
图例 UTF-8编码字符串的结构
==UTF-8编码字符串中的字符数据必须是按照Unicode规范定义的有效的UTF-8格式,并在RFC3629中进行重述。特别需要指出的是,这些数据不能包含字符码在U+D800
和U+DFFF
之间的数据。==如果服务端或客户端收到了一个包含无效UTF-8字符的控制报文,则它是无效报文。
==UTF-8编码的字符串不能包含空字符U+0000
。==如果客户端或服务端收到了一个包含U+0000
的控制报文,则它是无效报文。
数据不应包含下面列出的Unicode代码点的编码。 如果接收者(服务器或客户端)接收到包含其中任何一个的MQTT控制报文,则可以将其视为无效报文。 这些是禁止的Unicode代码点。
U+0001..U+001F
控制字符U+007F..U+009F
控制字符U+0FFFF
)UTF-8编码的序列0xEF 0xBB 0xBF
始终被解释为U + FEFF
(零宽度非换行空白字符),无论它出现在字符串中的什么位置,都不得被数据报文接收者跳过或剥离。
例如,字符串 A?
它是拉丁文大写字母A,后跟代码点U + 2A6D4
(代表中日韩统一表意文字扩展B中的字符),其编码如下:
图例 UTF-8编码字符串非规范示例
剩余长度字段使用一个变长字节编码方案,对小于 128 的值它使用单字节编码。更大的值按下面的方式处理。低 7 位有效位用于编码数据,最高有效位用于指示是否有更多的字节。因此每个字节可以编码 128 个数值和一个延续位(continuation bit)。剩余长度字段最大 4 个字节,编码值必须使用表示该值所需的最小字节数,如下表所示。
可变字节整数的大小
字节数 | 最小值 | 最大值 |
---|---|---|
1 | 0 (0x00) | 127 (0x7F) |
2 | 128 (0x80, 0x01) | 16,383 (0xFF, 0x7F) |
3 | 16,384 (0x80, 0x80, 0x01) | 2,097,151 (0xFF, 0xFF, 0x7F) |
4 | 2,097,152 (0x80, 0x80, 0x80, 0x01) | 268,435,455 (0xFF, 0xFF, 0xFF, 0x7F) |
用于将非负整数X
编码为可变字节整数编码方案的算法如下:
do
encodedByte = X MOD 128
X = X DIV 128
// if there are more data to encode, set the top bit of this byte
// 如果还有更多数据要编码,请设置此字节的高位
if (X > 0)
encodedByte = encodedByte OR 128
endif
'output' encodedByte
while (X > 0)
MOD是模运算,DIV是整数除法,OR是位操作或(C语言中分别是%,/,|)
可变字节整数类型的解码算法如下:
multiplier = 1
value = 0
do
encodedByte = 'next byte from stream'
value += (encodedByte AND 127) * multiplier
if (multiplier > 128*128*128)
throw Error(Malformed Variable Byte Integer)
multiplier *= 128
while ((encodedByte AND 128) != 0)
AND 是位操作与(C 语言中的&)
这个算法终止时,value 包含的就是剩余长度的值。
二进制数据由一个双字节整数指示其数据长度,因此,二进制数据的长度被限制为0到65,535字节。
UTF-8字符串对由两个UTF-8编码的字符串组成,用来表示名字-值对,第一个字符串表示名字,第二个字符串表示值。
所有的字符串必须遵循UTF-8字符串编码规范,如果接受者(客户端或者服务端)接受到一个字符串对,然而其编码并不遵循规范,则此报文为无效报文。
MQTT客户端和服务器实现应提供身份验证,授权和安全通信选项,后续会有说明。强烈建议与关键基础结构,个人身份信息或其他个人或敏感信息有关的应用程序使用这些安全性功能。
MQTT协议通过以定义的方式交换一系列MQTT控制报文来进行操作。
MQTT控制报文最多由三部分组成,始终按照以下顺序显示,如下所示。
图例–MQTT控制报文的结构
每个MQTT控制包都包含一个固定报头,如下所示。
图例-固定报头的格式
位置:第一个字节,二进制位 7- 4。
表示为4位无符号值的值在下表中列出。
固定标头中第一个字节的剩余4位即[3-0]位,包含特定于每种MQTT控制数据包类型的标志,如下表所示。 如果标志位标记为“保留”,则保留该标志位以备将来使用,并且必须将其设置为列出的值。 如果收到非法的标志,则它是无效报文。
位置:从第2个字节开始。
剩余长度是一个可变字节整数,表示当前控制报文中剩余的字节数,包括可变报头和有效载荷中的数据。 剩余长度不包括用于编码剩余长度的字节数。 数据报文大小是MQTT控制数据报文中的字节总数,它等于固定报头的长度加上剩余长度。
非规范评注
例如,十进制数64会被编码为一个字节,数值是64,十六进制表示为0x40。十进制数字321(=65+2*128)被编码为两个字节,最低有效位在前。第一个字节是 65+128=193。注意最高位为1表示后面至少还有一个字节。第二个字节是2。
非规范评注
这允许应用发送最大256MB(268,435,455)大小的控制报文。这个数值在报文中的表示是:0xFF,0xFF,0xFF,0x7F。
某些MQTT控制报文包含一个可变报头部分。它在固定报头和有效载荷之间。可变报头的内容根据报文类型的不同而不同。可变报头的报文标识符(Packet Identifier)字段存在于在多个类型的报文里。
许多MQTT控制数据报文类型的可变报头组件都包含一个2字节整数数据包标识符字段。 这些MQTT控制数据报文是PUBLISH(当QoS> 0),PUBACK,PUBREC,PUBREL,PUBCOMP,SUBSCRIBE,SUBACK,UNSUBSCRIBE和UNSUBACK。
需要数据报文标识符的MQTT控制数据报文如下所示:
MQTT 控制报文 | 报文标识符字段 |
---|---|
CONNECT | 不需要 |
CONNACK | 不需要 |
PUBLISH | 需要(如果 QoS > 0) |
PUBACK | 需要 |
PUBREC | 需要 |
PUBREL | 需要 |
PUBCOMP | 需要 |
SUBSCRIBE | 需要 |
SUBACK | 需要 |
UNSUBSCRIBE | 需要 |
UNSUBACK | 需要 |
PINGREQ | 不需要 |
PINGRESP | 不需要 |
DISCONNECT | 不需要 |
AUTH | 不需要 |
AUTH是MQTT5.0版本的
客户端和服务端彼此独立地分配报文标识符。因此,客户端服务端组合使用相同的报文标识符可以实现并发的消息交换。
客户端发送标识符为0x1234的PUBLISH报文,它有可能会在收到那个报文的PUBACK之前,先收到服务端发送的另一个不同的但是报文标识符也为0x1234的PUBLISH报文。
CONNECT,CONNACK,PUBLISH,PUBACK,PUBREC,PUBREL,PUBCOMP,SUBSCRIBE,SUBACK,UNSUBSCRIBE,UNSUBACK,DISCONNECT和AUTH数据报文的可变报头中的最后一个字段是一组属性。 在CONNECT数据包中,有效载荷的遗愿Properties字段中还有一组可选的Properties。
属性字段由属性长度和所有属性组成。
属性长度被编码为可变字节整数。属性长度不包括用于编码自身的字节,但包括属性的长度。如果没有属性,则必须通过包含零的属性长度来指示。
属性
一个属性由一个标识符定义,该标识符定义其用途和数据类型,后跟一个值。 标识符被编码为可变字节整数。 包含对它的数据报文类型无效的标识符或包含的值不是指定数据类型的控制数据报文是无效数据报文。 如果收到,服务端或客户端使用包含原因码0x81(无效报文)CONNACK或DISCONNECT报文进行错误处理。 具有不同标识符的属性的顺序没有意义。
无法确定的翻译,怕译不准确,以括号原始英文给出,例如Server Reference
尽管属性标识符被定义为可变字节整数,但在MTQQ5.0的规范中,所有属性标识符的长度均为一个字节。
一些MQTT控制数据报文包含有效载荷作为数据报文的最后部分。 在PUBLISH 报文中,是指应用消息。
黄色部分突出显示的是MQTT5和MQTT3.1.1的不同
MQTT5新增
原因码是一个无符号的单字节值,指示操作的结果。 原因代码小于0x80表示操作成功完成。 成功的常规原因码为0。原因码值为0x80或更大表示失败。
CONNACK,PUBACK,PUBREC,PUBREL,PUBCOMP,DISCONNECT和AUTH控制数据报文具有单个原因码作为变量头的一部分。 SUBACK和UNSUBACK数据包在有效负载中包含一个或多个原因代码的列表。
原因码共享一组通用值,如下所示。
对于原因码0x91(正在使用的数据报文标识符),对此的响应是尝试修复状态,或者通过使用设置为1的“Clean Start”标志位进行连接来重置会话状态,或者确定客户端或服务器实现是否有缺陷。
MQTT5增加了共享订阅,属性等内容,实现负载均衡。下一篇博文将会介绍MQTT控制报文的第一个报文,CONNECT报文。