参考 Reference

v3.1.1

  • 英文原版
  • 中文翻译版

其他资源

网站

  • MQTT官方主页
  • Eclipse Paho 项目主页

测试工具

  • MQTT Spy(基于JDK)
  • Chrome插件 MQTTLens(需FQ)

基本概念 Basic Conception

Session 会话

定义

  • 定义:某个客户端(由ClientID作为标识)和某个服务器之间的逻辑层面的通信
  • 生命周期(存在时间):会话 >= 网络连接

ClientID

  • 客户端唯一标识,服务端用于关联一个Session
  • 只能包含这些 大写字母,小写字母 和 数字(0-9a-zA-Z),23个字符以内
  • 如果 ClientID 在多次 TCP连接中保持一致,客户端和服务器端会保留会话信息(Session)
  • 同一时间内 Server 和同一个 ClientID 只能保持一个 TCP 连接,再次连接会踢掉前一个

CleanSession 标记

  • 在Connect时,由客户端设置
  • 0 —— 开启会话重用机制。网络断开重连后,恢复之前的Session信息。需要客户端和服务器有相关Session持久化机制。
  • 1 —— 关闭会话重用机制。每次Connect都是一个新Session,会话仅持续和网络连接同样长的时间。

客户端 Session

  • 已经发送给服务端,但是还没有完成确认的 QoS 1 和 QoS 2 级别的消息
  • 已从服务端接收,但是还没有完成确认的 QoS 2 级别的消息

服务器端 Session

  • 会话是否存在,即使会话状态的其它部分都是空  (SessionFlag)
  • 客户端的订阅信息  (ClientSubcription)
  • 已经发送给客户端,但是还没有完成确认的 QoS 1 和 QoS 2 级别的消息
  • 即将传输给客户端的 QoS 1 和 QoS 2 级别的消息
  • 已从客户端接收,但是还没有完成确认的 QoS 2 级别的消息
  • (可选)准备发送给客户端的 QoS 0 级别的消息

长连接维护与管理

Keep Alive 心跳

  • 目的是保持长连接的可靠性,以及双方对彼此是否在线的确认。
  • 客户端在Connect的时候设置 Keep Alive 时长。如果服务端在 1.5 * KeepAlive 时间内没有收到客户端的报文,它必须断开客户端的网络连接
  • Keep Alive 的值由具体应用指定,一般是几分钟。允许的最大值是 18 小时 12 分 15 秒

Will 遗嘱

  • 遗嘱消息(Will Message)存储在服务端,当网络连接关闭时,服务端必须发布这个遗嘱消息,所以被形象地称之为遗嘱,可用于通知异常断线。
  • 客户端发送 DISCONNECT 关闭链接,遗嘱失效并删除
  • 遗嘱消息发布的条件,包括:
  • 服务端检测到了一个 I/O 错误或者网络故障
  • 客户端在保持连接(Keep Alive)的时间内未能通讯
  • 客户端没有先发送 DISCONNECT 报文直接关闭了网络连接
  • 由于协议错误服务端关闭了网络连接
  • 相关设置项,需要在Connect时,由客户端指定
  • Will Flag —— 遗嘱的总开关
  • 0 -- 关闭遗嘱功能,Will QoS 和 Will Retain 必须为 0
  • 1 --  开启遗嘱功能,需要设置 Will Retain 和 Will QoS
  • Will QoS —— 遗嘱消息 QoS
  • 可取值 0、1、2,含义与消息QoS相同
  • Will Retain —— 遗嘱是否保留
  • 0 -- 遗嘱消息不保留,后面再订阅不会收到消息
  • 1 -- 遗嘱消息保留,持久存储
  • Will Topic —— 遗嘱话题
  • Will Payload —— 遗嘱消息内容

消息基本概念

报文标识 Packet Identifier   

  • 存在报文的可变报头部分,非零两个字节整数 (0-65535]
  • 一个流程中重复:这些报文包含 PacketID,而且在一次通信流程内保持一致:
  • PUBLISH(QoS>0 时),PUBACK,PUBREC,PUBREL,PUBCOMP
  • SUBSCRIBE,  SUBACK
  • UNSUBSCIBE,UNSUBACK
  • 新的不重复:客户端每次发送一个新的这些类型的报文时都必须分配一个当前 未使用的PacketID
  • 当客户端处理完这个报文对应的确认后,这个报文标识符就释放可重用。
  • 独立维护:客户端和服务端彼此独立地分配报文标识符。因此,客户端服务端组合使用相同的报文标识符可以实现 并发 的消息交换。可能出现一下情况,并不算异常:

