文章目录
  1. 1. 前言
  2. 2. 协议介绍
    1. 2.1. 固定头部
    2. 2.2. 消息类型
    3. 2.3. DUP flag(打开标志)
    4. 2.4. QoS(Quality of Service,服务质量)
    5. 2.5. RETAIN(保持)
    6. 2.6. Remaining Length(剩余长度)
    7. 2.7. 可变头部
    8. 2.8. Playload/消息体/负荷
    9. 2.9. 消息标识符/消息ID
    10. 2.10. 小结

前言

  MQTT(Message Queue Telemetry Transport),遥测传输协议,提供订阅/发布模式,更为简约、轻量,易于使用,针对受限环境(带宽低、网络延迟高、网络通信不稳定),可以简单概括为物联网打造,官方总结特点如下:

1
2
3
4
5
6
7
8
9
1.使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合。
2. 对负载内容屏蔽的消息传输。
3. 使用 TCP/IP 提供网络连接。
4. 有三种消息发布服务质量:
“至多一次”,消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
“至少一次”,确保消息到达,但消息重复可能会发生。
“只有一次”,确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果。
5. 小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量。
6. 使用 Last Will 和 Testament 特性通知有关各方客户端异常中断的机制。

  在沉寂了四年之后, MQTT 3.1.1规范 于2014年10月30号正式发布,与此同时MQTT 3.1.1已成为OASIS(结构化信息标准促进组织)开放物联网消息传递协议标准( 连接1 连接2 ),换种说法就是MQTT 3.1.1已升级为国际物联网标准。
  细心的朋友一定会发现,原来MQTT 3.1规范是IBM的协议,到了MQTT 3.1.1规范 后就变成了OASIS的标准。
  中文版的文档查看以下的链接:
  MQTT协议v3.1中文版
  MQTT协议v3.1.1中文版

协议介绍

固定头部

  固定头部,使用两个字节,共16位:

mqtt 固定头部
Byte 1 消息类型和标志字段,消息类型(4-7),使用4位二进制表示,可代表16种消息类型。
Byte 2 剩余长度字段(至少1个字节,最多4个字节),采用big-endian模式存储。

消息类型

mqtt 消息类型

除去0和15位置属于保留待用,共14种消息事件类型。

DUP flag(打开标志)

保证消息可靠传输,默认为0,只占用一个字节,表示第一次发送。不能用于检测消息重复发送等。只适用于客户端或服务器端尝试重发PUBLISH, PUBREL, SUBSCRIBE 或 UNSUBSCRIBE消息,注意需要满足以下条件:

1
2
当 QoS > 0
消息需要回复确认

此时,在可变头部需要包含消息ID。当值为1时,表示当前消息先前已经被传送过。

QoS(Quality of Service,服务质量)

  使用两个二进制表示PUBLISH类型消息:

mqtt 服务质量

RETAIN(保持)

  仅针对PUBLISH消息。不同值,不同含义:
  1:表示发送的消息需要一直持久保存(不受服务器重启影响),不但要发送给当前的订阅者,并且以后新来的订阅了此Topic name的订阅者会马上得到推送。

备注:新来乍到的订阅者,只会取出最新的一个RETAIN flag = 1的消息推送。

  0:仅仅为当前订阅者推送此消息。
假如服务器收到一个空消息体(zero-length payload)、RETAIN = 1、已存在Topic name的PUBLISH消息,服务器可以删除掉对应的已被持久化的PUBLISH消息。

Remaining Length(剩余长度)

  在当前消息中剩余的byte(字节)数,包含可变头部和负荷(称之为内容/body,更为合适)。单个字节最大值:01111111,16进制:0x7F,10进制为127。单个字节为什么不能是11111111(0xFF)呢?因为MQTT协议规定,第八位(最高位)若为1,则表示还有后续字节存在。
  同时MQTT协议最多允许4个字节表示剩余长度。那么最大长度为:0xFF,0xFF,0xFF,0x7F。
二进制表示为:11111111,11111111,11111111,01111111
十进制:268435455 byte=261120KB=256MB=0.25GB
四个字节之间值的范围:

mqtt 剩余长度

可变头部

  固定头部仅定义了消息类型和一些标志位,一些消息的元数据,需要放入可变头部中。可变头部内容字节长度 + Playload/负荷字节长度 = 剩余长度,这个是需要牢记的。可变头部,包含了协议名称,版本号,连接标志,用户授权,心跳时间等内容,这部分和后面要讲到的CONNECT消息类型,有重复,暂时略过。

