消息类型
发布
当 MQTT client 在连接到 broker 之后就可以发送消息了,MQTT 使用的是基于 topic 主题的过滤。每条消息都应该包含一个 topic ,broker 可以使用 topic 将消息发送给感兴趣的 client。除此之外,每条消息还会包含一个负载(Payload)
,Payload 中包含要以字节形式发送的数据。
MQTT 是数据无关性的,也就是说数据是由发布者 - publisher 决定要发送的是 XML 、JSON 还是二进制数据、文本数据。
MQTT 中的 PUBLISH 消息结构如下。
Packet Identifier
:这个 PacketId 标识在 client 和 broker 之间唯一的消息标识。packetId 仅与大于零的 Qos 级别相关。TopicName
:主题名称是一个简单的字符串,/
代表着分层结构。Qos
:这个数字表示的是服务质量水平,服务质量水平有三个等级:0、1 和 2,服务级别决定了消息到达 client 或者 broker 的保证类型,来决定消息是否丢失。RetainFlag
:这个标志表示 broker 将最近收到的一条 RETAIN 标志位为true
的消息保存在服务器端(内存或者文件)。
MQTT 服务器只会为每一个 Topic 保存最近收到的一条 RETAIN 标志位为
true
的消息。也就是说,如果MQTT 服务器上已经为某个 Topic 保存了一条 Retained 消息,当客户端再次发布一条新的 Retained 消息时,那么服务器上原来的那条消息会被覆盖。
Payload
:这个是每条消息的实际内容。MQTT 是数据无关性的。可以发送任何文本、图像、加密数据以及二进制数据。Dupflag
:这个标志表示该消息是重复的并且由于预期的 client 或者 broker 没有确认所以重新发送了一次。这个标志仅仅与 Qos 大于 0 相关。
当 client 向 broker 发送消息时,broker 会读取消息,根据 Qos 的级别进行消息确认,然后处理消息。处理消息其实就是确定哪些 subscriber 订阅了 topic 并将消息发送给他们。
最初发布消息的 client 只关心将 PUBLISH 消息发送给 broker,一旦 broker 收到 PUBLISH 消息,broker 就有责任将其传递给所有 subscriber。发布消息的 client 不会知道是否有人对发布的消息感兴趣,同时也不知道多少 client 从 broker 收到了消息。
订阅
client 会向 broker 发送 SUBSCRIBE 消息来接收有关感兴趣的 topic,这个 SUBSCRIBE 消息非常简单,它包含了一个唯一的数据包标识和一个订阅列表。
Packet Identifier
:这个 PacketId 和上面的 PacketId 一样,都表示消息的唯一标识符。ListOfSubscriptions
:SUBSCRIBE 消息可以包含一个 client 的多个订阅,每个订阅都会由一个 topic 和一个 Qos 构成。订阅消息中的 topic 可以包含通配符。
确认消息
client 在向 broker 发送 SUBSCRIBE 消息后,为了确认每个订阅,broker 会向 client 发送 SUBACK 确认消息。这个 SUBACK 包含原始 SUBSCRIBE 消息的 packetId 和返回码列表。
其中
Packet Identifier
:这个数据包标识符和 SUBSCRIBE 中的相同。ReturnCode
:broker 为每个接收到的 SUBSCRIBE 消息的 topic/Qos 对发送一个返回码。例如,如果 SUBSCRIBE 消息有五个订阅消息,则 SUBACK 消息包含五个返回码作为响应。
到现在我们已经探讨过了三种消息类型,发布 - 订阅 - 确认消息,这三种消息的示意图如下。
退订
SUBSCRIBE 消息对应的是 UNSUBSCRIBE
消息,这条消息发送后,broker 会删除关于 client 的订阅。所以,UNSUBSCRIBE 消息与 SUBSCRIBE 消息类似,都具有 packetId 和 topic 列表。
确认退订
取消订阅也需要 broker 的确认,此时 broker 会向 client 发送一个 UNSUBACK
消息,这个 UNSUBACK 消息非常简单,只有一个 packetId 数据标识符。
退订和确认退订的流程如下。
当 client 收到来自 broker 的 UNSUBACK 消息后,就可以认为 UNSUBSCRIBE 消息中的订阅被删除了。
聊聊 Topic
聊了这么多关于 MQTT 的内容,但是我们还没有好好聊过 Topic。在 MQTT 中,Topic 是指 broker 为每个连接的 client 过滤消息的 UTF-8
字符串。Topic 是一种分层的结构,可以由一个或者多个 Topic 组成。每个 Topic 由 /
进行分割。
与传统的消息队列相比,MQTT Topic 非常轻量级,client 在发布或订阅之前不需要先创建所需要的 Topic,broker 在接收每个 Topic 前不用进行初始化操作。
通配符
当客户端订阅 Topic 时,它可以订阅已发布消息的确切 Topic,也可以使用通配符来同时订阅多个 Topic。通配符有两种:单级和多级。
单级通配符
单级通配符可以替换 Topic 的一个级别,+
号代表 Topic 中的单级通配符。
如果 Topic 包含任意字符串而不是通配符,则任何 Topic 都能够和单级通配符匹配。例如
myhome/groundfloor/+/temperature 就有下面这几种匹配方式。
多级通配符
多级通配符涵盖多个 Topic,#
代表 Topic 中的多级通配符。为了让 broker 能够确定和哪些 Topic 匹配,多级通配符必须作为 Topic 中的最后一个字符放置,并以 /
开头。
下面是 myhome/groundfloor/# 的几个例子
当 client 订阅带有多级通配符的 Topic 时,不论 Topic 有多长多深,它都会收到通配符之前 Topic 的所有消息。如果你只将 Topic 定义为 # 的话,那么你将会收到所有的消息。
总结
这篇文章的起因是有小伙伴留言说想要了解一下 MQTT 协议,因为之前没有了解过这个协议,更别谈写了。MQTT 相当于是舒适区之外的新东西,是值得花时间研究的,这也是我写这篇文章的原因。
文中可能有写的不太好或者有勘误的地方,大家多多提出建设性意见呀~ 我们下篇文章见。