Payload 有效载荷,消息体

  • 最大允许 256MB
  • Publish 的 Payload 允许为空。在很多场合下,代表将持久消息(或者遗嘱消息)清空。
  • UTF-8编码

Retain 持久消息(粘性消息)

  • RETAIN 标记:每个Publish消息都需要指定的标记
  • 0 —— 服务端不能存储这个消息,也不能移除或替换任何 现存的保留消息
  • 1 —— 服务端必须存储这个应用消息和它的QoS等级,以便它可以被分发给未来的订阅者
  • 每个Topic只会保留最多一个 Retain 持久消息
  • 客户端订阅带有持久消息的Topic,会立即受到这条消息
  • 服务器可以选择丢弃持久消息,比如内存或者存储吃紧的时候
  • 如果客户端想要删除某个Topic 上面的持久消息,可以向这个Topic发送一个Payload为空的持久消息
  • 遗嘱消息(Will)的Retain持久机制同理

QoS 服务等级(消息可靠性)

最多一次 At most Once(QoS == 0)

  • 没有回复,不需要存储。有可能丢失(网络异常断开,业务层繁忙或者错误)

至少一次 At least Once(QoS == 1 )

  • 发送者S 发送前需要做持久化存储,接受者R 不需要持久化存储
  • 如果 发送者S 没有收到 接收者R 的回复 PUBACK,过一段时间 发送者S 会重新发送,DUP标记为1(在同一Session内)。
  • 接受者R 发送 PUBACK 后,不需要知道对方是否收到,马上把消息交给上层业务。如果此时网络异常,会导致发送者重发。这样接受者收到多个消息(所以叫至少一次)。

有且仅有一次 Exactly Once(QoS == 2 )

  • 发送者S 发送 PUBLISH 前,需要做持久化存储。接受者R 回复PUBREC 后,也需要做持久化存储
  • 如果 发送者S 没有收到 接收者R 的回复 PUBREC,过一段时间 发送者S 会重新发送,DUP标记为1(在同一Session内)。
  • 如果 接受者R 没有收到 发送者S 的回复 PUBREL,过一段时间 接受者R 会重新发送PUBREC。
  • 发送者S 收到 PUBREC后,删除持久化消息,但是要保存 PacketID
  • 接收者R 受到 PUBREL后,删除持久化PUBREC。然后将消息发给上层,同时回复 PUBCOMP。
  • 发送者S 收到 PUBCOMP 后,删除 PacketID,通信完美结束。
  • 这套流程可以 严格保证 一个包不管在什么情况下 接收者R 只收到一次 。

重传标记 DUP 与重传机制 (QoS > 0)

  • 如果客户端或者服务器发送了一个 Publish 消息,一段时间内没收到 PublishAck 回复,则认为消息丢失,进行重传。
  • 在一个Session内,进行重传的时候,头部的 DUP 重传标志 设置为1。
  • 客户端有可能收到 DUP == 0 的重传包(Payload相同,PacketID不同)。因为可能因为网络问题,下次重传时间较久,Session已经释放,PacketID 已经变更。
  • 客户端发给服务器的和服务器转发给别的客户端的 Publish 消息,DUP 重传标志不会传递
  • 接收者收到一个 DUP 标志为 1 的控制报文时,并不能保证之前收到过相同的报文

消息重传顺序 

  • 重发任何之前的 PUBLISH 报文时,必须按原始 PUBLISH 报文的发送顺序重发 (适用于QoS 1 和 QoS 2 消息)
  • 必须按照对应的 PUBLISH 报文的顺序发送 PUBACK 报文 (QoS 1 消息)
  • 必须按照对应的 PUBLISH 报文的顺序发送 PUBREC 报文 (QoS 2 消息)
  • 必须按照对应的 PUBREC 报文的顺序发送 PUBREL 报文 (QoS 2 消息)
  • QoS == 1 时,虽然是PUBLISH有序的,但是可能会重复。例如,发布者按顺序 1,2,3,4 发送消息,订阅者收到的顺序可能是 1,2,3,2,3,4。
  • QoS == 1 时,如果限制 传输窗口 (in-flight window) ==1,即同一时刻只有一个包在传输,就可以保证乱序。例如,订阅者收到的顺序可能是 1,2,3,3,4,而不是 1,2,3,2,3,4
  • QoS == 2 时,肯定不会存在乱序的问题。

话题 与订阅机制  Topic & Subcribe