Playload/消息体/负荷

  消息体主要是为配合固定/可变头部命令(比如CONNECT可变头部User name标记若为1则需要在消息体中附加用户名称字符串)而存在。
  CONNECT/SUBSCRIBE/SUBACK/PUBLISH等消息有消息体。PUBLISH的消息体以二进制形式对待。
  请记住MQTT协议只允许在PUBLISH类型消息体中使用自定义特性,在固定/可变头部想加入自定义私有特性,就免了吧。这也是为了协议免于流于形式,变得很分裂也为了兼顾现有客户端等。比如支持压缩等,那就可以在Playload中定义数据支持,在应用中进行读取处理。这部分会在后面的文章中详细论述。

消息标识符/消息ID

  固定头中的QoS level标志值为1或2时才会在:PUBLISH,PUBACK,PUBREC,PUBREL,PUBCOMP,SUBSCRIBE,SUBACK,UNSUBSCRIBE,UNSUBACK等消息的可变头中出现。
  一个16位无符号位的short类型值(值不能为 0,0做保留作为无效的消息ID),仅仅要求在一个特定方向(服务器发往客户端为一个方向,客户端发送到服务器端为另一个方向)的通信消息中必须唯一。比如客户端发往服务器,有可能存在服务器发往客户端会同时存在重复,但不碍事。
  可变头部中,需要两个字节的顺序是MSB(Most Significant Bit) LSB(Last/Least Significant Bit),翻译成中文就是,最高有效位,最低有效位。最高有效位在最低有效位左边/上面,表示这是一个大端字节/网络字节序,符合人的阅读习惯,高位在最左边。

mqtt 消息标识符

小结

  掌握固定头部的QoS level、RETAIN标记、可变头部的Connect flags作用和意义,对总体理解MQTT作用很大。下面列举了一些常用的操作:
CONNECT
  TCP连接建立完毕后,Client向Server发出一个Request。
如果一段时间内接收不到Server的Response,则关闭socket,重新建立一个session连接。
  如果一个ClientID已经与服务器连接,则持有同样ClientID的旧有连接必须由服务器关闭后,新建立才能建立。
CONNACK
  Server发出Response响应。

1
2
3
4
5
6
0x00 Connection Accepted
0x01 Connection Refused: unacceptable protocol version
0x02 Connection Refused: identifier rejected
0x03 Connection Refused: server unavailable
0x04 Connection Refused: bad user name or password
0x05 Connection Refused: not authorized

PUBLISH 发布消息
  Client/Servier均可以进行PUBLISH。
  publish message 应该包含一个TopicName(Subject/Channel),即订阅关键词。

关于Topic通配符

  • /:用来表示层次,比如a/b,a/b/c。
  • #:表示匹配>=0个层次,比如a/#就匹配a/,a/b,a/b/c。
    单独的一个#表示匹配所有。不允许 a#和a/#/c。
  • +:表示匹配一个层次,例如a/+匹配a/b,a/c,不匹配a/b/c。
    单独的一个+是允许的,a+不允许,a/+/b不允许

PUBACK 发布消息后的确认
  QoS=1时,Server向Client发布该确认(Client收到确认后删除),订阅者向Server发布确认。
PUBREC / PUBREL / PUBCOMP
  QoS=2时
  1. Server->Client发布PUBREC(已收到);
  2. Client->Server发布PUBREL(已释放);
  3. Server->Client发布PUBCOMP(已完成),Client删除msg;订阅者也会向Server发布类似过程确认。
PINGREQ / PINGRES 心跳
  Client有责任发送KeepAliveTime时长告诉给Server。在一个时长内,发送PINGREQ,Server发送PINGRES确认。
  Server在1.5个时长内未收到PINGREQ,就断开连接。
  Client在1个时长内未收到PINGRES,断开连接。
  一般来说,时长设置为几个分钟。最大18小时,0表示一直未断开。
Clean Session
  如果为false(flag=0),Client断开连接后,Server应该保存Client的订阅信息。
  如果为true(flag=1),表示Server应该立刻丢弃任何会话状态信息。

文章目录
  1. 1. 前言
  2. 2. 协议介绍
    1. 2.1. 固定头部
    2. 2.2. 消息类型
    3. 2.3. DUP flag(打开标志)
    4. 2.4. QoS(Quality of Service,服务质量)
    5. 2.5. RETAIN(保持)
    6. 2.6. Remaining Length(剩余长度)
    7. 2.7. 可变头部
    8. 2.8. Playload/消息体/负荷
    9. 2.9. 消息标识符/消息ID
    10. 2.10. 小结