zoukankan      html  css  js  c++  java
  • MQTT协议简析

    1 概述

    MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是一个C/S架构的发布/订阅模式消息传输协议,最早在1999年由IBM的Andy Stanford-Clark博士和Arcom公司的ArlenNipper博士提出,本文的MQTT协议主要基于MQTT3.1.1

    2 报文结构

    MQTT报文结构主要分为固定报头、可变报头、有效载荷3个部分,具体结构如下表所示:

    说明字节数76543210
    固定报头 1 Message Type Dup Flag QoS Level Retain Flag
      1~4 剩余长度,最小1字节最大4字节
    可变报头 1 消息标识符最高有效位MSB
      1 消息标识符最低有效位LSB
    有效载荷 N Payload

    2.1 固定报头(Fixed Header)

    每个MQTT报文都包含至少2个字节的固定报头,包括消息类型(Message Type)、重发标识(Dup Flag)、质量等级(QoS)、保持标识(Retain Flag)、剩余长度等字段。

    2.1.1 消息类型

    MQTT第1个字节的前4位表示消息类型,具体的消息类型定义如下表所示:

    名字报文流动方向描述
    Reserved 0 禁止 保留值
    CONNECT 1 客户端到服务端 客户端请求连接服务端
    CONNACK 2 服务端到客户端 连接报文确认
    PUBLISH 3 两个方向都允许 发布消息
    PUBACK 4 两个方向都允许 QoS 1消息发布收到确认
    PUBREC 5 两个方向都允许 发布收到(保证交付第一步)
    PUBREL 6 两个方向都允许 发布释放(保证交付第二步)
    PUBCOMP 7 两个方向都允许 QoS 2消息发布完成(保证交互第三步)
    SUBSCRIBE 8 客户端到服务端 客户端订阅请求
    SUBACK 9 服务端到客户端 订阅请求报文确认
    UNSUBSCRIBE 10 客户端到服务端 客户端取消订阅请求
    UNSUBACK 11 服务端到客户端 取消订阅报文确认
    PINGREQ 12 客户端到服务端 心跳请求
    PINGRESP 13 服务端到客户端 心跳响应
    DISCONNECT 14 客户端到服务端 客户端断开连接
    Reserved 15 禁止 保留值

    2.1.2 标识符

    固定报头第一个字节的后4位为标识符,对于PUBLISH类型的消息来说,分别表示DUP、QoS、RETAIN3个标识,对于其他类型的消息来说,这4位为保留位,具体的值应该符合下表的定义,如果收到非法的标识符,协议的双方应该选择关闭网络连接。

    控制报文固定报头标志Bit 3Bit 2Bit 1Bit 0
    CONNECT Reserved 0 0 0 0
    CONNACK Reserved 0 0 0 0
    PUBLISH Used in MQTT 3.1.1 DUP1 QoS2 QoS2 RETAIN3
    PUBACK Reserved 0 0 0 0
    PUBREC Reserved 0 0 0 0
    PUBREL Reserved 0 0 1 0
    PUBCOMP Reserved 0 0 0 0
    SUBSCRIBE Reserved 0 0 1 0
    SUBACK Reserved 0 0 0 0
    UNSUBSCRIBE Reserved 0 0 1 0
    UNSUBACK Reserved 0 0 0 0
    PINGREQ Reserved 0 0 0 0
    PINGRESP Reserved 0 0 0 0
    DISCONNECT Reserved 0 0 0 0
    2.1.2-1 重发标识(Dup Flag)

    对PUBLISH类型的消息来说,MQTT第1个字节的第3位表示重发标识,主要用于保证消息可靠传输,默认值为0,表示第一次发送。

    当QoS为大于0时,发布的消息需要回复确认,如果客户端或服务器端没有收到确认回复则尝试重发消息,此时消息的Dup Flag被置为1(需要指出的是,该标识位不能用于检测消息的重复发送)。

    2.1.2-2 质量等级(QoS Level)

    MQTT使用第1个字节第2、1位表示质量等级,具体的定义如下表所示:

    QoS Level21说明
    0 0 0 至多1次(At Most Once)
    1 0 1 至少1次(At Least Once)
    2 1 0 保证1次(Exactly Once)
    3 1 1 保留值
    2.1.2-3 保持标识(Retain Flag)

    MQTT报文第1字节第0位用于保持标识,服务端和客户端处理保持标识时需要满足以下规则:

    i.在客户端发给服务端的PUBLISH报文中,如果RETAIN标识被设置为1,服务端必须保存改消息及其QoS,以使该报文可以被投递给订阅了对应主题的新订阅者;

    ii.当新的订阅建立时,服务端必须把每一个主题保持的最近一条RETAIN消息(如果存在的话),投递给订阅者;

    iii.如果服务端收到QoS为0,RETAIN标识为1的消息,服务端必须丢弃该主题下保存的所有RETAIN消息,同时保存这条QoS为0的消息,但是这条消息可以随时被丢弃;

    iv.当一个新订阅建立时,服务端发给客户端的PUBLISH报文需设置RETAIN为1,其他情况服务端发给客户端的PUBLISH报文的RETAIN位都需要被置为0,不管服务端收到该PUBLISH报文时它的标识是什么;

    v.当一个0字节内容的PUBLISH报文的RETAIN标识为1时,服务端会把这条消息投递给订阅了对应主题的订阅者,同时清空这个主题下保持的所有消息。需要指出的是,客户端收到的这条消息的RETAIN标识为0,服务端不会保存0字节内容的消息;

    vi.当客户端向服务端发布一条RETAIN为0的PUBLISH报文时,服务端不会保存这条消息,也不会删除或者替换已经保存的RETAIN为1的消息;

    2.1.3 剩余长度

    MQTT报文的剩余长度是指可变报头和有效载荷的字节总数,从第2个字节开始,最大包括4个字节,剩余长度字段的大小及其能表示的范围如下表所示:

    字节数最小值最大值
    1 0 (0x00) 127 (0x7F)
    2 128 (0x80, 0x01) 16 383 (0xFF, 0x7F)
    3 16 384 (0x80, 0x80, 0x01) 2 097 151 (0xFF, 0xFF, 0x7F)
    4 2 097 152 (0x80, 0x80, 0x80, 0x01) 268 435 455 (0xFF, 0xFF, 0xFF, 0x7F)

     

    剩余长度的编码算法伪代码如下:

     

    do
        encodedByte = X MOD 128
        X = X DIV 128
        // if there are more data to encode, set the top bit of this byte
        if ( X > 0 )
            encodedByte = encodedByte OR 128
        endif
        'output' encodedByte
    while ( X > 0 )
    

      

    剩余长度的解码算法伪代码如下:

    multiplier = 1
    value = 0
    do
        encodedByte = 'next byte from stream'
        value += (encodedByte AND 127) * multiplier
        multiplier *= 128
        if (multiplier > 128*128*128)
            throw Error(Malformed Remaining Length)
    while ((encodedByte AND 128) != 0)
    

    2.2 可变报头(Variable Header)

    在某些类型的MQTT报文中存在位于固定报头和有效载荷之间的包含2个字节的可变报头。具体的报文类型包括PUBLISH (QoS > 0), PUBACK, PUBREC, PUBREL, PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK等。

    2.2.1

    可变报头需要满足以下规则:

    2.2.1-1

    SUBSCRIBE, UNSUBSCRIBE, PUBLISH (QoS > 0)等类型的报文必须包含一个16bit的报文标识符;

    2.2.1-2

    每次客户端发送上面几种类型的报文时,必须指定一个未使用过的标识符;

    2.2.1-3

    当一个客户端重发一个特定的MQTT报文时,重发的报文必须使用与被重发报文相同的报文标识符;

    当客户端重发的报文被确认后,这个报文的标识符可以在下次重用;

    重发报文被确认是指: QoS1的PUBLISH收到PUBACK, QoS2的PUBLISH收到PUBCOMP, SUBSCRIBE收到SUBACK, UNSUBSCRIBE收到UNSUBACK;

    2.2.1-4

    服务端发送QoS>0的PUBLISH消息也要遵循2.2.3相同的规则;

    2.2.1-5

    QoS为0的PUBLISH消息不能包含报文标识符字段;

    2.2.1-6

    PUBACK、PUBREC、PUBREL报文必须包含与相应的PUBLISH报文相同的报文标识符;

    2.2.1-7

    类似的,SUBACK和UNSUBACK消息必须包含与之对应的SUBSCRIBE和UNSUBSCRIBE报文相同的报文标识符;

    2.2.2 包含报文标识符的报文

    需要包含报文标识符的报文如下表所示:

    控制报文报文标识符字段
    CONNECT 不需要
    CONNACK 不需要
    PUBLISH QoS > 0需要
    PUBACK 需要
    PUBREC 需要
    PUBREL 需要
    PUBCOMP 需要
    SUBSCRIBE 需要
    SUBACK 需要
    UNSUBSCRIBE 需要
    UNSUBACK 需要
    PINGREQ 不需要
    PINGRESP 不需要
    DISCONNECT 不需要

    2.3 有效载荷Payload

    有效载荷Payload即报文的正文部分,包含Payload字段的报文类型主要如下表所示:

     

    控制报文有效载荷
    CONNECT 需要
    CONNACK 不需要
    PUBLISH 可选
    PUBACK 不需要
    PUBREC 不需要
    PUBREL 不需要
    PUBCOMP 不需要
    SUBSCRIBE 需要
    SUBACK 需要
    UNSUBSCRIBE 需要
    UNSUBACK 不需要
    PINGREQ 不需要
    PINGRESP 不需要
    DISCONNECT 不需要
  • 相关阅读:
    点击劫持漏洞之理解 python打造一个挖掘点击劫持漏洞的脚本
    URL重定向漏洞,python打造URL重定向漏洞检测脚本
    动态链接库(DLL)
    vs不支持通过afxgetmainwnd()获取窗口句柄(转)
    HALCON学习-下载、安装
    HALCON学习-资料
    MFC,ADO方式实现数据库操作
    VS2010 EXCEL2010 表格操作的编程实现
    Git Compare with base,比较大文件时,长时间等待,无法加载
    VS2010编译VS2008工程时,LINK : fatal error LNK1123: failure during conversion to COFF: file invalid or corrupt
  • 原文地址:https://www.cnblogs.com/schbook/p/7576919.html
Copyright © 2011-2022 走看看