0%

MQTT协议基础概念

MQTT协议基础(1):MQTT协议的基本介绍

MQTT协议的基础知识包括基本定义、通信模型的介绍、Client、Broker介绍
数据包格式:固定头、可变头、消息体

定义&特点

  • 概念:mqqt协议(Message Queuing Telemetry Transport),是运行在tcp/ip协议栈之上的应用层协议,应用场景为物联网行业。

    同传统的消息队列相比,MQTT具有以下的不同

    • 传统消息队列需要先设立相应的队列来放置消息,而MQTT协议可以不用预先设立topic,直接发布。
    • 传统消息队列中未被取用的数据会一直保存,而MQTT协议中发布的消息若没有被任何一个客户端订阅则直接丢弃
    • 传统消息队列中,消息只能被一个客户端使用,而MQTT协议下,发布的消息可以被多个客户端订阅并接收

通信模型

  • 概念:MQTT是采用发布/订阅的模式来构建通信模型。图示如下

    模型图

    可以看到,发布方和接收方不必直接建立连接。通过mqtt服务器判别发布/订阅关系,来分发相应的数据。

通信过程

  • 在MQTT协议中,我们将服务端称为Broker,客户端称为client。同时客户端中分为:Publisher(发布方),Subscriber(订阅方)
    在通讯流程中,其大致过程如下
    • 发布方与订阅方都建立了到Broker的TCP连接
    • 订阅方告知Broker它要订阅的主题(Topic)
    • 发布方将消息发送到Broker,并指定消息的主题(Topic)
    • Broker接收到消息后,检查哪些订阅方订阅了该主题,分发该消息到相应订阅方
    • 订阅方从Broker获取该消息
    • 如果某个订阅方此时处于离线状态,Broker先为其保留此条消息,当订阅方下次连接时,将消息发到订阅方

通信主体

  • MQTT Client

    任何终端类型,在程序中运行运行MQTT协议库或者运用其代码,连接到MQTT Broker,都可以称作客户端。客户端中Publisher和Subscriber的属性取决于当前的行为状态是在发布消息还是订阅消息。当然也可以既是Publisher也是Subscriber。

    MQTT协议库, 可以查看相关源码:https://github.com/mqtt/mqtt.org/wiki/libraries

  • MQTT Broker

    Broker的主体功能是接收Publisher发布的消息,分发到相应的Subscriber。除此之外还有如下的功能要求

    • 可以对Client的接入进行授权,并对Client进行权限控制
    • 可以横向扩展,比如集群,满足海量Client的接入
    • 有较好的扩展性,可以比较好的接入原有的业务系统
    • 易于监控,满足高可用性(注:Broker负责核心的业务,监控其实时状态可以有效避免因Broker故障而停机)

MQTT协议数据包格式

MQTT协议的数据包采用二进制。一个数据包由三部分组成

  • 固定头:存在于所有的MQTT协议数据包中,用于表示数据包类型&对应标识&表明数据包大小
  • 可变头:存在于部分的MQTT协议数据包中,具体内容由相应类型数据包决定。
  • 消息体:存在于部分的MQTT协议数据包中,存储消息的具体数据。

固定头的数据包分析

基本样式:

基本样式

第一个字节,高四位用来指定报文的类型,第四位用作报文类型标志位。
从第二个字节开始,指定剩余长度。该部分最少一个字节,最多四个字节,表示范围0(0x00)~268 435 455(0xFF 0xFF 0xFF 0x7F),也就是0-256MB.

  • 控制报文类型

控制报文类型

  • 标志位定义(只有PUBLISH不同,其余均被置0保留)

标志位

  • 剩余长度
    在MQTT协议中,采用可变长度来表示剩余长度,这个字段最少一个字节,最多四个字节。其中,每一个字节的最高位叫做延续位,用于标志是否还有一个用于表示剩余长度的字节,剩下的低7位用于标识值0~127.

其可以表示的范围如下

剩余长度

这个范围是如何得来的? 编码时如何确定转换关系。

因为每个字节的最高位是标志位,所以其实际范围可以这样看待

1
2
3
4
5
字节数                   范围                                  计算方式                  最大值
1 0x00~0x7F 0~127 2^7-1
2 (0x80 0x01)~(0xFF 0x7F) 128~128*128-1 2^14-1
3 (0x80 0x80 0x01)~(0xFF 0xFF 0x7F) 128*128~128*128*128-1 2^21-1
4 (0x80 0x80 0x80 0x01)~(0xFF 0xFF 0xFF 0x7F) 128*128*128~128*128*128*128-1 2^28-1

通俗地说,在MQTT中当达到8的整数倍的字节时,进位要进到下一个字节。

示例:321如何编码?

先判断属于两个字节编码范围。

1
2
321/128= 2             高8位为2              0x02
321%128= 65 低8位为65+128(因为延续位置1) 0xC1

321编码之后0xC1 0x02

可变报头

  • 定义

    某些MQTT控制报文中包含可变报头,它在固定报头和负载(消息体)之间。具体内容由报文的类型来确定。常见的报文标识符如下:

    可变头定义

  • 应用范围

    可变头应用场景

  • 进一步理解

    出于简单理解,可将该标识符类比于TCP传输中序列号。在某些类型的报文中,在发送报文时要为这个新报文分配一个当前并未使用的报文标识符。如果需要重发某个特殊报文,报文标识符必须一致,当客户端处理完这个报文的确认之后,这个报文标识符就可以释放重用了。而且Broker和Client分配的报文标识符是独立的,也就是说可能出现同样的报文标识符但来自不同的主体。

    标识符独立分配

消息体(有效载荷(payload))

  • 定义

    最为重要的是在PUBLISH中,有效载荷即为应用消息。

  • 应用范围

    有效载荷