q里单做一些小l和ҎQ针对前面的协议译部分Q一阶段的学习完l?/p>
MQTT-SNZMQTT原有语义Q但做了很多的调整。比如: 一个CONNECT消息被拆分ؓ3个消?主题名称需要用主题标识符替代 * |关地址可以q播、查询得?/p>
MQTT-SN ?MQTTҎQ用一张图介绍
比较cd | MQTT | MQTT-SN |
---|---|---|
传输cd | 可靠点对Ҏ模式 | 不可靠的数据?/td> |
通信方式 | TCP/IP | Non-IP ?UDP |
|络传输 | Ethernet, WiFi, 3G | ZigBeeQBluetoothQRF |
最消?/td> | 2个字?/td> | 2个字?/td> |
最大消?/td> | ≤24MB | < 128个字?/td> |
甉|供电 | X | √ |
休眠支持 | X | √ |
QoS -1(哑客L) | X | √ |
主题标识W?/td> | X | √ |
|关自动发现和回退 | X | √ |
有关QoSQMQTT-SN新增增加了哑客户端(QoS Q?1Q支持:
QoS Level | 消息传输ơ数 | 传输语义 | 传输保证 |
---|---|---|---|
-1 | ≤ 1 | 臛_一?/td> | 无连接,只用于传输,力而ؓQ无保证Q只有MQTT-SN支持 |
0 | ≤ 1 | 臛_一?/td> | 力而ؓQ无保证 |
1 | ≥ 1 | 臛_一?/td> | 保证送达Q可能存在重?/td> |
-1 | ≡ 1 | 只有一?/td> | 保证送达Qƈ且不存在重复 |
和MQTT 3.1协议cMQ在上一ơ的客户端成功连接时在CONNECT中设|了清理会话标志clean session为falseQ遗q性Will也ؓtrueQ再ơ连接时Q那么服务器为其~存订阅数据和遗嘱数据是否已l被删除Q对客户端不透明Q因为就是服务器因内存压力{清理了~存Q但没有通知到客LQ会造成订阅、遗q误解?/p>
q好QMQTT-SN协议中,|关在清理掉遗嘱数据后,可以咨询客户端,或主动通知客户端断开Q重新徏立会话流E?/p>
在MQTT 3.1.1协议中,服务器会在CONNACK中标C话是否已l被持久的标记?/p>
切来说QMQTT-SN协议存在三种格式主题名称Qtopic nameQ,可由消息标志位包含TopicIdType属性决定:
所有主题被替换成标识符Q在发布PUBLISH消息Ӟ直接使用被指定的主题标识Wtopic id、short topic name卛_?/p>
下面对MQTT-SN常用程q行的流E简单梳理:
Client Gateway Server/Broker
| | |
Generic Process | --- SERCHGW ----> | |
| <-- GWINFO ----- | |
| --- CONNECT ----> | |
| <--WILLTOPICREQ-- | |
| --- WILLTOPIC --> | |
| <-- WILLMSGREQ -- | |
| --- WILLMSG ----> | ---- CONNECT ----> |(accepted)
| <-- CONNACK ----- | <--- CONNACK ----- |
| --- PUBLISH ----> | |
| <-- PUBACK ----- | (invalid TopicId) |
| --- REGISTER ---> | |
| <-- REGACK ----- | |
| --- PUBLISH ----> | ---- PUBLISH ----> |(accepted)
| <-- PUBACK ----- | <---- PUBACK ----- |
| | |
// // //
| | |
SUBSCRIBE -->| --- SUBSCRIBE --> | ---- SUBSCRIBE --> |
[var Callback] | <-- SUBACK ------ | <--- SUBACK ------ |
| | |
// // //
| | |
| <-- REGISTER ---- | <--- PUBLISH ----- |<-- PUBLISH
| --- REGACK ----> | |
[exec Callback] | <-- PUBLISH ---- | |
| --- PUBACK ---> | ---- PUBACK ----> |--> PUBACK
| | |
// // //
| | |
active -> asleep| --- DISCONNECT -> | (with duration) |
| <-- DISCONNECT -- | (without duration) |
| | |
// // //
| | |
| | <--- PUBLISH ----- |<-- PUBLISH
| | ----- PUBACK ----> |
| | <--- PUBLISH ----- |<-- PUBLISH
| | ----- PUBACK ----> |
| | (buffered messages)|
asleep -> awake | | |
| --- PINGREQ ----> | |
awake state | <-- PUBLISH ---- | |
| <-- PUBLISH ---- | |
| <-- PINGRESP ---- | |
asleep <-awake | | |
MQTT-SN可以q行在不同的无线协议上,只要可以满MQTT-SN 所定义卛_Q支持双向数据传输和|关卛_QMQTT-SN完全可以q行在其上面?/p>
MQTT-SN可以在ZigBee、Bluetooth、RF、UDP?loWPAN{底层协议层面运行?/p>
下面是来自网友的ZMQTT-SNq行的架构图Q?/p>
但实际上的其|络拓扑可能更ؓ复杂Q比如两个不同的传感器网l:
传感器和制动器,合称为SA。传感器汇报状态数|自n发布PUBLISH消息Q,制动器会被某参数D发(接收到的PUBLISH消息Q。好比,文g的输?输出模式Q传感器用于文g的读取,制动器用于文件写入。或者用管道阀门,某指标超q阀D发制动器报警QSA一起作用便于更好追t数据。大部分旉QPUBLISH消息被用于触发制动器Q这建立在后端服务器的分析结果基上?/p>
MQTT-SN比较知名的实玎ͼ比如(http://eclipse.org/proposals/technology.mosquitto/)[Eclipse Mosquitto]Q?RMSB)[http://git.eclipse.org/c/mosquitto/org.eclipse.mosquitto.rsmb.git/]{,但不是实现所有已定义l节Q比如MQTT-SN协议转发部分(MQTT-SN Forwarder)Q就鲜有实现Q但实现不难嘛,可能~Z相应的场景支持吧?/p>
MQTT-SN支持cM于传感器的网养IE强的网l可与适用于MQTT协议Q这L来,MQTT要做到连接一切(Connect anythingQ,如IBM所发布的红皮书所_要用MQTT打造一个智能星球,有戏Q?/p>
本篇是MQTT-SN 1.2协议最后一翻译了Q主要涉及实现要点,很简短?/p>
QoS虽默认设|有0,1,2三个|但还有一U情况其gؓ-1。来自客L的PUBLISH消息中若QoS?1的情况下Q此dL不会兛_和网x没有建立q接Q也不在乎时间点Q有消息需要发出去。透明的网关需要维护此cL息ƈ与远E的MQTT Server建立一个专用TCPq接。聚合网xhybirdh|关可用已有的MQTT Serverq接转发此类消息?/p>
定时?计数?/th> | 说明 | 推荐?/th> |
---|---|---|
T_ADV | q播频率 | 大于15分钟 |
N_ADV | 没有接收到ADVERSEq播ơ数 | 2-3?/td> |
T_SEARCHGW | 发送SEARCHGW延迟 | 5U?/td> |
T_GWINFO | {待|关响应GWINFOq播延迟旉 | 5U?/td> |
T_WAIT | {待旉 | 大于5分钟 |
T_RETRY | 重试旉 | 10s - 15s |
N_RETRY | 重试ơ数 | 3-5?/td> |
|关处理客户端的休眠和存zd时器Q需要根据客L在所发送消息中延箋旉的定义倹{例如,定时器值应该高?0%大于指定值持l时?分钟Q如果不高出50%?/p>
协议严重所有客L的Topic Id和Topic Name之间对应关系不应该用一个共享池对象Q因样可以避免不同客LTopic Id和Topic Name匚w错误Q将PUBLISH消息发错地方Q客L接收者)Q可能会D引发潜在的不可恢复的N性后果?/p>
正确做法是按照客L的维度ؓl护Topic Id和Topic Name的对应关pRQ何两个客L之间可能会存在同LTopic NameQ但对应的Topic Id不一栗可能Topic Id一_但Topic Name不一栗?/p>
MQTT-SN 1.2协议到此译Q非直译Q完毕,嗯,有种惌吐血的感觉,但也是坚持了下来 (^_^)?/p>
我的生一片无悔,我想起那天夕阳下的奔跑,那是我逝去的青春?br />---《万万没惛_》王大锤
紧接上文Q这是第三篇Q主要是对MQTT-SN 1.2协议q行M性功能描q?/p>
嗯,q一部分可以l合着MQTT协议Ҏ着来看?/p>
|关只能在成功连接到MQTT Server之后Q才能够周期性的在无U个人区域网WPNs内对所有客Lq播ADVERTISE消息Q便于客L被动知道|关的存在?/p>
在同一|络下,多个拥有不同Id的网兛_有同时运行中Q但会由客户端根据信号强弱决定连接具体网养I无论何时只能q接一个网兟?/p>
客户端可l护一份可用网兛_表(包含|关地址Q,在接收到包含有新的网关id的ADVERTISE和GWINFO消息后,其列表需要添加新的网兛_素进厅R?/p>
ADVERTISEq播消息包含的下一ơ广播间隔时长Duration属性,单位U,设ؓ变量T_ADVQ应该尽可能大与15分钟Q?00U)Q频率降低是Z避免低速个人区域网l的拥塞?/p>
针对接收ADVERTISE消息频率Q处理能力较强客L可以用于监督|关是否可用。egQ客Lq箋N_ADVơ接收不到某个网关ADVERTISEq播消息Q可认ؓ此网关经L不可用ƈ且从已维护的|关列表中移除。同LQ作为备用的|关认ؓȝ兛_挂掉Q此时可处于Ȁzȝ态,正常发挥作用?/p>
|关发送广播消息ADVERTISE的时间间隔很长,q对D新加入的客户端不利,但客L可以直接发送SEARCHGWq播消息q行查询|关。大量的新入讑֤会造成q播风暴造成|络拥挤Q每一个新加入的客L在发送SEARCHGWq播消息之前都需要获取一个随机的延迟发送?0-Tsearchgw)Q在延迟{待发送期间若接收到其它客L发送的SEARCHGWq播消息Q会取消掉自qSEARCHGWq播消息发送,{待|关GWINFO消息通知?/p>
SEARCHGW消息属性radiusq播半径Q记为变量RbQ?跻I1 hopQ在一般密集部|下的MQTT-SN客户端基本可用?/p>
|关接收到SEARCHGW会即d复包含自wid的GWINFO消息。客L收到SEARCHGW后,若有需要gq发送的SEARCHGW会取消掉Q若自nl护一份多个可用网兛_表,在等待T_GWINFO旉内没有收到GWINFO消息Q会从列表中取出一条网关信息组装成GWINFO消息q广播出厅R这p求客L已运行多Ӟq且l护多个可用|关列表?/p>
GWINFO和SEARCHGW所包含半径radius属性g_q就要求底层|络在传输时q行军_是否需要传输到其它cd|络中?/p>
若没有接收到响应QSEARCHGW消息可能被重C输。两个连l的SEARCHGW消息重传间隔应该呈指数Ş式增加,避免太密集传输?/p>
无论是基于哪一U传输协议,TCP or UDPQ客L都需要徏立连接,q且保持心蟩Q逻辑上和服务器端保持一条不断线的双向通道。下面一张图Q演CZ客户端徏立连接的q程Qƈ且设定客L在CONNECT消息中标志位字段中遗嘱WILL属性ؓtrueQ然后就有了遗嘱主题/消息的请求过E?/p>
很多情况下,q接CONNECT是不需要遗嘱支持的Q网关会直接q回CONNACK消息Q但|关会因为拥塞或不支持一些CONNETҎ,CONNACK所包含q回代码字段ReturnCode中包含拒l代码,要求客户端检查是否连接成功,区别对待。比如:
CONNACK消息q回状态码?x01QRejected: congestionQ因拥塞被拒l)Q客L需要在T_WAIT旉间隔后进行重试?/span>
已经q接的客L断线后,若之前在CONNECT中没有设|过会话清理QClean SessionQ标识,那么之前的订阅等信息在网兛_会持久存在。相比MQTTQMQTT-SN中的“Clean Session”标识被扩展到遗嘱Ҏ中。在CONNECT消息中,CleanSession和Willl合会产生以下效果Q?/p>
受限于无U传感器|络的有限带宽和微小消息负蝲QPUBLISH消息中不能够包含完整的主题名Utopic name。这需要客L和网关之间通过注册程Q获取主题名U对应的Q?6位的自然敎ͼtopic idQ然后塞入PUBLISH消息的topicId属性中?/p>
客户端发送REGISTER消息Q网兌回REGACK消息Q其所包含的ReturenCode属性决定注册成功与否:
L旉Q只能执行一个REGISTER消息Q有没有完成注册程Q需要等待?/p>
|关->客户端方向,|关发送REGISTER消息l通知客户端指定topicId对应某个主题Q以便后面发送PUBLISH消息使用。若客户端在订阅SUBSCRIBE消息时用了通配W(#/+Q,那么与之相匹配的topic name也将被一一通知到。因此不使用通配W,较ؓ低效?/p>
客户端一旦获取到topic name对应topic idQ就可以直接发送PUBLISH消息了。这和MQTT协议相比QPUBLISH消息中Topic Name被替换成Topic IdQ除此之外,q要注意ReturnCodeQ?/p>
QoS 1?QoS 2在Q一旉Q都必须{待已有PUBLISH消息完成Q才能进行下面的PUBLISH消息发布程?/p>
预定义的topic id已提前指z֥对应的topic nameQ需要客L和网兛_代码层支持Q省略了中间注册程Q在q接建立之后可以马上q行PUBLISH消息Q但q需要在PUBLISH标志Flags字段中设|TopicIdTypegؓ0b01Q?b10表示两个字节长度的短topic nameQ。虽然可以快速发送PUBLISH消息Q但客户端想订阅预定义的topic idQ接收对应的PUBLISH消息Q一样需要发送SUBSCRIBLE消息hq行订阅。若乱指定预定义topic idQ会收到ReturnCode=“Rejection: invalid topic Id”的异常?/p>
预定义的短topic name只有两个字符长度的字W串Q也是两个字节)Qtopic idZ个字节表C的一个自然数Q?-65535Q,两者用场景一_都需要在标志位Flags讄TopicIdType具体|0b01表示预定义topic idQ?b10表示两个字节长度的短topic nameQ需要分清?/p>
q对仅仅支持PUBLISH QoS -1的非常简单的客户端实现而言Q除此之外不支持MҎ。它不关心连接是否徏立,也没有注册、订阅这一_按照已经固化C码中的网兛_址直接发送PUBLISH消息Q不兛_|关地址是否正确、网x否存zR消息是否发送成功?/p>
下面的PUBLISH属性g赖于QoS -1的情况:
客户端对某个主题感兴,可以发vSUBSCRIBLE程Q携带上感兴的主题名(topic idQ,服务器一般会q回包含有指定主题IdQtopic idQ的SUBACK消息。订阅失败,可以从PUBACK的ReturnCode中获知:
有一U情冉|SUBSCRIBLE订阅主题包含通配W,|关的处理就很简单,在SUBACK中返回的topic id?x0000。稍后,|关向客L发送REGISTER消息走注册流E,通知通配W匹配到的主题对应的topic id倹{?/p>
来自客户端的SUBSCRIBLE消息一h持预定义topic idQ以及短topic nameQ这和PUBLISH消息差不多?/p>
退订就很简单,客户端发送UNSUBSCRIBLE消息Q网兌回UNSUBACK消息?/p>
但同一时刻Q客L只允许处理订阅SUBSCRIBLE或取消订阅UNSUBSCRIBLE按照串行化顺序,下一个操作依赖于上一个操作完全成功?/p>
服务器发布流E和客户端类|在发布之前需要检其主题是否已经向客L提前注册q,若无需要把主题和指定的topic id攑օREGISTER消息中发送给客户端进行注册流E,然后{待客户端处理结果REGACK。注册通过Q然后才能正常发送PUBLISH消息?/p>
|关需要确保REGISTER的主题以及PUBLISH消息的内容负载都不能太长过当前|络负蝲上限Q比如在ZigBee环境下不能超q?0个字节)Q取消注?发布程好了?/p>
|关发布PUBLISH消息Ӟ客户端检到未知的topic idQ把拒绝理由装到PUBACK后,|关遇到ReturnCode=“Rejected: invalid Topic ID”非法topic idQ需要考虑删除或重新注册?/p>
客户端或怼拒绝其注册,或许会不允许PUBLISH消息Q网兛_上静默处理就好了Q失败就p|了,不需要告知别人?/p>
客户端发布流E于此类|需要在发布之前q行主题注册以获取指定的topic idQ提交PUBLISH消息后,同样需要检查PUBACK所包含的ReturnCode字段是接受还是拒l,因网l拥塞而生的拒绝Q客L需要在T_WAIT旉后再ơ重试?/p>
客户端的发布必须是串行方式,下一个需要发送到PUBLISH消息需要等待上一个发送成功被|关接受之后才能q行处理?/p>
一般是客户?>|关Q网?>客户端也没有问题。但要求PINGREQ -> PINGRESP 一定要单个旉循环QPINGREQ发送者不能也是PINGRESP的发送者,那样不但׃程Q也费了网l资源。嗯Q不允许双向互发?/p>
客户端可Z心蟩机制监测已连接网兛_康与否,q箋多次接收不到来自|关的PINGRESP消息后,客户端连接下一个可替换的网兟뀂因为客L的连接和心蟩和其它客L状态属性不同步Q但q可能会带来一个问题,同一旉若有大量的客Lz水般同时连接一个网养I|关可能毫无征兆的会被冲垮掉。这p求网兌有批量的q接处理能力Qƈ发特性增强才行?/p>
客户端主动发送DISCONNECT消息告知|关需要断U之后,若有交换信息的需要可以重新发起一个新的会话连接。DISCONNECT消息之后Q网关不会清理掉已有订阅和遗嘱数据,除非在之前的CONNECT消息中已性设|了CleanSession会话清理标识为true。网x收到DISCONNECT消息之后会返回一个DISCONNECT消息作ؓ响应?/p>
有一U情冉|客户端会H然接收到来自网关的DISCONNECT消息Q这也许是网兌w发生了异常错误Q或|关无法定位客户端的消息归属Q客L的消息和客户端无法关联到一PQ此时客L需要发送CONNECT消息重徏与网关的会话q接?/p>
客户?>|关的消息都是单路传播的Q这依赖于客L所持有的已q接|关的单播地址?/p>
客户端发送一个消息之后,需要启动一个重试定时器Tretry和一个重试计数器Nretry用以监督|关消息响应。定时器会被客户端在指定旉内接收到来自|关的消息后取消掉,若没有准时接收到则会触发定时器执行消息重发流E,q箋Nretryơ重发后Q客L会直接取消掉当前程Q判断当前网兛_l断U,需要连接到另外一个可用的|关。假如另外的|关也是q接p|Q会试重连之前的网兟?/p>
若在休眠状态下Q一旦超q重试计数器|客户端直接进入休眠状态?/p>
q里所说的客户端指的是依赖甉|驱动的电子设备,你要明白一个事实,节省甉|资源是多麽的重要Q省电就是关键,没电了就没得玩了嘛。当不处于激zȝ态时Z省电得需要进入睡?休眠状态,当有数据需要接收或发送时可以醒q来。网兛_需要追t设备的休眠状态ƈ且支持缓存需要发送给休眠讑֤的消息,在设备唤醒时一一发送?/p>
下面是客L的状态{换图Q很清晰描述了各U状态之间的交互Q?
客户端具有五U状态:Ȁz(activeQ,休眠QasleepQ,唤醒QawakeQ,断线QdisconnectedQ,丢失QlostQ,每次只能是其中一U?/p>
|关需要监督客L的状态,开始于CONNECT消息中存zL长字D(keep aliveQ,在大于存zL长时间内|关接收不到来自客户端消息,|关认ؓ客户端已l处于丢q态(lostQ,会激zd应的遗嘱Ҏ若存在的话?/p>
客户端发送DISCONNECT消息但没有duration休眠旉字段Q网兌处于没有时间监督的断线状态。一旦包含duration休眠旉字段Q表C客L需要休眠一D|_|关q客L被{换ؓ休眠状态,休眠旉为duration所定义在倹{超q此休眠旉的一D|间内Q网兌接收不到客户端发送过来的M消息Q那么客L会被转化Zq态,若已讄遗嘱Ҏ,此时遗嘱Ҏ会生效。客L休眠期间需要被发送的消息都会被网关缓存?/p>
睡眠状态下程图会更Ş象的说明程Q?
毫无疑问Q网兛_使用一个休眠定时器l护客户端的休眠状态等Q休眠定时器会被停掉当网x收到客户端发送过的PINGREQ消息Q网关从PINGREQ消息所包含的Client Id索是否存在已~存的PUBLISH消息Q若有会一一按照序发送到客户端。所有对应已~存消息发送完毕后Q会随之发送一个PINGRESP消息。若没有~存消息Q网关直接返回一个PINGRESP消息。网关会重新启动休眠定时器,|关l护的客L状态被转换Z眠状态,客户端在接收到PINGRESP消息之后Q将直接转向休眠状态,节省用电?/p>
客户端在唤醒状态下处理消息Q遵?#8220;客户端重传流E?#8221;行ؓQ一旦达到重试计数器限制Q将q入睡眠状态?/p>
客户端从休眠状态{向唤醒状态用于检查网x否ؓ其缓存消息时Q需要发送一个PINGREQ消息到网养I从休?唤醒状态{换ؓȀzȝ态,需要发送一个CONNECT消息告知|关Q{换ؓ断线状态时需要发送两个字节的DISCONNECTQ没有休眠时长字DdurationQ消息;需要重新定义的休眠旉Q发送一个DISCONNECT消息Q包含新的duration旉|通知|关卛_?/p>
功能性描qCl完了,基本上MQTT-SN协议介绍已接q尾壎ͼ最后面的篇章就是短短的实现描述了?/p>
紧接着上篇初步介绍Q本文ؓW二,主要梳理MQTT-SN 1.2协议中定义的消息格式?/p>
消息?/th> | 其它可变部分 |
---|---|
2/4字节表示 | N字节l成 |
长度 | 消息cd |
---|---|
1?个字?/td> | 1个字?/td> |
备注Q?/p>
MQTT-SN不支持消息的分片和重l。底层网l所定义数据包长度若于MQTT-SN消息的最大长度,自nq行的分片和重组Q对MQTT-SN协议本n不受影响?
MQTT-SN定义的消息类型数量众多,过25个,感觉有些头大?/p>
消息cd?/th> | 消息cd名称 | 说明 |
---|---|---|
0x00 | ADVERTISE | q播消息 |
0x01 | SEARCHGW | L|关 |
0x02 | GWINFO | |关信息 |
0x03 | reserved | 没有使用?/td> |
0x04 | CONNECT | 发vq接 |
0x05 | CONNACK | q接认 |
0x06 | WILLTOPICREQ | 遗嘱主题h |
0x07 | WILLTOPIC | 遗嘱主题认 |
0x08 | WILLMSGREQ | 遗嘱消息h |
0x09 | WILLMSG | 遗嘱消息认 |
0x0A | REGISTER | 注册主题 |
0x0B | REGACK | 注册认 |
0x0C | PUBLISH | 发布消息 |
0x0D | PUBACK | 发布认 |
0x0E | PUBCOMP | 发布环节消息 |
0x0F | PUBREC | 发布环节消息 |
0x10 | PUBREL | 发布环节消息 |
0x11 | reserved | 保留字段 |
0x12 | SUBSCRIBE | 订阅主题 |
0x13 | SUBACK | 订阅认 |
0x14 | UNSUBSCRIBE | 退?/td> |
0x15 | UNSUBACK | 退订确?/td> |
0x16 | PINGREQ | Pingh |
0x17 | PINGRESP | Ping响应 |
0x18 | DISCONNECT | 断开 |
0x19 | reserved | 保留字段 |
0x1A | WILLTOPICUPD | 遗嘱主题更新 |
0x1B | WILLTOPICRESP | 遗嘱主题更新认 |
0x1C | WILLMSGUPD | 遗嘱消息更新 |
0x1D | WILLMSGRESP | 遗嘱消息更新认 |
0x1E-0xFD | reserved | 保留字段 |
0xFE | 转发装标志 | 用于转发 |
可变字段很多Q与MQTT相比Q多了:
q回?/th> | q回值含?/th> |
---|---|
0x00 | 接受h(Accepted) |
0x01 | 因拥塞拒l?Rejected: congestion)Q一般需要接收方{待T_WAIT旉?/td> |
0x02 | 因非法主题标识符拒绝(Rejected: invalid topic ID) |
0x03 | 因不支持拒绝(Rejected: not supported) |
0x04 - 0xFF | 保留Q没有用到 |
|关周期性会对当前网l下所有客L、节点进行广播,用于客户端发现可用网兟?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 0x05 |
1 | MsgType | 0x00 |
2 | GwId | |关需要吧自n标识W包含其?/td> |
3-4 | Duration | |关的下ơ广播间隔时长,单位U?/td> |
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 0x03 |
1 | MsgType | 0x01 |
2 | Radius | q播半径深度Q同时也是只是给当前|络传输?/td> |
客户端主动寻扄兌行广播的消息Q广播\径范围受限于当前|络环境下的客户端部|密度,比如只有1跛_播在非常密集的网l环境下客户端都可以彼此互相讉K?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 动态确?/td> |
1 | MsgType | 0x02 |
2 | GwId | |关Id |
3-n | GwAdd* | 一个网兛_址Q仅仅由客户端发出消息时Q此字段才存?/td> |
GWINFO作ؓ对SEARCHGW消息的响应:
客户端向|关发出建立q接的消息?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 动态计?/td> |
1 | MsgType | 0x04 |
2 | Flags | 标志?/td> |
3 | ProtocolId | 0x01Q表C协议版本和协议名称 |
4-5 | Duration | 存活持箋旉 |
6-n | ClientId | 客户端标识符Q?-23个字节表C的字符?/td> |
在CONNECT消息标志位具体表C如下:
DUP | QoS | Retain | Will | CleanSession | TopicIdType |
---|---|---|---|---|---|
bit 7 | 6,5 | 4 | 3 | 2 | 1,0 |
X | X | X | 0/1 | 0/1 | X |
在Flags中用到的标志位Q?/p>
|关对客L发出CONNECT消息的响应?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 0x03 |
1 | MsgType | 0x05 |
2 | ReturnCode | 接受?x00Q拒lؓ0x01-0x03Q具体见上文RecodeCode定义 |
Ҏ客户端CONNECT标志位中WILL字段为true情况下,|关向客L发出遗嘱主题hQ格式如下:
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 0x02 |
1 | MsgType | 0x06 |
只有头部部分Q很单?/p>
客户端作为网关WILLTOPICREQh响应消息。下面是一个正常版本的WILLTOPIC消息Q?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 动态计?/td> |
1 | MsgType | 0x07 |
2 | Flags | 标志?/td> |
3-n | WillTopic | 遗嘱主题 |
此时的标志位如下
DUP | QoS | Retain | Will | CleanSession | TopicIdType |
---|---|---|---|---|---|
bit 7 | 6,5 | 4 | 3 | 2 | 1,0 |
X | 0x00-0x02 | 0/1 | X | X | X |
而空的WILLTOPIC也是允许存在的,׃个字节表C,用于客户端请求删除已存在于服务器端的对应遗嘱主题和消息?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 0x02 |
1 | MsgType | 0x07 |
Ҏ客户端CONNECT标志位中WILL字段为真情况下,|关向客L发出遗嘱消息hQ格式如下:
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 0x02 |
1 | MsgType | 0x08 |
只有头部部分Q没有别的?/p>
客户端对|关WILLMSGREQh的响应,从而把遗嘱消息传递给|关q行保存?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 动态计?/td> |
1 | MsgType | 0x09 |
2-n | WillMsg | 客户端遗?/td> |
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 动态计?/td> |
1 | MsgType | 0x0A |
2-3 | TopicId | 客户端发出,此gؓ0x0000Q服务器发出Q需要包含对应于Topic Name的主题标识符 |
4-5 | MsgId | 自然敎ͼ用以标识对应的REGACK认 |
6-n | TopicName | 主题名称Q不能太长,量不要使用通配W?/td> |
客户端或|关针对REGISTER消息的响应?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 0x07 |
1 | MsgType | 0x0B |
2-3 | TopicId | 对应于Topic Name的主题标识符Q被用于PUBLISH消息发布 |
4-5 | MsgId | 自然敎ͼ用以标识对应的REGISTER消息 |
6 | ReturnCode | 0x00被接受,其它D拒绝 |
PUBLISH消息用于客户端或|关发布消息用:
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 动态计?/td> |
1 | MsgType | 0x0C |
2 | Flags | 标志?/td> |
3-4 | TopicId | 主题标识W?/td> |
5-6 | MsgId | QoS 1-2旉要填充自然|QoS 0Ӟgؓ0x0000 |
7-n | Data | 用于发布的具体消息内?/td> |
标识位具体如下:
DUP | QoS | Retain | Will | CleanSession | TopicIdType |
---|---|---|---|---|---|
bit 7 | 6,5 | 4 | 3 | 2 | 1,0 |
0/1 | 0x00-0x02 | 0/1 | X | X | 0b00/0b01/0b10 |
标识位里面各个字D和MQTT协议一_无须多解释?/p>
客户?|关仅仅对QoS 1/2的PUBLISH消息做出响应?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 0x07 |
1 | MsgType | 0x0D |
2-3 | TopicId | 对应PUBLISH消息中TopicId |
4-5 | MsgId | 自然敎ͼ用以标识对应的REGISTER消息 |
6 | ReturnCode | 0x00被接受,其它D拒绝Q不同DCZ同拒l理?/td> |
处理PUBLISH消息异常Q在PUBACK消息中的ReturnCode字段中以相应g现出来,q就要求接收者处理拒l理由?/p>
只有在PUBLISH消息中QoS 2ӞPUBREC, PUBREL, PUBCOMP才会一L场,否则是没有出场机会的。消息格式嘛Q都很统一Q?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 0x04 |
1 | MsgType | 0x0F/0x10/0x0E |
2-3 | MsgId | 对应PUBLISH消息中的MsgId |
SUBSCRIBE用于客户端订阅某个主题的消息?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 动态计?/td> |
1 | MsgType | 0x12 |
2 | Flags | 标志?/td> |
3-4 | MsgId | 用于定对应的订阅确认SUBACK消息 |
5-N | TopicId/TopicName | 具体需要根据Flags标志位中TopicIdTypeq行填充 |
标识位具体如下:
DUP | QoS | Retain | Will | CleanSession | TopicIdType |
---|---|---|---|---|---|
bit 7 | 6,5 | 4 | 3 | 2 | 1,0 |
0/1 | 0x00-0x02 | X | X | X | 0b00/0b01/0b10 |
此处Q标志位中TopicIdType军_了SUBSCRIBE消息中TopicId/TopicName字段具体填充|预定义topic idQ或短小两个字符表示主题Qtopic nameQ,或直接填写主题?/p>
|关->客户端,订阅处理情况的确认回执,接受订阅或出于其它原因拒l之?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 0x08 |
1 | MsgType | 0x13 |
2 | Flags | 标志?/td> |
3-4 | TopicId | |关接受其注册,此处对应具体指派的TopicId |
5-6 | MsgId | SUBSCRIBE消息中对应MsgId?/td> |
7 | ReturnCode | 0x00被接受,其它D拒绝 |
标识位具体如下:
DUP | QoS | Retain | Will | CleanSession | TopicIdType |
---|---|---|---|---|---|
bit 7 | 6,5 | 4 | 3 | 2 | 1,0 |
X | 0x00-0x02 | X | X | X | X |
SUBACK消息标志位中QoS为网x据实际情冉|权后的QoS具体|q也应该是客L需要知道ƈ处理的?/p>
UNSUBSCRIBE用于客户端取消订阅某个主题的消息?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 动态计?/td> |
1 | MsgType | 0x14 |
2 | Flags | 标志?/td> |
3-4 | MsgId | 用于定对应的退订确认UNSUBACK消息 |
5-N | TopicId/TopicName | 具体需要根据Flags标志位中TopicIdTypeq行填充 |
标识位具体如下:
DUP | QoS | Retain | Will | CleanSession | TopicIdType |
---|---|---|---|---|---|
bit 7 | 6,5 | 4 | 3 | 2 | 1,0 |
X | X | X | X | X | 0b00/0b01/0b10 |
UNSUBSCRIBE消息标志位中唯一可用属性TopicIdType军_了UNSUBSCRIBE消息中TopicId/TopicName字段具体填充倹{?/p>
|关->客户端,取消订阅处理情况的确认回执,很简单,4个字节表C?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 0x04 |
1 | MsgType | 0x15 |
2-3 | MsgId | UNSUBSCRIBE消息中对应MsgId?/td> |
和MQTT协议中的PINGREQ一_存活?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 动态计?/td> |
1 | MsgType | 0x16 |
2-N | ClientId | 可选项Q表C客L休眠状态{换ؓ唤醒状态用于检查网x否ؓ其缓存消?/td> |
接受PINGREQ消息的一方,如网兛_应PINGRESP消息表示自己现在q行OK?/p>
另外一个意图,若唤醒状态客L发送PINGREQ消息之后Q直接收到PINGRESP消息Q表C网兛_前暂时没有ؓ其缓存的消息可供发送?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 0x02 |
1 | MsgType | 0x17 |
很简单,两个字节表示矣?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 动态计?/td> |
1 | MsgType | 0x18 |
2-3 | Duration | 可选项Q表C客L卛_q入睡眠状态的持箋旉?/td> |
|关接收到要q入休眠状态的客户端发送的包含有Duration字段DISCONNECT消息Ӟ可以直接q回2个字节的Q不能包含有Duration字段QDISCONNECT消息以示认?/p>
客户端发送请求网x新其遗嘱主题?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 动态计?/td> |
1 | MsgType | 0x1A |
2 | Flags | 标志?/td> |
3-N | WillTopic | 用于更新的遗׃?/td> |
标识位具体如下:
DUP | QoS | Retain | Will | CleanSession | TopicIdType |
---|---|---|---|---|---|
bit 7 | 6,5 | 4 | 3 | 2 | 1,0 |
X | 0x00-0x02 | 0/1 | X | X | X |
协议规定只有两个字节IWILLTOPICUPD也是允许存在的,存在意义用于客户端请求网兛_除已保存的遗׃题和遗嘱消息{?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 0x02 |
1 | MsgType | 0x1A |
WILLTOPICRESP为网x到WILLTOPICUPD后作出的应答消息?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 0x03 |
1 | MsgType | 0x1B |
2 | ReturnCode | 0x00被接受,其它D拒绝 |
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 动态计?/td> |
1 | MsgType | 0x1C |
2-N | WillMsg | 用于更新的遗嘱消?/td> |
客户?>|关Q确认更新的遗嘱消息?/p>
WILLMSGRESP为网x到WILLMSGUPD后作出的应答消息?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 0x03 |
1 | MsgType | 0x1D |
2 | ReturnCode | 0x00被接受,其它D拒绝 |
在MQTT-SN架构图中QMQTT-SN Forwarder转发器适用于客L无法直接讉K|关或当前传感器|络区域中不存在|关Ӟ转发器作用就体现出来了:
转发器作用于消息的封装{发,解封发送,针对消息不做修改?/p>
转发器对MQTT-SN消息装格式Q?/p>
字节索引 | 表示内容 | 说明 |
---|---|---|
0 | Length | 十进制表C长度就是N |
1 | MsgType | 0xFE |
2 | Ctrl | 包含|关和{发器之间的控制交换信息,主要是前两位包含了半径范?/td> |
3-N | Wireless Node Id | 标识所发目的或需要接收封装消息的无线节点 |
N+1-M | MQTT-SN message | 一个MQTT-SN消息消息 |
无线节点IdQWireless Node IdQ:
控制交换字段CtrlQ单个字节,位表C含义:
bit 7,2 | 1,0 |
---|---|
X | 0x00-0x03 |
MQTT-SN 1.2规范中所定义消息格式介绍完毕Q下一将对MQTT-SN主要程功、能q行阐述?/p>
q一D|间在ȝMQTT-SN的协议,寚w对不依赖于TCP传输的MQTT协议十分感兴,L再想着q货到底是怎么定义的。一pd文章皆有MQTT-SN 1.2协议所Dl成Q原文档地址Q?MQTT-SN_spec_v1.2.pdf
MQTT-SN文档分ؓ7个部分,我直接按照从前到后的序Q直接组装成四个篇。嗯Q若攑֜一文章中Q文字太长,造成排版隑ֺ?/p>
非直译,完全按照自己理解整理而成Q请知晓?/p>
原名是MQTT-SQ但会引起h们的误解Q因此更名成MQTT-SNQ?/p>
As part of the job of applying the same or similar license terms to the MQTT-S specification as those on the MQTT specification, we are proposing a small name change. The new name would be MQTT-SN, standing for exactly the same long name, MQTT for Sensor Networks. Some people had assumed that the S in MQTT-S stood for secure, so we hope this change will avoid that confusion. -- Ian Craggs
MQTT for Sensor Networks is aimed at embedded devices on non-TCP/IP networks, such as Zigbee. MQTT-SN is a publish/subscribe messaging protocol for wireless sensor networks (WSN), with the aim of extending the MQTT protocol beyond the reach of TCP/IP infrastructure for Sensor and Actuator solutions.
针对适配传感装置Q羃写ؓSAQ的特定版MQTT协议Q一般运行在嵌入式电池驱动的电子元g中,传输通过ZIEEE 802.15.4规范无线低速网l构成的无线传感|络QWSNQ,同样h企业U别Ҏ具有以数据为核心的Qdata-centricQ订?发布Ҏ?/p>
MQ针对低功耗、电池驱动、处理存储受限的讑֤、不支持TCP/IP协议栈网l的电子器g而定Ӟ比如常见的ZigBeeQ或XBeeQ,Ҏ依赖的底层传输网l不可知Q但只要|络支持双向数据传输和网养I都是可以支持较ؓ上层的MQTT-SN协议传输。比如简单数据报服务Q只要支持一个源端点发送数据到一个特定目的地端点Q这Ҏ持MQTT-SN协议Q就_了。广播数据报传输服务也是必须的用于网兛_l端的自动发现流E。ؓ了降低广播风_MQTT-SN定义了广播\径深度(q播范围或广播半径)?/p>
管MQTT-SN被设计成可能接q于MQTTQ但那些低功耗、电池驱动、资源受限的讑֤所在网l场景ؓ低速带宽、高q接p|、物理层数据包上Uؓ128字节。文档提Z以下不同点:
在MQTT-SN架构图中Q存在三U组Ӟ
MQTT-SN|关传输方式Q下面的囄一目了然?
|关需要抉择哪些消息需要和q程的MQTT Serverq行交互Q比如只选择客户端发送的PUBLISH、SUBSCRIBLE消息{?/p>
上面单介l了MQTT-SNQ下面将会介lMQTT-SN消息头部和格式?/p>
以前看英文文章或资料Q看完之后,摘要或者忘记。这一ơ选择感兴的MQTT 3.1.1介绍文章资料Q引文见文末Q作为练手;非完全翻译,去除掉一些广告性描qͼ若R权,请告知?/p>
在沉寂了四年之后Q?a >QTT 3.1.1规范?014q?0?0h式发布,与此同时MQTT 3.1.1已成为OASISQ结构化信息标准促进l织Q开攄联网消息传递协议标准(q接1 q接2Q,换种说法是MQTT 3.1.1已升Uؓ国际物联|标准?/p>
正如HTTPZh们通过万维|分享信息铺q了道\一PMQTT能将几十亿低成本、嵌入式数据采集遥测讑֤q接到网l?/p>
与MQTT 3.1Q还不是国际物联|标准呢Q规范相比,MQTT 3.1.1目标在于消除歧义Q尽可能的向后兼容,事实上一些大众所需的新Ҏ被包含在这个版本(更多的是物联|标准推动)Q因此不仅是一个维护版本,也是一U巨大的q步呢。除了概늚澄清和陈旧规范重写外Q有一些很有趣的变化是值得注意的?/p>
如果一个终端与服务器之间徏立一个持久会话连?假设q个l端没有使用C?#8220;clean session”标记清除已有回话标志), 一个新增的“Session Present”标志Q会话表C标志,逻辑gؓtrue或falseQ会在CONNACK中出玎ͼ表明MQTT服务器已l拥有当前客L上次q接会话信息Q比如订阅的主题Q排队信息和其它信息{?/p>
会话表示标志若ؓtrueQ客L可减了一ơ发送订阅SUBSCRIBLE交互步骤Q有助于更有效的数据通信QؓfalseQ客L需要再ơ发送订阅SUBSCRIBLE消息Q不可略q?/p>
MQTT 3.1.1之前Q终端连接之后无法知道其发送的订阅主题是否被MQTT服务器接受与否。此新特性较适用于细_度权限MQTT主题理Q若无授权,服务器会把错误代?0x80)附加在SUBACK中,客户端就可以知道订阅p|?/p>
需要支持时或匿名Q客L仅仅需要在发送CONNECT时把客户端标识符Q?client identifier Q置I(雉度)卛_QMQTT服务器会为此c请求生成一个随机、唯一客户端标记符。但q要求客L必须讄Clean Session标记?Q否则服务器端会直接q回包含0x02 (Identifier rejected)代码的CONNACKQ同时关闭连接?/p>
可用于后端程序(不需要维护回话状态)向终端发送消息的客户端,MQTT服务器程序可区别对待?/p>
q是一个新增的特别有用的特性,客户端可以在发送CONNECT之后Q可无须{待MQTT服务器返回的CONNACKQ根据需要即d送PUBLISH、SUBSCRIBLE、DISCONNEECT{消息,可避免客L资源{待。此Ҏ也适用于突发模式(burst-modeQ客L需求,只关心数据要快的发送出去,而不是去担心是否需要维护一个长q接?/p>
q需要MQTT服务器实现在分发消息之前查客L是否有权限发布到q些主题上?/p>
MQTT 3.1针对客户端标识符Q?client identifierQ限制是23个字节,实际环境下会有所不便Q已有遗留系l可能用UUID作ؓ客户端的标识W,q样服务器端需要做一些彼此之间的MAP映射?MQTT 3.1.1中上限ؓ65535个字节,毕竟成ؓ业界标准Q需要兼容大量的遗留讑֤和基设施?/p>
当前MQTT 3.1.1已经在很多活跃开源项?商业产品得到支持。比如Eclipse PahoQMosquittoQJBoss A-MQ 6.1, Apache ActiveMQ 5.10-SNAPSHOTQApache Camel 2.13.0QHiveMQ{?/p>
关于Eclipse Paho:
包含MQTT 3.1.1和MQTT 3.1的客L可以混合使用Q彼此可以共存于同一个MQTT服务器下Q在基本消息传输层面没有多大修改Q同LPUBLISH消息可以在MQTT客户端中自由{Q这个需要服务器端编码支持?/p>
已有的MQTT 3.1客户端可以用着急升U,但升U之后可以从新增Ҏ中收益良多?/p>
MQTT 3.1协议在弱|络环境下(比如2G/3G{)表现不够好,因此才有了反思?/p>
手机{终端在ql环境下丢包情况会非常明显,q接MQTT Server成功率很低。相比单U的h-相应模型的HTTPQ其成功率会比MQTT订阅成功高很多?/p>
手机l端在每ơTCP断开或断|后Q会卛_发vTCP重连Q连接成功,会重复以前步骤依ơ发送连接命令(CONNECTQ,订阅命oQSUBSCRIBLEQ,表明上看Q这些过E没有Q何问题,但问题就在于从终端成功徏立到服务器的q接Q到发送订阅命令,在弱|情况下Q这个过E将会变得很昂贵Q?/p>
从TCP建立开始的三次握手到完整的订阅命o发送完毕,考虑到TCP堆栈的每ơ接收数据方响应ACKQ这中间l端和服务器端至生了10ơ数据交互?
在网l变化频J或者不太稳定的2G/3G|络环境下,q种q程昑־有些冗长和不适应Q同时会加重已经不堪的弱|络负蝲的负担?/p>
q下,在Q何一个阶D늚执行q程中,都有可能产生H发性的|络中断的问题:
无法成功建立TCP链接Q或d三次握手期间Q或数据包丢失在握手之后Q或客户端连接超时过?/p>
建立q接后,发送CONNECT命o后,或没接收到TCP ACK认包,或客L{待延时太小Q导致订阅命令交互失?/p>
发送SUBSCRIBLE命o后,但服务器端没收到Q或因ؓ丢包Q或|络已断开Q导致发送SUBSCRIBLE命op|
成功发送SUBSCRIBLE命o后,或移动网l断开了(有些q营商针对认为HTTP的请求有时判断Q,或等待超ӞD订阅p|
TCP是无感知的虚拟连接,中间断开两端不会立刻得到通知Q否则就用不着心蟩保活机制了?/p>
举一个例子,U上的服务器Ҏ日志分析Q只接收到连接命令(CONNECTQ但没有后箋的订阅命令(SUBSCRIBLEQ的情况Q每天有上百万别的数量?/p>
MQ针对低速率ql环境,MQTT表现不怎么好?/p>
业务改进点:
协议改进点:
有些看似冗余Q批量或打包处理L单个处理更高效一些、更节省资源Q弱|络环境要求交互要尽可能的少Q数据嘛要的是瞬间抵达,快好?/p>
严格的分层和业务解耦,会导致性能问题。好比当前Linux内核的TCP/IP|络堆栈分层很清晎ͼ每一层都各司其职Q但和直接略q内核态直接运行在用户?User Space)的Packet I/O相比Q处理性能不是在一个档ơ上Q比如Netmap 、DPDK{?/p>
针对没有TCP/IP{网l堆栈支持的l端环境QMQTTp能助了?/p>
在一些类g传感器电子元件中Q资源十分受限,计算能力不Q嵌入TCP/IP|络堆栈不现实,比较好的方式ZIEEE 802.15.4用于低速无U个人域|?LR-WPAN)的物理层和媒体接入控制层规范之上发送UDP数据包,每一个数据包最?28个字节?/p>
MQTT-SNQMQTT For Sensor NetworksQ协议就是ؓ了非常受限类g感器而设的,协议程架构比较有趣Q?/p>
更多协议l节Q有待进一步阅诅R?/p>
先来一下网l传输的字节数?/p>
以太|头至?8个字节,IP头固?0个字节,TCP?0个字节(UDP头部8个字节)Q再加上电信宽带计费的PPPoE?个字节:
UDP可以比TCP节省12个字节?/p>
MQTT-SN协议选择使用UDPQ可以看出其在节省资源方面的努力?/p>
再看看弱|环境?/p>
在网l不好的情况下,UDP的时效性会好于TCPQTCP长连接中间交换过多、之徏立完整交互的q程成功率就很低。此U情况UDP的低延迟和实时性呈现的l果会表现的很突出?/p>
TCP或HTTP理论上是可靠q接Q但是在|络不好的时候,也不是那么可靠。客L一般提交HTTPh之后Q没有确认是否提交成功,在弱|环境下会生丢包,服务器端嘛收不到。另TCP|络堆栈会存在数据包重发机制 + 应用层重发请求,可能会导致内核处理多ơ数据包的重发(q有拥塞H口会收~,发包速度减慢Q,可能会加重弱|络的负载?/p>
和TCP相比QUDP的无q接Q代表了它快速,资源消耗小Q突现就是gq较。至于数据包丢失没有重传Q上层的业务层面应用协议/机制可以保丢失的数据包重发或补发等Qƈ且会更透明Q安全的控性权。而TCP的包重发Q上层应用没有控制权限?/p>
q接协议斚wQ?/p>
MQ要实时性特诊,或者快速抵辄端的Ҏ,不妨考虑一下UDP。不q呢Q很多时候UDP和TCP大家会合着使用Q会互相弥补其不?/p>
若MQTT协议不能够满业务需求,或许可考虑选择定制Q或化流E,或用UDP重新实现Q或者用TCPQHTTP作ؓ补充{,不一而?/p>
xQ还真有点小Ȁ动呢Q?------ 《万万没惛_》王大锤
MQTT协议诞生之初Q就未曾考虑通过HTTP传输。这也正常,|络受限、不E_|络不太适合HTTPQ?G/3G|络大家使用WAP不也OK嘛)。在|络较ؓ充裕的桌面端而言Q虽U文本对比二q制而言没多大优势,受制于历史遗留和使用习惯Q以及一大票传统基础设施方便控制事宜Q传l互联网/企业型应用,HTTP协议都是默认最佳选择Q安全可控,人机友好。选择HTTP也在情理之中?/p>
虽桌面端日渐式微Q但做统一的全q_化消息系l?消息中间Ӟ也是势?/p>
MQTT OVER HTTPQؓWEB环境提供HTTP通道协议支持Q在l一q_化这L考量下,显得合情合理?MQTT相比其它ZHTTP的交换协议而言Q比如socket.ioQ,量节省Q消息质量可控?/p>
一句话Q比它强大的Q没有它轻量。比它还ȝQ没有它可控?/p>
MQ在资源受限环境下表现如此优异,那么桌面端会表现的更ZU?/p>
二进制支持,针对览器端JavaScript而言Q处理v来,如同在石器时代要处理工业时代一些通讯方式Q非常吃力。支持Javascript二进制操作的览器现Ӟ
q和支持Websocket的浏览器的基本重叠?/p>
要想让HTTP传输MQTT具体消息二进Ӟ然后由浏览器javascript脚本q行解析Q无法做到支持所有常见浏览器Q需要考虑U文本方式的传输?/p>
AJAX方式Q支持跨域,支持所有主^収ͼ桌面+Ud讑֤Q所有浏览器Q移动的Q桌面的Q包括IE6+。那么最理想方式是JSONPQ基于文本通信的Long-polling JSONP方式?/p>
MQTT 协议关键交互点: 1. q接获取授权 1. 订阅/退订主?1. 发布消息 1. {待订阅消息 1. 关闭q接
若支持HTTP方式完成以上功能/步骤Q服务器端需要支持接收HTTPU文本内容请求,D、{换成Java对象Q业务层面实现数据流转、交换,直接插入到更具体业务中,q样很Ҏ。虽cM于桥接,但减了桥接的\径(HTTP —》MQTTQ,减少资源占用Q性能上有所保证?/p>
HTTP 文本方式Q和MQTT二进制之间需要某些规则的转换Qؓ了兼容,需要单独定义接口传输接口,ChannelEntryQ双通道和单通道处理数据的方式不同,单通道的HTTP JSONP需要支持短暂缓存消息ƈ{待客户端的依次获取。发?接收订阅消息ӞTCP/Websocket利用双通道对应Channel可直接发送?/p>
HTTP通道已经预留出接口,便于支持其它cd的HTTP传输通道Q比如需要在非浏览器环境中实现常规的long polling/Http StreamingQ仅仅需要做到实现相应接口即可?/p>
客户端ID的生成方式,一般是由服务器端生成的SessionId军_。传输纯文本方式比较l构化JSONl构比较合适?/p>
JSONP只支持HTTP GET方式Q这一炚w要牢记?/p>
要求q回的消息类型都是JSON字符串Ş式,订阅/发布的消息,一定要包含{id:10, msg:'具体消息内容'}cMjson字符丌Ӏ?/p>
一般传输的是文字内容,但具有结构化的,非json莫属。无论是走TCP方式二进制流q是JSONP传输的内容体Qjson都是不错的可l构化数据的选择?/p>
览器端jqueryQ支持jsonphQ这里有一个简单示范:
填写好地址Q自动执行连接,订阅Q接收消息,一个最单的Demo表现了其程?/p>
单实现jsonp形式的MQTT OVER HTTPQ做到文本和二进制彼此之间交换数据。M在纯HTTP环境下用MQTT协议Q是一个不错的选择?/p>
MQTT协议专注于网l、资源受限环境,建立之初不曾考虑WEB环境Q倒也正常。虽然如此,但不代表它不适合HTML5环境?/p>
HTML5 Websocket是徏立在TCP基础上的双通道通信Q和TCP通信方式很类|适用于WEB览器环境。虽然MQTT基因层面选择了TCP作ؓ通信通道Q但我们d个编解码方式QMQTT Over Websocket也可以的?/p>
q样做的好处QMQTT的用范畴被扩展到HTML5、桌面端览器、移动端WebApp、Hybrid{,多了一些想像空间。这L来,无论是移动端Q还是WEB端,MQTT都会有自q使用I间?/p>
话说Q现代化览器都已经支持WebsocketQ这里有一个所有浏览器支持列表Q?/p>
更详l列表,L接访问:http://caniuse.com/websockets
毫无疑问Q火狐和h览器带动了C览器的发展Q对HTML5标准的支持也是如此。支持Websocket的浏览器单纯从上面数字来Ԍ73.88%的支持率。但实际上还得参考浏览器市场占有率:
上图数据Q来源于: 2014q?月䆾全球L览器市Z额排行榜
过60%用户机器上浏览器的支持WebsocketQ数据很可观?/p>
针对不支持websocker的部分历史浏览器Q可以考虑一下Flash socketQ虽然用Flashsocket用以模拟Websocket很Ҏ理解Q但条g如下Q?- 需要单独占用一个端口专用于安全跨域讉K{略 - 需要浏览器支持二进制Blob 支持二进制操作的览器现Ӟ
比较一下支持Websocket和XHR2的桌面浏览器Q重叠率很高Q用Flash Socket用以模拟Websocket必要性不大,在类gIEq_上,不如直接使用Flash版本?/p>
https://github.com/yangboz/as3MQTT/tree/master/MQTTClient_AS3
不是所有浏览器都支持WebsocketQ尤其是ȝ历史发展的IE6/IE7/IE8/IE9。MQTT协议Zq制协议压根和HTTPU文本不兼容Q尤其浏览器端JavaScript处理文本很合适,但二q制显得笨手笨角,除非支持XHR2?/p>
q部分后面专门会讲到?/p>
现有一些解x案可能是后面为MQTT BrokerQ前面是d一层代理。比如:例如 mod_websocket Q对应在U示范:http://test.mosquitto.org/ws.html
表面上看着很解藕的Q实际上模仿的还是传l型的短q接反向代理架构QNginx/Apache QJava/PHP/Python/Ruby?/p>
客户端徏立一条连接,服务器端需要用到臛_两个文g句柄Q中间多了一层\径。优雅的解决ҎQ可以向socket.io看v。一套服务端E序Q同时提供若q种协议供终端选择。其实,一台MQTT Broker中间件服务器Q可以绑定多个端口,一个面向纯TCP?883端口Q一个面向Websocket?0/8080端口Q共享基逻辑Q面向不同协议?/p>
服务器添加对Websocket支持Q基本不用做多大改动。对比Tcp的附加到单个Channel的处理器列表Q?
Websocket对应单个Channel的处理器列表Q?
Z支持Websocket协议Q仅仅额外增加了Q?
啰啰嗦嗦的讲了一大通WebsocketQM对Websocket的支持还容易。后面有旉写写如何使用HTTP协议辑ֈMQTT OVER HTTP的效果?/p>
MQTT定义了物联网传输协议Q其标准們于原始TCP实现。构ZTCP的上层协议堆栈,诸如HTTP{,在空间上多了一些处理\径,E微耗费了CPU和内存,虽看似微乎其微,但对很多处理能力不的嵌入式讑֤而言Q选择原始的TCP却是最好的选择?/p>
但单UTCP不是所有物件联|的最佳选择Q提供构ZTCP基础之上的传l的HTTP通信支持Q尤其是览器、性能富裕的桌面涉及领域,q是企业最 可信赖、最可控的传输方式之一。支持多U多Lq接通道Q让目前所有一切皆可联|,除了原始TCP SocketQ还要支持构Z其之上的HTTP、HTML5 WebsocketQ就很有必要?/p>
mqtt.ioQPub/Sub中间Ӟ也可以称之ؓ推送服务器Q涵盖所有主桌面系l、浏览器q_Qƈ且倾斜 于移动互联网Q以及物联网的广阔适应天地。用一句英文概括可能更为合适:"Make everything connect”Q让所有物仉可连接。其业务目标Q可用下图概括:
mqtt.io致力于做下一代支持所有主桌面^台、所有主浏览器、所有可联网物g都可以联|的PUB/SUB消息推送系l?/p>
构徏此系l,在于降低传统企业各自分散的推送系l,l一q营Q统一理Q节省h员、运l开支?/p>
用于转换二进制流到JAVA对象的过E:
Ҏ有要写入|卡~冲区的JAVA对象转换成二q制Q?
借助于mqtt-library目Q编解码不复杂?/p>
更具体的可以查看目?/p>
单介l了一个简单的不能再简单的MQTT ServerQ只h最基本的QoS 0cd的消息订阅等?/p>
后面Q对HTML 5 WebsocketQ会在现有基代码之上Q不做多大改动,增加对MQTT Over WebSocket的支持?/p>
前面的笔记已把所有消息类型都q了一遍,q里从消息流的角度尝试解M下?/p>
在Q何网l环境下Q都会出C方连接失败,比如d公司大门那一L有了WIFI信号。但持箋q接的另一?服务器可能不能立即知道对方已断开。类似网l异常情况,都有可能在消息发送的q程中出玎ͼ消息发送出去,׃׃?/p>
MQTT协议假定客户端和服务器端E_情况一般,彼此之通信道不可靠,一旦客L|络断开Q情况就会很严重Q很难恢复原状?/p>
但别忘记Q很多客L会有怹性存储设备支持,比如闪存ROM、存储卡{,在通信出现异常的情况下可以用于保存关键数据或状态信息等?/p>
MQ异常网l情况很复杂Q只能小心处理之?/p>
?strong>QoS > 0情况下,PUBLISH、PUBREL、SUBSCRIBE、UNSUBSCRIBE{类型消息在发送者发送完之后Q需要等待一个响应消息,若在一个指定时间段内没有收刎ͼ发送者可能需要重试。重发的消息Q要求DUP标记要设|ؓ1.
{待响应的超时应该在消息成功发送之后开始算Pq且{待时应该是可以配|选项Q以便在下一ơ重试的时候,适当加大。比如第一ơ重试超?0U,下一ơ可能ؓ20U,再一ơ重试可能ؓ60U呢。当Ӟq要有一个重试次数限制的?/p>
q有一U情况,客户端重新连接,但未在可变头部中讄clean session标记Q但双方(客户端和服务器端)都应该重试先前未发送的动态消息(in-flight messagesQ。客L不被强制要求发送未被确认的消息Q但服务器端得需要重发那些未被去认的消息?/p>
QoS level为Quality of Service level的羃写,译成中文,服务质量{?/p>
MQTT 3.1协议?4.1 Quality of Service levels and flows"章节中,仅仅讨论了客L到服务器的发布流E,不太完整。因为决定消息到辄Q能够提升发送质量的Q应该是服务器发布PUBLISH消息到订阅者这一消息方向?/p>
臛_发送一ơ,发送即丢弃。没有确认消息,也不知道Ҏ是否收到?/p>
Client | Message and direction | Server |
---|---|---|
QoS = 0 | PUBLISH ----------> | Action: Publish message to subscribers then Forget Reception: <=1 |
针对的消息不重要Q丢׃无所谓?/p>
|络层面Q传输压力小?/p>
所有QoS level 1都要在可变头部中附加一?6位的消息ID?/p>
SUBSCRIBE和UNSUBSCRIBE消息使用QoS level 1?/p>
针对消息的发布,Qos level 1Q意味着消息臛_被传输一ơ?/p>
发送者若在一D|间内接收不到PUBACK消息Q发送者需要打开DUB标记?Q然后重新发送PUBLISH消息。因此会D接收方可能会收到两次PUBLISH消息。针对客L发布消息到服务器的消息流Q?/p>
Client | Message and direction | Server |
---|---|---|
QoS = 1 DUP = 0 Message ID = x Action: Store message | PUBLISH ----------> | Actions:
Reception: >=1 |
Action: Discard message | PUBACK <---------- | Message ID = x |
针对服务器发布到订阅者的消息:
Server | Message and direction | Subscriber |
---|---|---|
QoS = 1 DUP = 0 Message ID = x | PUBLISH ----------> | Actions:
Reception: >=1 |
PUBACK <---------- | Message ID = x |
发布者(客户?服务器)若因U种异常接收不到PUBACK消息Q会再次重新发送PUBLISH消息Q同时设|DUP标记?。接收者以服务器ؓ例,q可能会D服务器收到重复消息,按照程QbrokerQ服务器Q发布消息到订阅者(会导致订阅者接收到重复消息Q,然后发送一条PUBACK认消息到发布者?/p>
在业务层面,或许可以弥补MQTT协议的不之处:重试的消息ID一定要一致接收方一定判断当前接收的消息ID是否已经接受q?/p>
但一样不能够完全保Q消息一定到达了?/p>
仅仅在PUBLISHcd消息中出玎ͼ要求在可变头部中要附加消息ID?/p>
U别高,通信压力E大些,但确保了仅仅传输接收一ơ?/p>
先看协议中流E图QClient -> Server方向Q会有一个M印象Q?/p>
Client | Message and direction | Server |
---|---|---|
QoS = 2 DUP = 0 Message ID = x Action: Store message | PUBLISH ----------> | Action(a) Store message or Actions(b):
|
PUBREC <---------- | Message ID = x | |
Message ID = x | PUBREL ----------> | Actions(a):
or Action(b): Delete message ID |
Action: Discard message | PUBCOMP <---------- | Message ID = x |
Server -> SubscriberQ?/p>
Server | Message and direction | Subscriber |
---|---|---|
QoS = 2 DUP = 0 Message ID = x | PUBLISH ----------> | Action: Store message |
PUBREC <---------- | Message ID = x | |
Message ID = x | PUBREL ----------> | Actions:
|
PUBCOMP <---------- | Message ID = x |
Server端采取的Ҏa和bQ都包含了何时消息有效,何时处理消息。两个方案二选一QServer端自己决定。但无论死采取哪一U方式,都是在QoS level 2协议范畴下,不受影响。若一Ҏ有接收到对应的确认消息,会从最q一ơ需要确认的消息重试Q以便整个(QoS level 2Q流E打通?/p>
消息序会受许多因素的媄响,但对于服务器E序Q必M证消息传递流E的每个阶段要和开始的序一致。例如,在QoS level 2定义的消息流中,PUBREL必dPUBLISH具有相同的序发送:
Client | Message and direction | Server |
---|---|---|
PUBLISH 1----------> PUBLISH 2 ----------> PUBLISH 3 ----------> | ||
PUBREC 1<---------- PUBREC 2 <---------- | ||
PUBREL 1----------> | ||
PUBREC 3<---------- | ||
PUBREL 2----------> | ||
PUBCOMP 1<---------- | ||
PUBREL 3----------> | ||
PUBCOMP 2<---------- PUBCOMP 3 <---------- |
动消息(in-flight messages)数量允许有一个可保证的效果:
在MQTT协议中,PUBLISH消息固定头部RETAIN标记Q只有ؓ1才要求服务器需要持久保存此消息Q除非新的PUBLISH覆盖?/p>
对于持久的、最C条PUBLISH消息Q服务器不但要发送给当前的订阅者,q且新的订阅?new subscriberQ同样需要订阅了此消息对应的Topic nameQ会马上得到推送?/p>
TipQ新来乍到的订阅者,只会取出最新的一个RETAIN flag = 1的消息推送,不是所有?
MQTT协议中,q前定义的14U类型消息在客户端和服务器端之间数据q行交互。若以JAVA语言构徏MQTT服务器,可选择Netty作ؓ基础?/p>
在Netty中,数据的进入和出Q代表了一ơ完整的交互。无论是要进入的q是要流出的数据(单独以服务器Z)Q都可看做字节流。若把每U类型消息抽象ؓ一个具体对象,那么处理h׃难了?/p>
客户?>服务器,q入的字节流Q逐个字节/单位dQ可q原成一个具体的消息对象Q解码的q程Q?/p>
要发送到客户端的消息对象Q{换(~码Q成字节,然后由TCP通道{到接收者?/p>
断断l箋记录了MQTT 3.1协议的若q阅ȝ讎ͼM是把协议个h认ؓ不够清晰Q或者我不好理解的地方,着重进行了分析。也便于自己以后回过来头来翻阅,不是那么快的忘却?/p>
q次要讲到客L/服务器的发布消息行ؓQ与PUBLISH相关的消息类型,会在q里看到?/p>
客户端发布消息经由服务器分发到所有对应的订阅者那里。一个订阅者可以订阅若q个主题(Topic name)Q但一个PUBLISH消息只能拥有一个主题?/p>
消息架构一览:
Description | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ||
---|---|---|---|---|---|---|---|---|---|---|
Fixed header/固定头部 | ||||||||||
byte 1 | Message Type(3) | DUP flag | QoS level | RETAIN | ||||||
0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | |||
byte 2 | Remaining Length | |||||||||
Variable header/可变头部 | ||||||||||
Topic name | ||||||||||
byte 1 | Length MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
byte 2 | Length LSB (3) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | |
byte 3 | 'a' (0x61) | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | |
byte 4 | '/' (0x2F) | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | |
byte 5 | 'b' (0x62) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | |
Message Identifier | ||||||||||
byte 6 | Message ID MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
byte 7 | Message ID LSB (10) | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | |
Playload/消息?/strong> | ||||||||||
BLOBQ二q制对象形式。二q制具体包含的内容和格式Q可有应用程序自w定义。若消息体ؓI?0长度)也是可能的?/td> |
DUP flagQ设?Q表C当前ؓW一ơ发送?/p>
RETAIN flagQ只有在PUBLISH消息中才有效?/p>
Topic nameQUTF-8~码字符串Ş式,不支持通配W!
一般作为UTF-8~码写入接口Q但不排除自定义的消息格式?/p>
I的消息体(zero-lengthQ的PUBLISH消息也可以是合法的?/p>
当服务器接收到空消息?zero-length payload)、retain = 1、具有topic name的一个PUBLISHҎ消息Q表C同时满retain = 1、相同topic name的这两个特征的被持久化PUBLISH消息Q可被删除?/p>
固定头部QoS level军_了消息中间g针对发布者具体需要响应的内容Q?/p>
QoS Level | Expected response |
QoS 0 | None |
QoS 1 | PUBACK |
QoS 2 | PUBREC |
备注Q仅仅针对发布PUBLISH消息的发布者?/p>
无论是订阅者还是服务器接收到PUBLISH消息之后Q需要根据QoS level执行不同动作?/p>
QoS Level | Expected Action |
QoS 0 | 发送到所有感兴趣?/td> |
QoS 1 | 持久化记录下来,发送到所有感兴趣的参与者,q回一个PUBACK消息l发送?/td> |
QoS 2 | 持久化记录下来,暂时不发送所有感兴趣的参与者,q回一个PUBREC消息l发送?/td> |
如果服务器收到PUBLISH消息Q参与者指的是订阅者。如果订阅者收到PUBLISH消息Q参与者就是服务器? 需要注意:
未经授权的发布者提交的PUBLISH消息Q服务器会忽略掉Q客L不会被通知?/p>
作ؓ订阅?服务器接ӞQoS level = 1QPUBLISH消息之后对发送者的响应Q整个消息不复杂?/p>
Description | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
Fixed header/固定头部 | |||||||||
byte 1 | Message type (4) | DUP flag | QoS flags | RETAIN | |||||
0 | 1 | 0 | 0 | x | x | x | x | ||
byte 2 | Remaining Length (2) | ||||||||
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | ||
Variable header/可变头部 | |||||||||
Message Identifier | |||||||||
byte 1 | Message ID MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | Message ID LSB (10) | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
虽没有消息体Q但可变头部附加一?6位的无符号shortcd?/p>
字面意思ؓAssured publish receivedQ作阅?服务器对QoS level = 2的发布PUBLISH消息的发送方的响应,认已经收到QؓQoS level = 2消息的W二个消息? 和PUBACK相比Q除了消息类型不同外Q其它都是一栗?/p>
Description | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
Fixed header/固定头部 | |||||||||
byte 1 | Message type (5) | DUP flag | QoS flags | RETAIN | |||||
0 | 1 | 0 | 1 | x | x | x | x | ||
byte 2 | Remaining Length (2) | ||||||||
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | ||
Variable header/可变头部 | |||||||||
Message Identifier | |||||||||
byte 1 | Message ID MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | Message ID LSB (10) | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
无论是订阅者还是服务器Q在消费PUBREC消息之后需要发送一个PUBREL消息l发送者(和PUBRECh同样的消息IDQ,认已收到?/p>
Qos level = 2的协议流的第三个消息Q有PUBLISH消息的发布者发送,参与Ҏ收。完整示范如下:
Description | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
Fixed header/固定头部 | |||||||||
byte 1 | Message type (6) | DUP flag | QoS flags | RETAIN | |||||
0 | 1 | 1 | 0 | 0 | 0 | 1 | x | ||
byte 2 | Remaining Length (2) | ||||||||
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | ||
Variable header/可变头部 | |||||||||
Message Identifier | |||||||||
byte 1 | Message ID MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | Message ID LSB (10) | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
QoS level 1QPUBREL消息要求如此?/p>
DUP flag ?Q表C消息第一ơ被发送?/p>
毫无疑问Q剩余长度ؓ2个byte长度?/p>
可变头部中,消息ID和发布者接收到的PUBREC所包含的消息ID是一致的?/p>
作ؓQoS level = 2消息第四个Q也是最后一个消息,由收到PUBREL的一方向另一方做出的响应消息?/p>
完整的消息一览,和PUBREL一_除了消息cd?/p>
Description | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
Fixed header/固定头部 | |||||||||
byte 1 | Message type (7) | DUP flag | QoS flags | RETAIN | |||||
0 | 1 | 1 | 1 | x | x | x | x | ||
byte 2 | Remaining Length (2) | ||||||||
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | ||
Variable header/可变头部 | |||||||||
Message Identifier | |||||||||
byte 1 | Message ID MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | Message ID LSB (10) | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
当客L接收一个PUBCOMP消息Ӟ客户端摒弃原来的消息Q因为它已经成功发送消息到服务器?/p>
消息的发布和认{一些流E,主要是跟消息发布者所讑֮的QoS level有关Q稍加整理,l制了下面一张图Q理解v来可能会更清CQ?/p>
上图针对的是客户端发布消息到服务器端的方向?/p>
Z保消息已经成功传递过去,只有收到了确认,才会让h特别攑ֿ?/p>
在QoS level = 2Ӟ通信双方都需要知道各自的认程以及所处阶D늭Q交互很多,数据量大的情况下Q可能会造成数据U\传递拥塞。服务器选择QoS = 0/1Q大部分情况都是可以应对的? 比如重要消息Q就要确保对斚w要收刎ͼ然后彼此认QOKQ这个消息是真实、有效的?/p>
无论Qos level??Q还?Q服务器Q具备所有条仉满之后Q总要把收到的具体内容和topicl装成一个新的PUBLISH MessageQ也不一定要重新构造,但要求推送的PUBLISH消息Q一定要h明确的主题和内容QRETAIN标志不能讄?Q推送到所有感兴趣的订阅者?/p>
嗯,消息的发布来源别忘记q有可能来自CONNECT消息中的Will Topic和Will MessageQ若是设|了Will flag标记的话?/p>