# MQTT协议

### MQTT轻量发布订阅消息协议

#### 概览

&#x20;MQTT是一个轻量的发布订阅模式消息传输协议，专门针对低带宽和不稳定网络环境的物联网应用设计。

&#x20;MQTT官网: [http://mqtt.org](http://mqtt.org/)

&#x20;MQTT V3.1.1协议规范: <http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html>

MQTT中文文档：<https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/01-Introduction.html>

#### 特点

1. 开放消息协议，简单易实现
2. 发布订阅模式，一对多消息发布
3. 基于TCP/IP网络连接
4. 1字节固定报头，2字节心跳报文，报文结构紧凑
5. 消息QoS支持，可靠传输保证

#### 应用

&#x20;MQTT协议广泛应用于物联网、移动互联网、智能硬件、车联网、电力能源等领域。

1. 物联网M2M通信，物联网大数据采集
2. Android消息推送，WEB消息推送
3. 移动即时消息，例如Facebook Messenger
4. 智能硬件、智能家具、智能电器
5. 车联网通信，电动车站桩采集
6. 智慧城市、远程医疗、远程教育
7. 电力、石油与能源等行业市场

### MQTT基于主题(Topic)消息路由

&#x20;MQTT协议基于主题(Topic)进行消息路由，主题(Topic)类似URL路径，例如:

```
chat/room/1

sensor/10/temperature

sensor/+/temperature

$SYS/broker/metrics/packets/received

$SYS/broker/metrics/#
```

&#x20;主题(Topic)通过’/’分割层级，支持’+’, ‘#’通配符:

```
'+': 表示通配一个层级，例如a/+，匹配a/x, a/y

'#': 表示通配多个层级，例如a/#，匹配a/x, a/b/c/d
```

&#x20;订阅者与发布者之间通过主题路由消息进行通信，例如采用mosquitto命令行发布订阅消息:

```
mosquitto_sub -t a/b/+ -q 1

mosquitto_pub -t a/b/c -m hello -q 1
```

{% hint style="info" %}
&#x20;订阅者可以订阅含通配符主题，但发布者不允许向含通配符主题发布消息。
{% endhint %}

### MQTT V3.1.1协议报文

#### 报文结构

| 固定报头(Fixed header)    |
| --------------------- |
| 可变报头(Variable header) |
| 报文有效载荷(Payload)       |

#### 固定报头

| Bit      | 7                | 6 | 5 | 4 | 3     | 2 | 1 | 0 |
| -------- | ---------------- | - | - | - | ----- | - | - | - |
| byte1    | MQTT Packet type |   |   |   | Flags |   |   |   |
| byte2... | Remaining Length |   |   |   |       |   |   |   |

#### 报文类型

| 类型名称        | 类型值 | 报文说明     |
| ----------- | --- | -------- |
| CONNECT     | 1   | 发起连接     |
| CONNACK     | 2   | 连接回执     |
| PUBLISH     | 3   | 发布消息     |
| PUBACK      | 4   | 发布回执     |
| PUBREC      | 5   | QoS2消息回执 |
| PUBREL      | 6   | QoS2消息释放 |
| PUBCOMP     | 7   | QoS2消息完成 |
| SUBSCRIBE   | 8   | 订阅主题     |
| SUBACK      | 9   | 订阅回执     |
| UNSUBSCRIBE | 10  | 取消订阅     |
| UNSUBACK    | 11  | 取消订阅回执   |
| PINGREQ     | 12  | PING请求   |
| PINGRESP    | 13  | PING响应   |
| DISCONNECT  | 14  | 断开连接     |

#### PUBLISH发布消息

PUBLISH报文承载客户端与服务器间双向的发布消息。 PUBACK报文用于接收端确认QoS1报文，PUBREC/PUBREL/PUBCOMP报文用于QoS2消息流程。

#### PINGREQ/PINGRESP心跳

客户端在无报文发送时，按保活周期(KeepAlive)定时向服务端发送PINGREQ心跳报文，服务端响应PINGRESP报文。PINGREQ/PINGRESP报文均2个字节。

### MQTT消息QoS

MQTT发布消息QoS保证不是端到端的，是客户端与服务器之间的。订阅者收到MQTT消息的QoS级别，最终取决于发布消息的QoS和主题订阅的QoS。

| 发布消息的QoS | 主题订阅的QoS | 接收消息的QoS |
| -------- | -------- | -------- |
| 0        | 0        | 0        |
| 0        | 1        | 0        |
| 0        | 2        | 0        |
| 1        | 0        | 0        |
| 1        | 1        | 1        |
| 1        | 2        | 1        |
| 2        | 0        | 0        |
| 2        | 1        | 1        |
| 2        | 2        | 2        |

