• MQTT协议通俗讲解


    • 协议文档 Offical Docs
    v3.1.1
    其他资源
    网站
    测试工具
    基本概念 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 时)PUBACKPUBRECPUBRELPUBCOMP
    • SUBSCRIBE,  SUBACK
    • UNSUBSCIBEUNSUBACK                                        
    • 新的不重复:客户端每次发送一个新的这些类型的报文时都必须分配一个当前 未使用的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 [RFC5246WebSocket 作为传输层。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选型
    • Mosca
    Benchmark
    分布式部署 Cluster
     
     
    TopicTree 设计
    参考
    参考 Reference
    • 协议文档 Offical Docs
    v3.1.1
    其他资源
    网站
    测试工具
    基本概念 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 时)PUBACKPUBRECPUBRELPUBCOMP
    • SUBSCRIBE,  SUBACK
    • UNSUBSCIBEUNSUBACK                                        
    • 新的不重复:客户端每次发送一个新的这些类型的报文时都必须分配一个当前 未使用的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 [RFC5246WebSocket 作为传输层。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选型
    • Mosca
    Benchmark
    分布式部署 Cluster
     
     
    TopicTree 设计
    参考
  • 相关阅读:
    配置java环境变量
    编写Java中的第一个Java程序:HelloWorld(你好世界:问世)
    对Java的加载与执行的理解(理论比较重要)
    JDK、JRE、JVM三者之间的关系?
    Java语言的特性
    计算机编程语言发展史
    DRF解析器
    DRF分页组件
    DRF频率组件
    DRF权限组件
  • 原文地址:https://www.cnblogs.com/jiangzhaowei/p/8459452.html
Copyright © 2020-2023  润新知