MQTT-SN协议笔记


英文原版协议:https://www.oasis-open.org/committees/document.php?document_id=66091&wg_abbrev=mqtt

声明
本文档是对原版英文协议的大部分内容按自己的理解进行了汉语表述,并加入了部分与MQTT协议的对比。有些地方也没有完全理解,不正之处请指正。
本文也参考了http://www.cnblogs.com/yudar/ 的博客——MQTT-SN协议乱翻系列(但是绝对不是抄袭)。
对MQTT和MQTT-SN的对比及总结,可以参考大神yudar的博客:http://www.cnblogs.com/yudar/p/4642489.html,这个文章写得比较好。

一、简介

MQTT-SN:MQTT For sensor network,针对不支持tcp协议的、低速、低功耗的无线传感器网络的定制版MQTT协议。

针对无线传感器网络(WSN)与广域网之间的融合而产生的协议。
无线传感器网络的特点:

(1)网络节点(多为传感装置,简写为SA)量很大
(2)网络节点资源受限,包括存储资源和电量储备(电池驱动)
(3)网络带宽小,低至约1.2KB/s;底层协议帧(物理帧)长度小,如802.15.4物理帧长度上限为128个byte
(3)网络连接不连续(休眠唤醒),网络拓扑动态变化,节点上下较为频繁
(4)以数据为核心:节点产生的数据内容意义大于节点本身

为了将WSN与外界网络相融合,采取订阅发布类的协议是一个比较自然的选择,恰好MQTT协议是一个订阅发布协议,且专门用于M2M的嵌入式网络,所以针对WSN,对标准MQTT进行了一些特化和优化,使其专用于WSN内部以及WSN与外网之间的数据传输。

MQTT-SN最早是为zigbee协议设计的(基于IEEE 802.15.4,低速无线个域网LR-WPAN),可以用于所有支持点对点传输(直接通信或通过网关间接通信),并且支持广播的无线传感器网络。

二、网络架构

MQTT-SN 架构

mqtt-SN client:WSN的普通节点,客户端
mqtt-SN gateway:WSN的网关,实现MQTT-SN协议到MQTT协议之间的转换
mqtt-SN forwarder:转发器,针对client与gateway之间不能直接通信的情况。

网关有两种模型,如下:
在这里插入图片描述

(1)transparent GW: 网关为接入的每个client都会维护一个与broker之间的标准MQTT连接
(2)aggregating GW: 网关与broker之间只建立一个标准MQTT连接,自己维护到下属的每个client之间的对应关系

三、与MQTT的差别(概要,具体到报文格式及流程等细节,有很多的差异)

  1. CONNECT消息被拆分成三个消息(CONNECT,WILLTIPIC,WILLMSG),后两者用于客户端传递遗嘱主题和遗嘱消息等
  2. PUBLISH消息中的主题(topic name)被替换成两个字节的topic id,主题与topic id的对应关系需要在client到server/gateway的注册流程获取
  3. 支持预定义topic id和短主题(只有两个字节的主题)与topic name的对应关系,这样可以省去第二点所述的注册流程。该对应关系是要在client和server/gateway的固件中固化的
  4. 通过自发现机制(基于gateway的广播)来实现client与gateway之间的双向发现。多个网关之间可以协调为主从或负载均衡
  5. "clean session"即可作用于订阅持久化,也被扩展作用于遗嘱特性(遗嘱主题和遗嘱消息)
  6. 针对休眠设备增加离线保活机制支持,当有消息需要给休眠的client时,broker需要缓存,在client被唤醒时再发送
  7. 增加QoS-1的业务,client可以随时给固定的gateway发送消息,不需要建立连接

四、消息格式

mqtt-SN消息格式如下:

Message HeaderMessage Variable Part
(2 or 4 字节)(n 字节)

其中Message Header的格式如下:

LengthMsgType
(1 or 3 字节)(1 字节)

变长字段:

  1. client Id
    和MQTT一致,[1,23] byte
  2. Data
    对应publish消息中的消息体。
  3. Duration 广播时间间隔,即gateway发送的两次广播报文的时间间隔
    2字节,单位为s
  4. Flags