Topic 话题 和 TopicFilter 话题过滤器

  • Pub-Sub消息模型的核心机制
  • UTF-8 编码字符串,不能超过 65535 字节。层级数量没有限制
  • 不能包含任何的下文中提到的特殊符号(/、+、#),必须至少包含一个字符
  • 区分大小写,可以包含空格,不能包含空字符 (Unicode U+0000)
  • 在收部或尾部增加 斜杠 “/”,会产生不同的Topic和TopicFilter。举例:
  • “/A” 和 “A” 是不同的
  • “A” 和 “A/” 是不同的
  • 只包含斜杠 “/” 的 Topic 或 TopicFilter 是合法的

TopicFilter中的特殊符号

  • 层级分隔符 /
  • 用于分割主题的每个层级,为主题名提供一个分层结构
  • 主题层级分隔符可以出现在 Topic 或 TopicFilter 的任何位置
  • 特例:相邻的主题层次分隔符表示一个零长度的主题层级
  • 单层通配符 +
  • 只能用于单个主题层级匹配的通配符。例如,“a/b/+” 匹配 “a/b/c1” 和 “a/b/c2” ,但是不匹配 “a/b/c/d”
  • 可以匹配 任意层级,包括第一个和最后一个层级。例如,“+” 是有效的,“sport/+/player1” 也是有效的。
  • 可以在多个层级中使用它,也可以和多层通配符一起使用。 例如,“+/tennis/#” 是有效的。
  • 只能匹配本级不能匹配上级。例如,“sport/+” 不匹配 “sport” 但是却匹配“sport/”,“/finance” 匹配 “+/+” 和 “/+” ,但是不匹配 “+”。
  • 多层通配符 #
  • 用于匹配主题中任意层级的通配符
  • 匹配包含本身的层级和子层级。例如 “a/b/c/#" 可以匹配 “a/b/c”、“a/b/c/d” 和  “a/b/c/d/e”
  • 必须是最后的结尾。例如“sport/tennis/#/ranking”是无效的
  • “#”是有效的,会收到所有的应用消息。 (服务器端应将此类 TopicFilter禁掉 )

以$开头的,服务器保留

  • 服务端不能将 $ 字符开头的 Topic 匹配通配符 (#或+) 开头的 TopicFilter
  • 服务端应该阻止客户端使用这种 Topic 与其它客户端交换消息。服务端实现可以将 $ 开头的主题名用作其他目的。
  • $SYS/ 被广泛用作包含服务器特定信息或控制接口的主题的前缀
  • 客户端不特意订阅 $开头的 Topic,就不会收到对应的消息
  • 订阅 “#” 的客户端不会收到任何发布到以 “$” 开头主题的消息
  • 订阅 “+/A/B” 的客户端不会收到任何发布到 “$SYS/A/B” 的消息
  • 订阅 “$SYS/#” 的客户端会收到发布到以 “$SYS/” 开头主题的消息
  • 订阅 “$SYS/A/+” 的客户端会收到发布到 “$SYS/A/B” 主题的消息
  • 如果客户端想同时接受以 “$SYS/” 开头主题的消息和不以 $ 开头主题的消息,它需要同时 订阅 “#” 和 “$SYS/#”

订阅 Subscribe  与 QoS降级

  • 订阅机制基于TopicFilter匹配
  • 一个Subsribe请求 可订阅多个 Topic(节省带宽,多订阅尽量用一次请求)。取消订阅也同理
  • 每一个订阅需要指定一个QoS,指定了客户端接收消息所允许的最大QoS级别。但是服务器端最终授权返回的QoS可能会小于等于客户端请求的QoS
  • 对于高于QoS的消息(比如说订阅的QoS限制到1,消息的QoS指定到2),那么客户端会收到一个QoS降低为指定的 限制QoS 的消息(消息的QoS降为1,不保证只收到一次)
  • 订阅关系可以被覆盖,以TopicFilter为标识。如果后面订阅一个相同的TopicFilter,但是指定的QoS不同,则以后面的为准,QoS升高后,重发相应等级的 Retain 消息

安全传输与鉴权认证  Security & Certification

传输层

  • 可以采用 TCP、SSL/TLS [RFC5246] 、WebSocket 作为传输层。UDP不可以,因为不保证可靠传输与有序传输。
  • 服务器端返回的数据极有可能出现 粘包 的情况。客户端经常会在连接建立之后,连续调用多个订阅,这样服务器端就会回复多个订阅ACK包,同时还有各个Topic上的持久消息,一般粘成一个TCP包返回过来
  • 端口(IANA分发)
  • 1883:over TCP,无加密
  • 8883:over SSL/TLS,单向认证(强烈建议)
  • 8884:over SSL/TLS,双向认证
  • 8080:over WebSockets,未加密
  • 8081:over WebSockets,加密
  • 可使用SOCKS代理,可利用安全隧道(如SSH)

潜在的风险与应对机制

  • 潜在风险
  • 设备可能会被盗用
  • 客户端和服务端的静态数据可以被访问(比如客户端Root导致数据泄露、服务器被拖库)
  • 协议规定的行为可能有副作用 (如计时器攻击  “timing attacks”)
  • 拒绝服务攻击(DoS)
  • 通信可能会被拦截、修改、重定向或者泄露(抓包、中间人)
  • 虚假控制报文注入
  • 应对的机制
  • 用户和设备身份认证
  • 服务端资源访问授权
  • 控制报文和 Payload 的完整性校验
  • 控制报文和 Payload 的隐私控制

客户端身份验证与授权  (Authentication & Authorization of Client)

  • 用户名+密码验证:Connect 登录的时候,传入 UserName 和 Password
  • 相关标记位:在Connect时,由客户端设置
  • 用户名(UserName Flag)标记设置为1,才可以穿入
  • 密码(Password Flag)标记设置为1
  • 外部验证:LDAP、OAuth 或者 操作系统的认证机制
  • 用户名密码加密:防止中间人攻击和重放攻击
  • 应用层:客户端通过应用消息给服务端发送凭证用于身份验证。
  • 授权:基于客户端提供的信息如用户名、客户端标识符(ClientId)、客户端的主机名或 IP 地址,或者身份认证的结果,服务端可以限制对某些服务端资源的访问

服务端身份验证 (Authentication of Server by Client)

  • MQTT 协议不是双向信任的,它没有提供客户端验证服务端身份的机制
  • TLS:客户端可以使用服务端发送的SSL证书验证服务端的身份
  • 应用层:可以通过服务端给客户端发送凭证用于身份验证的应用层消息
  • VPN:在客户端和服务端之间使用虚拟专用网(VPN)可以确保客户端连接的是预期的服务器。

控制报文和 Payload 的完整性(Integrity)

  • TLS:提供了对网络传输的数据做完整性校验的哈希算法
  • 应用层:可以在应用消息中单独包含哈希值。这样做可以为 PUBLISH 控制报文的网络传输和静态数据提供内容的完整性检查
  • VPN:在客户端和服务端之间使用虚拟专用网(VPN)连接可以在 VPN 覆盖的网络段提供数据完整性检查

控制报文和 Payload 的保密性(Privacy)

  • 轻量级加密:AES or DES,可适用于低端设备
  • TLS:可以对网络传输的数据加密
  • 应用层:可以单独加密 Payload 内容。这可以提供 Payload 传输途中和静态数据的私密性。但不能给应用消息的其它属性如 Topic 加密
  • 静态数据加密:客户端和服务端实现可以加密存储静态数据,例如可以将应用消息作为会话的一部分存储
  • VPN:在客户端和服务端之间使用虚拟专用网(VPN)连接可以在 VPN 覆盖的网络段保证数据的私密性

异常行为的检测

  • 服务端实现可以监视客户端的行为,检测潜在的安全风险。例如:
  • 重复的连接请求
  • 重复的身份验证请求
  • 连接的异常终止
  • 主题扫描 (请求发送或订阅大量主题)
  • 发送无法送达的消息 (没有订阅者的主题)
  • 客户端连接但是不发送数据
  • 应对策略
  • 发现违反安全规则的行为,服务端实现可以断开客户端连接
  • 可以基于 IP地址 或 ClientID 实现一个 动态黑名单列表
  • 可以使用网络层面的控制,实现基于 IP 地址或其它信息的 速率限制 或黑名单
  • 连接拒绝与错误码

最佳实践  Best Practice

客户端 Client

选型

  • Android
  • iOS
  • Javascript
  • PHP
  • Java

服务器端 Server

Broker选型

  • Mosquitto
  • ActiveMQ
  • RabbitMQ
  • HiveMQ
  • VerneMQ
  • EMQ
  • Apollo (from ActiveMQ)
  • RocketMQ
  • 商用版
  • Mosca

Benchmark

  • Mosquitto + Apollo + Rabbit MQ + Mosca + VerneMQ 测评
  • RabbitMQ vs ActiveMQ
  • RocketMQ vs Kafka(18项差异)
  • RocketMQ vs Kafka vs RabbitMQ 消息中间件的对比 —— 消息发送性能
  • 业界主流MQ对比

MQTT协议通俗讲解相关推荐

  1. 股票服务器通讯协议,通俗讲解股票类app - TCP网络通信层设计

    场景 要做的产品 --- 炒股app 数据量大:5000多支股票,每支股票有分时,分笔数据,画一条k线,可能要500条分时数据,甚至更多. 实时性:股价每分每秒都在变化,一分钟产生很多条数据,用户要看 ...

  2. WIZnet W5500系列培训活动之“MQTT协议讲解和实践(接入OneNET)”

     万物联网的时代已经到来,物联网也由当初的概念开始进一步落实.物联网通信协议当中发展最迅速.应用最广泛的就是MQTT,它是轻量级基于代理的发布/订阅的消息传输协议,并且可以通过很少的代码和带宽与远程设 ...

  3. C语言实现MQTT协议(一)协议讲解

    MQTT介绍 MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议.它的设计思想是轻巧.开放.简单.规范,易于实现.这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器 ...

  4. 通俗讲解:PoW共识机制与以太坊的关系、Ghost协议 及 Casper PoS共识机制的变种...

    作者:林冠宏 / 指尖下的幽灵 掘金:juejin.im/user/587f0d- 博客:www.cnblogs.com/linguanh/ GitHub : github.com/af9133374 ...

  5. 通俗讲解:PoW共识机制与以太坊的关系、Ghost协议 及 PoS共识机制的变种---Casper...

    作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...

  6. opc ua 用哪种语言编写_通俗讲解OPC UA 之通俗入门篇

    这是一篇通俗讲解OPC UA的入门篇 为什么要通俗的讲呢,主要是怕自己讲的自己看不懂,会尴尬. 比这更尴尬的是,自己写的没看懂,你们却看懂了. OPC UA内容过于深奥,笔者最近研究,说不上研究,只能 ...

  7. 冲突域、广播域的通俗讲解

    冲突域.广播域的通俗讲解 1.冲突域(物理分段)         连接在同一导线上的所有工作站的集合,或者说是同一物理网段上所有节点的集合或以太网上竞争同一带宽的节点集合.这个域代表了冲突在其中发生并 ...

  8. IoT:MQTT协议详解

    IoT -- (七)MQTT协议详解 转自:https://blog.csdn.net/anxianfeng55555/article/details/80908795 MQTT是什么? MQTT(M ...

  9. 利用nodemcu和mqtt协议让嵌入式设备接入互联网(一.layui前端框架)

    文章目录 前言 HTML知识简单总结 vscode快捷操作 HTML知识总结 HMTL结构 头部 身体 样式表CSS layui介绍 源码 前言 为了让自己的嵌入设备接入互联网,而且能互相通讯,用直接 ...

最新文章

  1. lighttpd 负载均衡-反向代理+cache浅谈
  2. 【python教程入门学习】Pandas库下载和安装
  3. 基于Selenium2+Java的UI自动化(6)-操作Alert、confirm、prompt弹出框
  4. 科技推动时代发展,浅谈IT技术如何改善数据中心运维管理
  5. access开发内销核算系统
  6. live555 windows下编译以及修订
  7. 当鼠标移入时切换背景图时闪烁的问题
  8. 家里安装20M宽带,为什么看视频都不卡,但一玩游戏就卡的要死?
  9. 【ajax】前端ajax传值的几种方法
  10. mysql密码修改无效后,修改方法
  11. list_for_each_entry和list_for_each_entry_safe
  12. java题库管理系统java试题管理系统java考试管理系统
  13. iphone手机html视频播放,iphone5视频格式 传到苹果手机上的视频怎么才能看
  14. mac android 模拟器启动,react-native使用脚本启动android模拟器(macos)
  15. 关闭compactos_compact 命令详解 – 原来文件还能这样压缩!
  16. [Audacity][帮助手册][手册内容]认识Audacity
  17. java csv 追加_java读、写、追加CSV
  18. ERROR 1227 (42000): Access denied; you need (at least one of) the SUPER privilege(s) for this operat
  19. 欺骗的艺术——第二部分(1)
  20. 解决小程序Failed to load localimage resource/pages/index/index/in.jpgtheserverrespondedwithastatusof500问题

热门文章

  1. 简述Linux虚拟内存管理
  2. Spring Cloud Gateway的全局异常处理
  3. Android --- 在Andoird应用程序中打开相册却没有图片(夜神模拟器)怎么办?
  4. Java 洛谷 P1035 级数求和
  5. 第十届蓝桥杯java B组—试题I 后缀表达式
  6. python 下载图片损坏_爬虫爬取出的图片下载出错,图片文件直接损坏
  7. 蜂鸣器接入_无源蜂鸣器的原理和和mixly结合制作电子琴
  8. oracle事务处理类型,Oracle EBS-SQL (OM-5):检查订单事务处理类型.sql
  9. 网络技术工程师专业核心 | 网络技术工程师就业方向
  10. 读懂 | 路由器简史