MQTT

**消息队列遥测传输**（，**MQTT**）是ISO 标准（ISO/IEC PRF 20922）下基于发布（Publish）/订阅（Subscribe）范式的消息协议，可視為「資料傳遞的橋樑」。它工作在TCP/IP协议族上，是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议。为此，它需要一个消息中间件（如HTTP），以解決當前繁重的資料傳輸協議。 

## 历史

IBM公司的及Arcom公司的阿兰·尼普于1999年撰写了该协议的第一个版本。

该协议的可用性取决于该协议的使用环境。IBM公司在2013年就向结构化资讯标准促进组织提交了 MQTT 3.1 版规范，并附有相关章程，以确保只能对规范进行少量更改。。MQTT-SN是针对非 TCP/IP 网络上的嵌入式设备主要协议的变种，与此类似的还有 ZigBee 协议。

纵观行业的发展历程，“MQTT”中的“MQ” 是来自于IBM的MQ系列消息队列产品线。然而通常队列本身不需要作为标准功能来支持。

可选协议包含了高级消息队列协议，面向文本的消息传递协议，互联网工程任务组约束应用协议， 可扩展消息与存在协议，数据分发服务，OPC UA以及 web 应用程序消息传递协议。

## 概览
MQTT 协议定义了两种网络实体：消息代理（message broker）与客户端（client）。其中，消息代理用于接收来自客户端的消息并转发至目标客户端。MQTT 客户端可以是任何运行有 MQTT 库并通过网络连接至消息代理的设备，例如微型控制器或大型服务器。

信息的传输是通过**主题**（**topic**）管理的。发布者有需要分发的数据时，其向连接的消息代理-{zh-hans:发送;zh-tw:發送}-携带有数据的控制消息。代理会向订阅此主题的客户端分发此数据。发布者不需要知道订阅者的数据和具体位置；同样，订阅者不需要配置发布者的相关信息。

如果消息代理接受到某个主题上的消息，且这个主题没有任何订阅，那么代理就会丢弃之，除非发布者将其标记为**保留消息**（**retained message**）。

当发布客户端首次与代理连接时，客户端可以设置一个默认消息。当代理发现发布者意外断开，其会向订阅者发送此预设的消息。

客户端仅与代理有直接的数据传输，但整个系统中可能有多个代理，其于当前订阅者的主题交换数据。

MQTT 控制消息最小只有 2 字节的数据。最多可以承载 256 Mb 的数据。有 14 种预定义的消息类型用于：连接客户端与代理、断开连接、发布数据、确认数据接收、监督客户端与代理的连接。

MQTT 基于 TCP 协议，用于数据传输。变体 MQTT-SN 用于在蓝牙上传输，基于 UDP。

MQTT 协议使用普通文本发送连接认证书，且并不包含任何安全或认证相关的措施。但可以使用传输层安全来加密并保护-{zh-hans:发送;zh-tw:發送}-的数据，以防止拦截、修改或伪造。

MQTT 默认端口为 1883，加密的端口为 8883。

## 连接
等待与服务器建立连接然后创建节点之间的连接。

## 保活(keep alive)
最大保活间隔：18小时12分钟15秒。客户在保活间隔乘以1.5倍的时间内可以不与broker通信。如果客户没有消息发给broker，则应该发布PINGREQ包；broker回复PINGRESP包。

broker具有Client Take-Over功能，以便在客户重连broker，但broker认为与客户的TCP连接还存在时（称为Half-Open），能删除原有连接，接收重连请求。

## 断开连接
等待MQTT客户端完成所必须完成的工作，然后等待TCP/IP会话关闭连接。

## WebSockets
WebSockets是在一个TCP连接上全双工通信。可以使得普通的网页浏览器成为基于WebSocket的MQTT client。

## 持久会话（Persistent Session）
连接broker的会话分为两种：
*持久的会话：当会话与broker断连时，broker会保存会话已经订阅的QoS为1或2的主题的消息，以便重新连接后把这些消息发给会话。
*非持久的non-persistent或称clean session。适合于只发布消息，不订阅消息的会话。broker不存储这种会话的任何未分发的订阅主题的消息。

建立与broker的connection时，参数clean session设置为真则创建非持久会话，否则创建持久会话。

## Topic
Topic是个utf-8编码的字符串，“topic level”由`/`(U+002F)分割开。topic name中相邻的2个分隔符表示一个0长的topic level。

topic name和topic filter是：
*大小写敏感的。
*可以包含空格符（但不建议这么做）
*前导或尾部的‘/’将产生不同的Topic Name或Topic Filter。例如“/finance”不同于“finance”
*Topic Name或Topic Filter只包含‘/’是有效的
*Topic Name或Topic Filter禁止包含null字符(U+0000)
*Topic Name或Topic Filter是utf-8编码的字符串，不能超过65,535个字节。
*Topic Name或Topic Filter的level数量没有限制。

订阅者的topic filter可包含通配符，使其同时能订阅多个topic：
* +(U+002B)：单级通配符，如：`check/+/baseline`。必须占据整个topic level。可是第一层或者最后一层。“sport/+”不匹配“sport”但是匹配“sport/”。“/finance”匹配“+/+”和“/+”，但不匹配“+”。“sport+”是无效的。
* #(U+0023)：多级通配符，必须放在topic的最后。或者单独存在(即匹配所有topic)，或者其前一个字符必须是'/'。可匹配上一层topic level及其所有子孙topic
* $：以$开始的topic name用于broker内部的统计信息。用户的topic name不应该以$开始。broker不会把以$开始的topic name与#或+开始的topic filter相匹配。
topic name中不能包含上述通配符。 