DUPQoSRetainWillCleanSessionTopicIdType
(bit 7)(6,5)(4)(3)(2)(1,0
DUP: 同mqtt。0:第一次发送;1:重传。仅用于publish
QoS: 同mqtt。
Retain: 同mqtt,仅用于publish
Will: 只和CONNECT报文相关。
CleanSession: 同mqtt,并且扩展到了遗嘱主题和遗嘱消息。只和CONNECT报文相关
TopicIdType: 0:使用协商的topic id;1:使用预定义的topic id;2:使用短主题
  1. GwAdd 网关地址
    变长,依赖于MQTT-SN所运行的无线传感器网络。比如,在zigbee中,该字段有2个字节。
  2. GwId
    1字节,网关ID,唯一标识WSN中的网关
  3. MsgId
    2字节,对应于MQTT中的消息ID
  4. ProtocolId 协议ID
    1字节。只在CONNECT报文中携带,对应于MQTT中的“protocol name”和“protocol version”字段,当前只能取0x01,其他值保留
  5. Radius
    1字节,广播路径跳数。0表示广播给WSN中的所有节点
  6. ReturnCode
    1字节。如下:
ReturnCode ValueMeaning
0x00
0x01
0x02
0x03
0x04-0xFF
Accepted
Rejected: congestion
Rejected: invalid topic ID
Rejected: not supported
reserved
  1. TopicId
    2字节。0x0000和0xFFFF保留
  2. TopicName
    变长,UTF-8编码?? 为什么还会有变长的topicName呢?
    5.3.
  3. WillMsg
    变长
    5.3.14
  4. WillTopic
    变长

报文

ADVERTISE 广播报文(gateway->client)

字段长度(byte)值及意义
Length10x5,长度
MsgType10x0
GwId1gateway标识
Duration2两次广播之间的时间间隔,单位为s

gateway发送,用于client发现gateway

SEARCHGW 广播报文(client->gateway)

字段长度(byte)值及意义
Length10x3,长度
MsgType10x1
Radius1报文的广播跳数(最大值)

client发送,用于client搜索gateway。广播的路径跳数依赖于WSN的节点部署密度,比如,如果WSN中的client之间都相互一跳可达,则radius可以设置为1。该值也被用于只是底层协议栈

GWINFO

字段长度(byte)值及意义
Length1长度
MsgType0x2
GwId1gateway标识
GwAdd变长仅当client发送该报文时才有本字段

其他报文

五、交互流程

client发现gateway

  1. gateway广播ADVERTISE
    网络中的激活状态的gateway要先与Broker建立MQTT连接,之后开始周期性发送ADVERTISE报文,周期为T_adv。为了避免链路拥塞,T_adv一般取值较大,建议在15分钟以上。
  2. client广播SEARCHGW
    由于T_adv较大,为了减小client的接入延时,MQTT-SN允许client主动发起查询处于激活状态的gateway,这是通过client发送广播报文SEARCHGW实现的。gateway和其他的client都可能收到SEARCHGW报文,对于gateway,其会回复广播报文GWINFO作为应答;对于client,如果自己维护的可用gateway列表不为空,则会选择一个激活状态的gateway,然后该gateway的信息组成GWINFO报文(注意该报文的GwAdd字段,此处体现出client和gateway的差异),通过广播发送出去。为了避免广播风暴,client在发送SEARCHGW时,需要随机退避一段时间(0~T_searchgw)。如果一个client在退避的时间内收到了其他client发送的SEARCHGW报文,则其会取消自己的SEARCHGW报文的发送,并执行与发送SEARCHGW报文的节点相同的逻辑
  3. client延迟发送GWINFO(也是广播)。
    为了使gateway可以优先发送GWINFO(相对于其他的client),client在发送GWINFO报文前,要随机退避一段时间(0~T_gwinfo),如果此期间收到了其他gateway发送的GWINFO,则client取消自己的GWINFO的发送
  4. SEARCHGW的二进制指数退避
    如果没有收到对SEARCHGW报文的应答,client可以重传SEARCHGW,重传间隔以二进制指数退避
  5. 广播跳数
    SEARCHGW报文与GWINFO报文具有相同的Radius,该值会传递给底层协议栈

client<->gateway间的连接建立

如果CONNECT报文中的will字段被置位,则连接建立流程如下图:

Client GW CONNECT(Flags, ProtocolId, KeepAlive, ClientId WILLTOPICREQ WILLTOPIC(Flags, WillTopic) WILLMSGREQ WILLMSG(WillMessage) CONNACK(Return Code) Client GW

如果CONNECT报文中的will字段没有被置位,则省去上图中的第2~5条消息,GW会直接回复CONNACK报文,来指示连接建立成功或者连接被拒绝。

cleansession, will

MQTT中的cleanSession标志可以清除原会话的状态,MQTT-SN将此特性扩展到了遗嘱消息,即遗嘱消息也是持久化的(MQTT的遗嘱不是持久化的么???)。

cleanSessionWill触发gateway的行为
truetrueGW会清除掉存储的所有client的订阅主题、消息,遗嘱主题、消息;并要求client发送新的遗嘱消息和主题
truefalseGW会清除掉存储的所有client的订阅主题、消息,遗嘱主题、消息;并直接放回CONNACK报文给client
falsefalseGW会保留之前存储的client订阅的主题、消息以及遗嘱主题、消息;并直接返回CONNACK报文给client
falsetrueGW会保留之前存储的client订阅的主题、消息以及遗嘱主题、消息;并要求client发送新的遗嘱消息和主题,新的遗嘱相关信息会覆盖GW之前存储的相关信息

更新遗嘱主题,消息

在连接建立后的任何时候,client都可以通过发送 WILLTOPICUPD消息和WILLMSGUPD消息更新遗嘱主题和相关。如果发送空的WILLTOPICUPD报文,会触发GW删除该client的遗嘱相关信息。

注册topic name和topic id的对应关系

为了减小传输资源负荷,MQTT-SN在publish消息中使用topic id取代MQTT中的topic name。
client在REGISTER报文中将要注册的topic name发送给GW,GW为其分配一个topic id并在REGACK报文中携带给client。

**注意:**client同一时间只能发送一条REGISTER报文,在收到ACK报文前不能发送下一条注册消息。

GW也可以主动发送REGISTER报文给client,这一般用于在client与GW的连接断开后重连,且cleansession没有被置位的情形。GW会在Publish给client之前,将之前分配给topic name的topic id告知给client。

Publish

MQTT-SN的发布流程与MQTT完全一致,出了一下两点差别:
(1)使用topic id代替topic name
(2)必须是同步流程,即对于QoS1和QoS2的报文,如果没有收到全部的ACK或超时前,不能publish下一条报文

注意:
(1)client在publish之前,如果不使用预定义的topic id,则需要先进行上述的注册流程
(2)GW在publish个client之前,如果没有给该client分配过topic id,则GW也需要通过RESIGTER流程向client注册一个topic id(GW分配的)

subscribe

client向GW发起订阅流程,如果订阅报文中的主题含有通配符,则GW会先回复subscribeACK报文,然后在有对应主题的报文要publish个该client时,GW会通过REGISTER流程为client分配一个对应该topic name的id,并将该对应关系告知client

预定义topic id和short topic name

如果使用client和GW约定好的(固化在代码中)id和name 的对应关系,可以省去前述的REGISTER流程,直接使用topic id进行publish。订阅中也可以通过topic id进行订阅。
对于short topic name,固定2个字节,也可以用来作为publish中的主题,但是不能作为subscribe中的主题(2个字节不可能构成一个有意义的主题)

QoS=-1的publish

该特性是针对极其简单的client实现而设计的,这种client不需要实现MQTT-SN的任何特性(连接建立、注册、订阅等),也不需要关心GW是否存在,是否正常工作等。支持QoS -1的client,其会按照已经固化到代码中的网关地址直接发送PUBLISH消息,不关心网关地址是否正确、网关是否存活、消息是否发送成功。

对于QoS-1的PUBLISH报文,只支持如下参数:

QoS标志,被置为0b11
TopicIdType标志,可能是(预定义topic id)0b01也可能是(短topic name)0b10
TopicId字段,预定义topic id或短topic name
Data字段,需要发送的数据

心跳保活

同MQTT一样。
如果client多次重发PINGREQ报文后都没有收到gateway的PINGRSP报文,则client会选择尝试连接其他的gateway,而不是尝试重连之前连接的gateway。
注意,由于各个client上的心跳定时器并不同步,所以当gateway故障时,所有之前连接该gateway的client发送的CONNECT报文并不会导致网络传输风暴。

DISCONNECT

client可以主动发起断连报文。
如果在CONNECT时,里面的cleansession被置位,则MQTT-SN连接断开后,gateway不会清除该client的订阅和遗嘱相关信息。client可以通过取消订阅、更新遗嘱、以及在新connection建立时通过置位cleansession标志来删除订阅和遗嘱相关信息

gateway也会主动发起DISCONNECT报文。当gateway发生错误,或者gateway无法定义publish消息的归属时(什么意思呢?????),gateway会主动发送DISCONNECT报文给client(给哪个client呢?)

重传

除gateway和client双向发现外用的三种消息(ADVERTISE、SEARCHGW和GWINFO)外,其他的报文都是单播报文。MQTT-SN支持client的重传:如果发送的消息没有收到GW的回复,则client会在重传定时器Tretry超时后重传。如果重传次数超过最大重传次数Nretry.,则认为当前网关异常,尝试连接其他的网关,如果连接其他的gateway也失败,则重新尝试重连之前连接的网关。

client的休眠唤醒及状态转移图

MQTT-SN支持client的休眠唤醒。对于依赖于电池供电的传感器设备,低功耗具有重要意义。具有休眠唤醒的client可以进入休眠状态,在有数据到达或有数据需要发送的时候唤醒。gateway/broker需要了解具有休眠功能的client的状态,并对其相关的数据进行缓存。

从gateway/broker的视角来看,具有休眠唤醒功能的client具有如下五个状态:

  • active 激活
  • asleep 休眠
  • awake 唤醒
  • disconected 断连
  • lost 掉线
    client同一时间处于且只能处于其中的一种状态。

gateway/broker的视角来看,client的状态转移图如下:

在这里插入图片描述

  • 处于disconnected状态的client,通过CONNECT报文,会变为active状态。之后通过keepalive保活。
  • 如果超时时间内没有收到client的任何消息,则认为client从active状态变为lost状态,并触发该client相关的遗嘱流程
  • gateway/broker如果收到了client发送的DISCONNECT报文,且该报文中没有携带duration字段,则认为client由active进入disconnected状态。gateway/broker不会持续监视client的disconnected状态
  • 如果client发送的DISCONNECT报文中携带了duration字段,则认为该client由active状态进入asleep状态,休眠时间为duration字段。gateway/broker会持续监视(通过定时器)处于asleep状态的client
  • 对于进入asleep状态的client,如果gateway/broker在超过休眠时间值的时间内都没有再收到该client的数据,则认为client进入了lost状态。同keepalive机制一样,从asleep进入lost状态的client也会触发遗嘱相关流程。
  • 对于处于asleep状态的client,gateway会缓存所有要发送给该client的消息
  • 如果gateway/broker收到了(在client的休眠时间定时器超时之前)处于asleep状态的client发送的PINGREQ(该PINGREQ报文会包含client的client id)报文,则认为该client从asleep进入了awake状态,同时会关闭为该client维护的休眠定时器。
  • gateway会对进入awake状态的client做如下处理:(1)如果在client休眠期间,gateway缓存了要发送给该client的消息,则会将这些消息发送给该client,并在发送完所有的消息后,发送PINGRSP报文给client。(2)如果在client休眠期间内没有缓存目的为该client的报文,则gateway会直接发送PINGRSP报文给client
  • 不论上述那种情形,只要gateway给处于awake状态的client发送的PINGRSP报文,即认为client又进入了asleep状态,gateway会重启针对该client的休眠定时器。
  • 处于asleep状态和awake状态的client,可以通过发送CONNECT报文进入active状态,或者通过发送不带duration字段的DSICONNECT报文进入disconnected状态;client还可以通过发送携带有duration字段的DISCONNECT报文来修改休眠的时间

注意:对于具有休眠唤醒功能的client,其由asleep进入awake状态的目的应该只是为了查看gateway是否有缓存的目的为该client的消息,并在收到了这些缓存消息后(也可能没有任何缓存消息)尽快进入asleep状态,在awake状态期间,不应该发送PINGREQ以外的报文(想进入active或disconnected状态导致发送的CONNECT报文、DISCONNECT报文除外)。如果client想publish或发送其他的报文,则应该通过发送CONNECT报文进入active状态

休眠状态下的重传机制

处于asleep状态的client,当休眠时间超时后,会发送PINGREQ报文给gateway,该报文有两个目的:

  • 在休眠状态维护与gateway之间的连接
  • 查看是否有发送给该client的消息被gateway缓存,如果有,则触发gateway发送所有的缓存消息给client,发送完成后,client会重新进入asleep状态(在收到PINGRSP报文后)

发送PINGREQ报文后的client会启动Tretry定时器,在收到除PINGRSP报文外的其他消息(应该之后publish报文)时会重启Tretry定时器,在收到PINGRSP报文时关闭Tretry定时器,并重新进入asleep状态。如果在Tretry超时前没有收到PINGRSP报文(不论期间是否收到了gateway发送的缓存的publish报文),则client会重传PINGREQ报文,重传次数不能超过最大重传次数。如果超过最大重传次数都没有收到PINGRSP报文,则client会重新进入asleep状态。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值