#### Qos0消息发布订阅

![](https://33059891-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LPz4APWKTuf0FRQG1lh%2F-LQYCF52n4mrsVRArm99%2F-LQYRL058-kvrAfJBK4U%2Fqos0_seq.png?alt=media\&token=6204a468-5f85-4277-bc50-9dd6a1bf9473)

#### Qos1消息发布订阅

![](https://33059891-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LPz4APWKTuf0FRQG1lh%2F-LQYCF52n4mrsVRArm99%2F-LQYRQ49NYGzFC92rJSK%2Fqos1_seq.png?alt=media\&token=52f9e1ca-c4f8-46b9-96cb-393cc4d276a0)

#### Qos2消息发布订阅

![](https://33059891-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LPz4APWKTuf0FRQG1lh%2F-LQYCF52n4mrsVRArm99%2F-LQYRV9NdGCm-XTpCsPM%2Fqos2_seq.png?alt=media\&token=fa5fd55d-3b6b-43a6-9815-477a7738fdf3)

### MQTT会话(Clean Session)

MQTT客户端向服务器发起CONNECT请求时，可以通过’Clean Session’标志设置会话。

‘Clean Session’设置为0，表示创建一个持久会话，在客户端断开连接时，会话仍然保持并保存离线消息，直到会话超时注销。

‘Clean Session’设置为1，表示创建一个新的临时会话，在客户端断开时，会话自动销毁。

### MQTT连接保活心跳

MQTT客户端向服务器发起CONNECT请求时，通过KeepAlive参数设置保活周期。

客户端在无报文发送时，按KeepAlive周期定时发送2字节的PINGREQ心跳报文，服务端收到PINGREQ报文后，回复2字节的PINGRESP报文。

服务端在1.5个心跳周期内，既没有收到客户端发布订阅报文，也没有收到PINGREQ心跳报文时，主动心跳超时断开客户端TCP连接。

### MQTT遗愿消息(Last Will)

MQTT客户端向服务器端CONNECT请求时，可以设置是否发送遗愿消息(Will Message)标志，和遗愿消息主题(Topic)与内容(Payload)。

MQTT客户端异常下线时(客户端断开前未向服务器发送DISCONNECT消息)，MQTT消息服务器会发布遗愿消息。

### MQTT保留消息(Retained Message)

MQTT客户端向服务器发布(PUBLISH)消息时，可以设置保留消息(Retained Message)标志。保留消息(Retained Message)会驻留在消息服务器，后来的订阅者订阅主题时仍可以接收该消息。

例如mosquitto命令行发布一条保留消息到主题’a/b/c’:

```
mosquitto_pub -r -q 1 -t a/b/c -m 'hello'
```

之后连接上来的MQTT客户端订阅主题’a/b/c’时候，仍可收到该消息:

```
$ mosquitto_sub -t a/b/c -q 1
hello
```

保留消息(Retained Message)有两种清除方式:

1. 客户端向有保留消息的主题发布一个空消息:

   ```
   mosquitto_pub -r -q 1 -t a/b/c -m ''
   ```
2. 消息服务器设置保留消息的超期时间。

### MQTT WebSocket连接

MQTT协议除支持TCP传输层外，还支持WebSocket作为传输层。通过WebSocket浏览器可以直连MQTT消息服务器，发布订阅模式与其他MQTT客户端通信。

MQTT协议的WebSocket连接，必须采用binary模式，并携带子协议Header:

```
Sec-WebSocket-Protocol: mqttv3.1 或 mqttv3.1.1
```

### MQTT协议客户端库

#### Eclipse Paho客户端库

Paho官网: <http://www.eclipse.org/paho/>

#### mqtt.org官网客户端库

mqtt.org: <https://github.com/mqtt/mqtt.github.io/wiki/libraries>

### MQTT与XMPP协议对比

MQTT协议设计简单轻量、路由灵活，将在移动互联网物联网消息领域，全面取代PC时代的XMPP协议:

1. MQTT协议一个字节固定报头，两个字节心跳报文，报文体积小编解码容易。XMPP协议基于繁重的XML，报文体积大且交互繁琐。
2. MQTT协议基于主题(Topic)发布订阅模式消息路由，相比XMPP基于JID的点对点消息路由更为灵活。
3. MQTT协议未定义报文内容格式，可以承载JSON、二进制等不同类型报文。XMPP协议采用XML承载报文，二进制必须Base64编码等处理。
4. MQTT协议支持消息收发确认和QoS保证，XMPP主协议并未定义类似机制。MQTT协议有更好的消息可靠性保证。
