MQTT协议通俗讲解
- 定义:某个客户端(由ClientID作为标识)和某个服务器之间的逻辑层面的通信
- 生命周期(存在时间):会话 >= 网络连接
- 客户端唯一标识,服务端用于关联一个Session
- 只能包含这些 大写字母,小写字母 和 数字(0-9a-zA-Z),23个字符以内
- 如果 ClientID 在多次 TCP连接中保持一致,客户端和服务器端会保留会话信息(Session)
- 同一时间内 Server 和同一个 ClientID 只能保持一个 TCP 连接,再次连接会踢掉前一个
- 在Connect时,由客户端设置
- 0 —— 开启会话重用机制。网络断开重连后,恢复之前的Session信息。需要客户端和服务器有相关Session持久化机制。
- 1 —— 关闭会话重用机制。每次Connect都是一个新Session,会话仅持续和网络连接同样长的时间。
- 已经发送给服务端,但是还没有完成确认的 QoS 1 和 QoS 2 级别的消息
- 已从服务端接收,但是还没有完成确认的 QoS 2 级别的消息
- 会话是否存在,即使会话状态的其它部分都是空 (SessionFlag)
- 客户端的订阅信息 (ClientSubcription)
- 已经发送给客户端,但是还没有完成确认的 QoS 1 和 QoS 2 级别的消息
- 即将传输给客户端的 QoS 1 和 QoS 2 级别的消息
- 已从客户端接收,但是还没有完成确认的 QoS 2 级别的消息
- (可选)准备发送给客户端的 QoS 0 级别的消息
- 目的是保持长连接的可靠性,以及双方对彼此是否在线的确认。
- 客户端在Connect的时候设置 Keep Alive 时长。如果服务端在 1.5 * KeepAlive 时间内没有收到客户端的报文,它必须断开客户端的网络连接
- Keep Alive 的值由具体应用指定,一般是几分钟。允许的最大值是 18 小时 12 分 15 秒
- 遗嘱消息(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 —— 遗嘱消息内容
- 存在报文的可变报头部分,非零两个字节整数 (0-65535]
- 一个流程中重复:这些报文包含 PacketID,而且在一次通信流程内保持一致:
- PUBLISH(QoS>0 时),PUBACK,PUBREC,PUBREL,PUBCOMP
- SUBSCRIBE, SUBACK
- UNSUBSCIBE,UNSUBACK
- 新的不重复:客户端每次发送一个新的这些类型的报文时都必须分配一个当前 未使用的PacketID
- 当客户端处理完这个报文对应的确认后,这个报文标识符就释放可重用。
- 独立维护:客户端和服务端彼此独立地分配报文标识符。因此,客户端服务端组合使用相同的报文标识符可以实现 并发 的消息交换。可能出现一下情况,并不算异常:

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


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

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


- 发送者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 只收到一次 。
- 如果客户端或者服务器发送了一个 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 时,肯定不会存在乱序的问题。
- Pub-Sub消息模型的核心机制
- UTF-8 编码字符串,不能超过 65535 字节。层级数量没有限制
- 不能包含任何的下文中提到的特殊符号(/、+、#),必须至少包含一个字符
- 区分大小写,可以包含空格,不能包含空字符 (Unicode U+0000)
- 在收部或尾部增加 斜杠 “/”,会产生不同的Topic和TopicFilter。举例:
- “/A” 和 “A” 是不同的
- “A” 和 “A/” 是不同的
- 只包含斜杠 “/” 的 Topic 或 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/#”
- 订阅机制基于TopicFilter匹配
- 一个Subsribe请求 可订阅多个 Topic(节省带宽,多订阅尽量用一次请求)。取消订阅也同理
- 每一个订阅需要指定一个QoS,指定了客户端接收消息所允许的最大QoS级别。但是服务器端最终授权返回的QoS可能会小于等于客户端请求的QoS
- 对于高于QoS的消息(比如说订阅的QoS限制到1,消息的QoS指定到2),那么客户端会收到一个QoS降低为指定的 限制QoS 的消息(消息的QoS降为1,不保证只收到一次)
- 订阅关系可以被覆盖,以TopicFilter为标识。如果后面订阅一个相同的TopicFilter,但是指定的QoS不同,则以后面的为准,QoS升高后,重发相应等级的 Retain 消息
- 可以采用 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 的隐私控制
- 用户名+密码验证:Connect 登录的时候,传入 UserName 和 Password
- 相关标记位:在Connect时,由客户端设置
- 用户名(UserName Flag)标记设置为1,才可以穿入
- 密码(Password Flag)标记设置为1
- 外部验证:LDAP、OAuth 或者 操作系统的认证机制
- 用户名密码加密:防止中间人攻击和重放攻击
- 应用层:客户端通过应用消息给服务端发送凭证用于身份验证。
- 授权:基于客户端提供的信息如用户名、客户端标识符(ClientId)、客户端的主机名或 IP 地址,或者身份认证的结果,服务端可以限制对某些服务端资源的访问
- MQTT 协议不是双向信任的,它没有提供客户端验证服务端身份的机制
- TLS:客户端可以使用服务端发送的SSL证书验证服务端的身份
- 应用层:可以通过服务端给客户端发送凭证用于身份验证的应用层消息
- VPN:在客户端和服务端之间使用虚拟专用网(VPN)可以确保客户端连接的是预期的服务器。
- TLS:提供了对网络传输的数据做完整性校验的哈希算法
- 应用层:可以在应用消息中单独包含哈希值。这样做可以为 PUBLISH 控制报文的网络传输和静态数据提供内容的完整性检查
- VPN:在客户端和服务端之间使用虚拟专用网(VPN)连接可以在 VPN 覆盖的网络段提供数据完整性检查
- 轻量级加密:AES or DES,可适用于低端设备
- TLS:可以对网络传输的数据加密
- 应用层:可以单独加密 Payload 内容。这可以提供 Payload 传输途中和静态数据的私密性。但不能给应用消息的其它属性如 Topic 加密
- 静态数据加密:客户端和服务端实现可以加密存储静态数据,例如可以将应用消息作为会话的一部分存储
- VPN:在客户端和服务端之间使用虚拟专用网(VPN)连接可以在 VPN 覆盖的网络段保证数据的私密性
- 服务端实现可以监视客户端的行为,检测潜在的安全风险。例如:
- 重复的连接请求
- 重复的身份验证请求
- 连接的异常终止
- 主题扫描 (请求发送或订阅大量主题)
- 发送无法送达的消息 (没有订阅者的主题)
- 客户端连接但是不发送数据
- 应对策略
- 发现违反安全规则的行为,服务端实现可以断开客户端连接
- 可以基于 IP地址 或 ClientID 实现一个 动态黑名单列表
- 可以使用网络层面的控制,实现基于 IP 地址或其它信息的 速率限制 或黑名单
- 连接拒绝与错误码


- Android
- iOS
- Javascript
- PHP
- Java
- Mosca
MQTT协议通俗讲解的更多相关文章
- 通俗讲解:PoW共识机制与以太坊的关系、Ghost协议 及 PoS共识机制的变种---Casper
作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...
- MQTT协议 局域网和广域网 云服务器和虚拟主机、VPS SSH和FTP、SFTP
MQTT协议 MQTT协议就很好的解决了coap存在的问题.MQTT协议是由IBM开发的即时通讯协议,相比来说比较适合物联网场景的通讯协议.MQTT协议采用发布/订阅模式,所有的物联网终端都通过TC ...
- 网络编程懒人入门(九):通俗讲解,有了IP地址,为何还要用MAC地址?
1.前言 标题虽然是为了解释有了 IP 地址,为什么还要用 MAC 地址,但是本文的重点在于理解为什么要有 IP 这样的东西.本文对读者的定位是知道 MAC 地址是什么,IP 地址是什么. (本文同步 ...
- MQTT 单片机端讲解
有空了和大家分享一下,如何从头架构一个高效mqtt并行客户端,基于传统GPRS等较差网络环境和网关等网络环境好的情景(当然仔细讲解mqtt的基本函数使很有必要的).---这会正忙着搬砖 MQTt协议 ...
- MQTT协议以及库函数说明
内容简介:之前我们写了一篇ESP8266AT指令的说明文档,在客户端连接上服务器之后,接下来便是网络通信.为此我们又写了这一片MQTT协议的说明文档,更加深层次的讲述了通信的原理.此文档只是我们在调试 ...
- 转战物联网·基础篇05-通俗理解MQTT协议的实现原理和异步方式
网络上搜索MQTT协议,会出现太多的解释,这里就不做官方标准释义的复制了.这一节我们从实战理解角度,通俗的将MQTT协议的作用及实现原理说一下,旨在可以快速理解MQTT协议.所以可能会出现很多看似 ...
- MQTT主题Topic讲解
文章转载于https://www.cnblogs.com/hayasi/p/7792191.html 我们已经把相关的连接报文搞定了.笔者想来想去还是决定先讲解一下订阅报文(SUBSCRIBE ).如 ...
- 海鑫智圣:物联网漫谈之MQTT协议
什么是MQTT协议 MQTT(消息队列遥测传输协议)是IBM在1999年专门针对物联网等应用场景来制订的轻量级双向消息传输协议,它主要是为了解决物联网上使用到的设备的互相通信的问题,以及这些设备与后端 ...
- 基于MQTT协议进行应用开发
官方协议有句如下的话来形容MQTT的设计思想: "It is designed for connections with remote locations where a "sma ...
随机推荐
- python的计算保留小数
1.要使得算术运算的结果有小数,则运算的对象至少有一个是float型的. 2.控制小数的位数:字符串格式化 格式:需要进行格式化的字符串%插入对象 需要进行格式化的字符串中带有一个或多个嵌入的转换目标 ...
- mysql的一些密码错误问题,并从windows登录linux主机的mysql
mysqld_safe --skip-grant-tables& mysql -u root mysql 可以修改密码: mysql>update mysql.user set auth ...
- android 修改源码framework后如何编译【转】
本文转载自:https://blog.csdn.net/fuchengbo000/article/details/43193801 1.如果在framework/base/core/res/res下添 ...
- JAVAWeb学习总结(二)
JavaWeb学习总结(二)——Tomcat服务器学习和使用(一) 一.Tomcat服务器端口的配置 Tomcat的所有配置都放在conf文件夹之中,里面的server.xml文件是配置的核心文件. ...
- 多校hdu5738 寻找
这道题前面给了两个公式,其实仔细分析一下,就会发现其实是给了你一堆点的坐标,然后让你求这些点有多少种组合可以形成共线的情况当两个点在一个坐标上时这两个点可以看做是不同的两个点,也就是说如果两个点在一个 ...
- dedecms 织梦列表页标题增加显示页数
判断是否为第一页,为第一页则不显示页数的代码: {dede:pagelist listsize='0' listitem='pageno' function='html2text(@me)' runp ...
- Adobe Reader 的直接下载地址
页面:https://get.adobe.com/reader/direct/ 选择操作系统.选择语言.选择版本,然后点击立即下载. https://ardownload2.adobe.com/pub ...
- 从AD域获取用户AD信息
public static Dictionary<string, string> SearchADInfo(string adName) { string strTemp = " ...
- SQL使用指南(1)—— 数据定义语言(DDL)
1.使用create 语句创建表 CREATE TABLE table_name (column_name datatype[null|not null], column_name datatype[ ...
- C++中string的常见用法
在ACM中主要用到string的这几个功能:赋值,添加,删除,替换,查找,比较,反向排序. 1.赋值 直接来就行: string ss; ss="aaa"; 或者 string s ...