## 发布
将请求传递给MQTT客户端后立即返回到应用程序线程。

## 服务品质（QoS）

服务品質指的是交通优先级和资源预留控制机制，而不是接收的服务品質。 服务品質是为不同应用程序，用户或数据流提供的不同优先级的能力，或者也可以说是为数据流保证一定的性能水平的能力。

以下是每一个服务品质级别的具体描述：
* QoS 0：最多一次传送，即“fire and forget”（只负责传送，发送过后就不管数据的传送情况）。
* QoS 1：至少一次传送（握手2次）；PUBLISH packet与PUBACK packet（确认数据交付）。
* QoS 2：正好一次传送（握手4次）；PUBLISH 、PUBREC-{zh-hans:包; zh-hant:封包}-用于确认收到。如果发送方没有收到PUBREC-{zh-hans:包; zh-hant:封包}-，就用DUP标志重发消息；如果收到PUBREC-{zh-hans:包; zh-hant:封包}-，就删除最初的PUBLISH-{zh-hans:包; zh-hant:封包}-，存储并回复PUBREL-{zh-hans:包; zh-hant:封包}-。接收方收到PUBREL-{zh-hans:包; zh-hant:封包}-，就回复PUBCOMP-{zh-hans:包; zh-hant:封包}-并删除所有相关状态（保证数据交付成功）。
## 保留的消息（Retained Message）
broker会在该主题（topic）下保留最新一条带有Retained标志的消息。新订阅者或者重连的订阅者总是会收到broker保存的最新的Retained Message。

这可用于发布者让订阅者总是能获取到最新的状态信息。

如果发布一条zero-byte payload的Retained Message，则broker就会删除保存的Retained Message。

## Last Will and Testament (LWT)
当一个会话（session）不文雅的（ungraceful）、出乎意料的（unexpected）的断连时，Last Will and Testament (LWT)可用于通知其它连接在这个broker上的客户端。

客户可以在一个topic下向broker发出CONNECT message时可以发出一条LWT消息。broker保存这条LWT消息，直到客户发出一条DISCONNECT消息文雅地断连为止。broker检测到下设情况之一，就会给所有订阅了LWT主题的客户发布LWT：
*检测到网络失败或IO错误
*由于协议错误，broker需要关闭网络连接
*客户关闭了网络连接且没有发DISCONNECT消息
*在Keep Alive周期后客户没有与broker通信

## MQTT实现
已经有几个工程项目实现了 MQTT协议。例如:
* Facebook Messenger。 脸书已经在 Facebook Messenger 上用了 MQTT 的多个特性用于网络聊天。但是，目前仍不清楚 Facebook 在哪些地方使用了多少 MQTT。
* *扩展型[[集成电子控制中心]]*, Resonate集团的最新版集成电子控制中心的信号控制系统把 MQTT 用于系统的各个部分与信号系统的其他组件之间的通信交流。 它为符合欧洲电工标准委员会重要安全通信标准的系统提供了底层通信框架。
* EVERYTHING 公司的IoT平台使用 MQTT 作为机器对机器的协议来为数百万个产品提供服务。
* 在 2015 年，亚马逊网络服务平台宣布 *Amazon Iot* 是基于 MQTT 的。
* 开放地理空间协会的传感器 API 标准规范有一个标准 MQTT 扩展作为额外的消息协议绑定当前 API。 它在美国国土安全部 IoT 试点项目中得到了证明。
* OpenStack 上游基础设施服务通过 MQTT  统一消息总线和作为 MQTT 中间件的 *Mosquitto*。
* Adafruit 公司在 2015 年为物联网实验和学习者启动了一个名为 *Adafruit IO* 的免费的 MQTT 云计算服务。
* Microsoft Azure Iot Hub  使用 MQTT 作为遥测消息的主要协议，尤其是使用NVIDIA GeForce GTX 690进行封包加速时，效率可提升100%到120%。
* XIM 公司在 2017 年开发了一个名为*MQTT Buddy* MQTT 客户端。iOS 和 Android 上都有该应用。 但是它并没有被放到 F-Droid 仓库（也就意味着它是闭源软件），该应用提供了英语，俄语，汉语三种语言界面。
* Node-RED 支持 0.14 版本以上的 MQTT 节点，以便正确配置 TLS 连接。
* 开源智慧家庭平台 *Home Assistant* 支持 MQTT，并为 MQTT 中间件提供了四个选项。
* 树莓派上基于Node.js 的 *Pimatic* 家庭自动化框架提供了 MQTT 插件来完全支持 MQTT 协议。
* McAfee OpenDXL 是基于对消息中间件本身增强的 MQTT，以便他们能够清楚地理解 DXL 消息格式，以支持如服务，请求/响应（点对点）消息传递，服务故障转移和服务区等高级功能。

## MQTT实现对比

更完整的 MQTT 库可以在 GitHub 上找到。

## 参见
*Apache ActiveMQ
*RabbitMQ
*XMPP
*高级消息队列协议（AMQP）
* (STOMP)

## 引用

## 外部链接
* 官方网站 
* Official website of Technical Committee 
* MQTT Specifications
** Version 5.0, 2019-03-07: PDF edition , HTML edition 
** Version 3.1.1 Plus Errata 01, 2015-12-10: PDF edition , HTML edition 
** Version 3.1.1, 2014-10-29: PDF edition , HTML edition 
** Version 3.1, 2010: PDF edition , HTML edition 
* MQTT-SN Specifications
** Version 1.2, 2013-11-14: PDF edition