---
doc_type: hypothesis-highlights
url: 'https://www.cnblogs.com/cxuanBlog/p/14917187.html'
---

## Metadata
- Author: [cnblogs.com]()
- Title: MQTT 协议是个啥？这篇文章告诉你！ - 程序员cxuan - 博客园
- Reference: https://www.cnblogs.com/cxuanBlog/p/14917187.html
- Category: #article

## Page Notes
## Highlights

![image](https://myonemanager.lzybetter.repl.co/picbed_big/picbed/abd5baa3d078f37535e2d82720b201b6.png)

- 还有一个优势就是 MQTT 在客户端容易实现，而且具有易用性，非常适合当今资源有限的设备。 

- MQTT 协议与 HTTP 相比具有一个明显的优势：数据包开销较小 

- pub/sub 最重要的方面是 publisher 与 subscriber 的解藕 

	- 空间解耦
	- 时间解藕
	- 同步 Synchronization 解藕 

	- 发布/订阅模式消除了传统客户-服务器之间的直接通信，把通信这个操作交给了 broker 进行代理，并在空间、时间、同步三个维度上进行了解藕。 

- pub/sub 比传统的客户端-服务器模式有了更好的拓展，这是由于 broker 的高度并行化，并且是基于事件驱动的模式。事件驱动中的程序流程会由诸如用户操作（点击鼠标、键盘）、传感器输出或者从其他程序或传递的消息事件决定。- 事件驱动编程是图形用户界面和其他应用程序比如 Web 中使用的主要范式，这些应用程序能够响应用户输入执行某些操作为中心，这同时也适用于驱动程序的编程 。

- broker 能够对消息进行过滤，使每个订阅者只接收自己感兴趣的消息：
	
	- 基于主题的过滤：每条消息都会有一个 topic ，接收客户端会向 borker 订阅感兴趣的 topic，订阅后，broker 就会确保客户端收到发布到 topic 中的消息
	
	- 基于内容的过滤 ：broker 会根据特定的内容过滤消息，接受客户端会经过过滤他们感兴趣的内容。这种方法的一个显著的缺点就是必须事先知道消息的内容，不能加密或者轻易修改 
	
	- 基于类型的过滤

- 为了发布/订阅系统的挑战，MQTT 具有三个服务质量级别，你可以指定消息从客户端传到 broker 或者从 broker 传到客户端，在 topic 的订阅中，会存在 topic 没有 subscriber 订阅的情况，作为 broker 必须知道如何处理这种情况。

- 订阅发布模式与消息队列模式区别
	- 在传统的消息队列模式中，一条消息会存储在消息队列中等待被消费，每个传入的消息都存储在消息队列中，直到它被客户端（通常称之为消费者）所接收，如果没有客户端消费消息的话，这条消息就会存在消息队列中等待被消费。但是在消息队列中，不会存在消息没有客户端消费的情况，但是在 MQTT 中，确存在 topic 无 subscriber 订阅的情况。
	
	- 在传统的消息队列模式中，一条消息只能被一个客户端所消费，负载会分布在队列的每个消费者之间；而在 MQTT 中，每个订阅者都会受到消息，每个订阅者有相同的负载。 

	- 在传统的消息队列模式中，必须使用单独的命令来显式创建队列，只有队列创建后，才可以生产或者消费消息；而在 MQTT 中，topic 比较灵活，可以即时创建。 

- MQTT基本概念
	- MQTT Client
		- publisher 和 subscriber 都属于 MQTT Client。 

		- 发布和订阅的功能也可以由同一个 MQTT Client 实现。 

		- MQTT 客户端是指运行 MQTT 库并通过网络连接到 MQTT broker 的任何设备，这些设备可以从微控制器到成熟的服务器.

	- MQTT Broker 
		- broker 负责接收所有消息，过滤消息，确定是哪个 client 订阅了每条消息，并将消息发送给对应的 client，broker 还负责保存会话数据，这些数据包括订阅的和错过的消息。broker 还负责客户端的身份验证和授权。 

		- MQTT 是基于 TCP/IP 协议基础之上的，所以 MQTT 的 client 和 broker 都需要 TCP/IP 协议的支持。

	- MQTT链接
		- MQTT 的连接总是在 client 和 broker 之间进行，client 和 client 之间并不会相互连接。如果要发起连接的话，那么 client 就会向 broker 发起 CONNECT 消息，代理会使用 CONNACK 消息和状态码进行响应 

	- 一旦 client 和 broker 的连接建立后，broker 就会使客户端的连接一直处于打开状态，直到 client 发出断开命令或者连接中断。 

- 消息
	- MQTT 的消息报文主要分为 CONNECT 和 CONNACK 消息。 

	- CONNECT

		- 为了初始化连接，需要 client 向 broker 发送 CONNECT 消息 

		- 一个 MQTT 客户端发送一条 CONNECT 连接，这条 CONNECT 连接可能会包含下面这些信息 

		![image](https://myonemanager.lzybetter.repl.co/picbed_big/picbed/a705b334a0d4cb20f22070038ec1cc98.png)

		- ClientId：显而易见，这个就是每个客户端的 ID 标识，也就是连接到 MQTT broker 的每个 client。 
	
		- 这个 ID 应该是每个 client 和 broker 唯一的，如果你不需要 broker 持有状态的话，你可以发送一个空的 ClientId，空的 ClientId 会没有任何状态。在这种情况下，ClientSession 需要设置为 true，否则将会拒绝连接。 
	
		- CleanSession：CleanSession 会话标志会告诉 broker client 是否需要建立持久会话。在持久会话 （CleanSession = false）中，broker 存储 client 的所有订阅以及服务质量(Qos) 是 1 或 2 订阅的 client 的所有丢失的消息。如果会话不是持久的（CleanSession = true），那么 broker 则不会为 client 存储任何内容并且会清除先前持久会话中的所有信息。 
	
		- Username/Password ：MQTT 会发送 username 和 password 进行 client 认证和授权
	
		- LastWillxxx ：LastWillxxx 表示的是遗愿，client 在连接 broker 的时候将会设立一个遗愿，这个遗愿会保存在 broker 中，当 client 因为非正常原因断开与 broker 的连接时，broker 会将遗愿发送给订阅了这个 topic（订阅遗愿的 topic）的 client。
		
		- keepAlive：keepAlive 是 client 在连接建立时与 broker 通信的时间间隔，通常以秒为单位。这个时间指的是 client 与 broker 在不发送消息下所能承受的最大时长。

	- CONNACK
		- 当 broker 收到 CONNECT 消息时，它有义务回复 CONNACK 消息进行响应

		- CONNACK 消息包括两部分内容 

		![](https://myonemanager.lzybetter.repl.co/picbed_big/picbed/43255ffc270491b4b99a411422899b01.png)

		- SessionPresent：会话当前标识，这个标志会告诉 client 当前 broker 是否有一个持久性会话与 client 进行交互。 SessionPresent 标志和 CleanSession 标志有关，当 client 在 CleanSession 设置为 true 的情况下连接时，SessionPresent 始终为 false，因为没有持久性会话可以使用。如果 CleanSession 设置为 false，则有两种可能性，如果 ClientId 的会话信息可用，并且 broker 已经存储了会话信息，那么 SessionPresent 为 true，否则如果没有 ClientId 的任何会话信息，那么 SessionPresent 为 false。 

		- ReturnCode：CONNACK 消息中的第二个标志是连接确认标志。这个标志包含一个返回码，告诉客户端连接尝试是否成功。连接确认标志有下面这些选项。 

- 消息类型 

	- 发布 

		- MQTT 使用的是基于 topic 主题的过滤。每条消息都应该包含一个 topic ，broker 可以使用 topic 将消息发送给感兴趣的 client。除此之外，每条消息还会包含一个负载(Payload)，Payload 中包含要以字节形式发送的数据。 

		- MQTT 是数据无关性的，也就是说数据是由发布者 - publisher 决定要发送的是 XML 、JSON 还是二进制数据、文本数据 

		- MQTT 中的 PUBLISH 消息结构如下

		![](https://myonemanager.lzybetter.repl.co/picbed_big/picbed/c0633b77a9c1cfb864bf472795e73131.png)

		- Packet Identifier：这个 PacketId 标识在 client 和 broker 之间唯一的消息标识。packetId 仅与大于零的 Qos 级别相关 
		
		- TopicName：主题名称是一个简单的字符串，/ 代表着分层结构 
		
		- Qos：这个数字表示的是服务质量水平，服务质量水平有三个等级：0、1 和 2，服务级别决定了消息到达 client 或者 broker 的保证类型，来决定消息是否丢失 
		
		- RetainFlag：这个标志表示 broker 将最近收到的一条 RETAIN 标志位为true的消息保存在服务器端（内存或者文件）。MQTT 服务器只会为每一个 Topic 保存最近收到的一条 RETAIN 标志位为true的消息 

		- Payload：这个是每条消息的实际内容 
		
		- Dupflag：这个标志表示该消息是重复的并且由于预期的 client 或者 broker 没有确认所以重新发送了一次。 

		- 当 client 向 broker 发送消息时，broker 会读取消息，根据 Qos 的级别进行消息确认，然后处理消息。处理消息其实就是确定哪些 subscriber 订阅了 topic 并将消息发送给他们。 

		- 发布消息的 client 不会知道是否有人对发布的消息感兴趣，同时也不知道多少 client 从 broker 收到了消息。 

	- 订阅 
	
		- client 会向 broker 发送 SUBSCRIBE 消息来接收有关感兴趣的 topic，这个SUBSCRIBE 消息非常简单，它包含了一个唯一的数据包标识和一个订阅列表 

		![](https://myonemanager.lzybetter.repl.co/picbed_big/picbed/400a2de3b1cca165e543c3de7214eab6.png)

		- Packet Identifier：这个 PacketId 和上面的 PacketId 一样，都表示消息的唯一标识符。 
		- ListOfSubscriptions：SUBSCRIBE 消息可以包含一个 client 的多个订阅，每个订阅都会由一个 topic 和一个 Qos 构成。订阅消息中的 topic 可以包含通配符。

	- 确认消息 
		- client 在向 broker 发送 SUBSCRIBE 消息后，为了确认每个订阅，broker 会向 client 发送 SUBACK 确认消息。这个 SUBACK 包含原始 SUBSCRIBE 消息的 packetId 和返回码列表。 

	![](https://myonemanager.lzybetter.repl.co/picbed_big/picbed/2a0be7b3a5825b41d6282c893853f137.png)

	- 发布 - 订阅 - 确认消息，这三种消息的示意图如下。 

		![](https://myonemanager.lzybetter.repl.co/picbed_big/picbed/1617d6fdc0aba70e124f06bded808008.png)

	- 退订 
	
		- SUBSCRIBE 消息对应的是 UNSUBSCRIBE 消息，这条消息发送后，broker 会删除关于 client 的订阅。 
		![](https://myonemanager.lzybetter.repl.co/picbed_big/picbed/e8ce80a3cb32f0f98372e2ade155df75.png)

	- 确认退订 
	
		- 取消订阅也需要 broker 的确认，此时 broker 会向 client 发送一个 UNSUBACK 消息。

		![](https://myonemanager.lzybetter.repl.co/picbed_big/picbed/0b069167a84eaf73290022c071ef8456.png)

	- 退订和确认退订的流程如下 

		![](https://myonemanager.lzybetter.repl.co/picbed_big/picbed/e3d655c72474894f37c5e02cb2719edf.png)

- Topic
	
	- MQTT Topic 非常轻量级，client 在发布或订阅之前不需要先创建所需要的 Topic，broker 在接收每个 Topic 前不用进行初始化操作。 
	
	- 当客户端订阅 Topic 时，它可以订阅已发布消息的确切 Topic，也可以使用通配符来同时订阅多个 Topic 
	
	- 单级通配符可以替换 Topic 的一个级别，+ 号代表 Topic 中的单级通配符 
	
	- 多级通配符涵盖多个 Topic，# 代表 Topic 中的多级通配符。为了让 broker 能够确定和哪些 Topic 匹配，多级通配符必须作为 Topic 中的最后一个字符放置，并以 / 开头 
	
	- 当 client 订阅带有多级通配符的 Topic 时，不论 Topic 有多长多深，它都会收到通配符之前 Topic 的所有消息。如果你只将 Topic 定义为 # 的话，那么你将会收到所有的消息。 



