MQTT 5.0到期消息Message Expiry

Publish: October 16, 2020 Category: 编程 No Comments

MQTT 5.0规范中有一个整洁的小功能,称为消息过期(或消息过期间隔)。

互联网上已经对此进行了多次解释。它允许发布者在其发送的任何消息中添加有效期限。有时候,我们对功能有误解。误解是MQTT 3.1.1协议缺少Message Expiry,而现在它已存在于5.0中,它将取代并替换保留的消息和其他持久会话功能。好吧,没那么快。

您很可能会从传统企业消息传递系统中了解消息过期(或TTL,生存时间)。规范的示例在这里:消息过期模式。

传统消息过期模式与MQTT

此模式的应用程序主要是这样的:

  • 首先,您希望使用者在特定时间范围内处理作业(应用程序消息)。
  • 其次,您希望将应用程序消息转发到dead lettering队列,以防使用者无法及时处理它。从dead lettering队列中,您的后端系统可以处理该消息并重新触发作业或任何其他需要的操作。
    现在,MQTT没有队列概念,当然也没有dead lettering队列概念!因此,我们从一开始就面临着截然不同的情况。您不能只配置dead lettering队列(或dead lettering主题)。顺便说一下,这也是原因,MQTT与其他系统一样,除了消息TTL之外没有“队列TTL”。 (请注意,它具有Session Expiry,这又是完全不同的东西)。

阅读剩余部分...

MQTT v3.1.1中用户名、密码、客户端ID的最大长度

Publish: October 16, 2020 Category: 编程 No Comments

在连接包中

MQTT CONNECT控制数据包始终包含有效负载。它是建立连接后客户端必须发送给服务器的第一个数据包。
它只能由客户端发送一次,如果发送了第二个连接包,服务器(代理)必须将其作为协议冲突处理并断开客户端连接。
有效载荷可能包含几个不同的字段:

唯一客户端标识符(clientid)–始终存在

  • topic
  • message
  • 用户名
  • 密码(也需要用户名!)

clientid是必需的,其他字段是可选的。可选字段的存在由标头中的标志确定。

限制

以下是合规broker应支持的限额:

clientid:23个UTF-8编码字节

用户名编码后最大655535字节(UTF-8编码)

密码:0到65535字节的二进制数据

消息限制大小:268435455字节

满足MQTT v3.1.1规范的任何代理都需要支持客户机id的以下字符:

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

broker可以选择支持其他字符和更长的客户机ID。

23个字就够了。记住,这些不是位,而是字符。例如,如果使用base58编码,则得到58^23个组合~3,62E+40~ 362112794667800000000000000000000000000个组合。如果你没有,那就更糟了。

broker可以选择支持长度为零字节的clientid。在这种情况下,代理必须为客户机分配一个唯一的clientid。并非所broker都支持这一点(根据规范“可选”)。

用户名必须是UTF-8编码字符串。密码可以是二进制数据。
消息也可以是二进制数据。




阅读剩余部分...

基于MQTT协议的物联网设备平台设计总结

Publish: September 3, 2020 Category: 编程 No Comments

IoT(Internet of Things),是当下出现频率非常高的一个词汇,无论是可穿戴设备、工业设备、商用设备通过连接互联网,实现彼此协作以及和后端服务协同交互,极大提高了设备灵活性,高可用性。
互联网的基础协议是TCP/IP。而MQTT是基于TCP/IP协议栈构建,相比其他协议更为简单,而且生态系统丰富,受到广大开发者的青睐。相比传统的HTTP协议,MQTT协议有如下优势:

  • 协议轻量级,带宽占用小
  • 通过业务层设计能实现全双工通信
  • 实时通信
  • 灵活的订阅发布模式
  • 协议简单

MQTT协议简介

MQTT 最初由 IBM 于上世纪 90
年代晚期发明和开发。它最初的用途是将石油管道上的传感器与卫星相链接。顾名思义,它是一种支持在各方之间异步通信的消息协议。异步消息协议在空间和时间上将消息发送者与接收者分离,因此可以在不可靠的网络环境中进行扩展。虽然叫做消息队列遥测传输,但它与消息队列毫无关系,而是使用了一个发布和订阅的模型。在
2014 年末,它正式成为了一种 OASIS 开放标准,而且在一些流行的编程语言中受到支持(通过使用多种开源实现)。

模型简介:

1599093713(1).png

在整个MQTT协议中有三个角色,分别是:发布者(Publisher)、代理(Broker)、订阅者(Subscriber)。如果以C-S架构来看发布者和订阅者都是客户端,代理是服务器,发布者和订阅者是独立的也是相对的。

完整流程

启动Broker,订阅者向服Broker订阅相关主题,发布者向Broker代理发布主题信息,订阅和发布没有前后顺序依赖,Broker代理向所有订阅该主题的订阅者推送消息。




阅读剩余部分...

MQTT客户端ID设计探索

Publish: March 16, 2020 Category: 小技巧 No Comments

客户机标识符是连接到MQTT Broker的每个MQTT客户机的标识符。对于Broker来说每个客户端都应该是唯一的。Broker使用它来标识客户机和客户机的当前状态。默认情况下是自动生成的。如果尝试使用相同的客户机标识符连接两个MQTT客户机,则Broker将拒绝连接。再开发中应该格外注意这件事,请确保您具有唯一的客户机id,否则您的客户机将被代理拒绝并可能显示为脱机。最糟糕的情况是导致重复clientid的客户端莫名的被Broker踢掉。

阅读剩余部分...

MQTT协议分析

Publish: November 3, 2016 Category: 小玩具,编程 2 Comments

比较全面的MQTT协议分析文档

目录

使用研究

协议分析

连接和心跳

发布流程

消息流

订阅流程

使用研究:

服务端:http://mosquitto.org/download/<;;/span>

编译安装:http://blog.csdn.net/xukai871105/article/details/39252653<;;/span>

配置说明

http://cswei.blog.51cto.com/3443978/1225617<;;/span>

PHP实现

https://github.com/mgdm/Mosquitto-PHP<;;/span>

LUA实现

https://github.com/flukso/lua-mosquitto<;;/span>

译文

http://my.oschina.net/scholer/blog/296402<;;/span>

PHP环境相关

http://www.hivemq.com/blog/mqtt-client-library-encyclopedia-mosquitto-php<;;/span>

http://wingsquare.com/blog/developing-php-client-for-mqtt-using-mosquitto-php-library/<;;/span>

mqtt 遇到的错误收集:

Error: Invalid user 'mosquitto' 没有mosquitto的用户

两种解决办法:

(1)修改配置文件:mosquitto.conf ,增加登录的用户,例如当前登录用户为root

60444674196.jpeg

(1)执行命令增加当前用户:

adduser mosquitto

如下图:

75166231759.jpeg

•PINGREQ/PINGRESP 心跳

功能测试:

1) Connect

2) Disconnect

3) Subscribe

4) UnSubscribe

5) Publish QOS0,QOS1, QOS2

6) Retain 【为新来的订阅者推送那些已经存在的持久化消息】

7) Will 特性【发布遗愿】

协议分析:

先说一下整个协议的构造,整体上协议可拆分为:

固定头部+可变头部+消息体

固定头部

002110437924.png

协议头占两个字节

BYTE1

【RETAIN】0 : 保持;针对PUBLISH消息。

1 表示发送的消息一直持久保存,不受服务端重启影响,不但要发送给当前订阅者,并且以后新来的订阅者订阅了此topic name的订阅者会马上得到推送,新订阅者只会接受一次flag为1的消息。

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

【Qos level】1、2 : Quality of Service服务质量,使用两个二进制表示的publish类型消息

QoS value

bit 2

bit 1

Description

0

0

0

至多一次

发完即丢弃

<=1

1

0

1

至少一次

需要确认回复

=1

2

1

0

只有一次

需要确认回复

=1

3

1

1

待用,保留位置

【DUP flag】3 打开标志

保证消息可靠传输,默认为0,只占用一个字节,表示第一次发送。不能用于检测消息重发送等。只适用于客户端或者服务端尝试重发PUBLISH、PUBREL、SUBCRIBE或UNSUBSCRIBE消息注意需要满足以下条件:当QOS>0 消息需要回复确认 此时,在可变头部需要包含消息ID。当值为1时,表示当前消息先前已经被传送过。

【Message Type】4、5、6、7 消息类型x

Mnemonic

Enumeration

Description

Reserved

0

Reserved //保留待用

CONNECT

1

Client request to connect to Server //客户端请求连接到服务端

CONNACK

2

Connect Acknowledgment //请求应答,请求连接确认

PUBLISH

3

Publish message //发布消息

PUBACK

4

Publish Acknowledgment //发布应答,发布确认

PUBREC

5

Publish Received (assured delivery part 1) //发布已接收,保证传递1

PUBREL

6

Publish Release (assured delivery part 2) //发布释放,保证传递2

PUBCOMP

7

Publish Complete (assured delivery part 3) //发布完成,保证传递3

SUBSCRIBE

8

Client Subscribe request //客户端订阅请求

SUBACK

9

Subscribe Acknowledgment //订阅应答,订阅请求

UNSUBSCRIBE

10

Client Unsubscribe request //客户端取消订阅请求

UNSUBACK

11

Unsubscribe Acknowledgment //取消订阅应答,取消订阅确认

PINGREQ

12

PING Request //ping请求

PINGRESP

13

PING Response //ping响应

DISCONNECT

14

Client is Disconnecting //客户端断开连接

Reserved

15

Reserved //保留待用

BYTE2

是用来保存接下去的变长头部+消息体的总大小的。

但是不是并不是直接保存的,同样也是可以扩展的,其机制是,前7位用于保存长度,后一部用做标识。举个例了,即如果计算出后面的大小为0的,正常保存,如果是127的,则需要二个字节保存了,将第一个字节的最大的一位置1,表示未完。然后第二个字节继续存。拿130来说,第一个字节存10000011,第二个字节存000000001,也就是0x83,0x01,把两个字节连起来看,第二个字节权重从2的8次开始。同起可以加第3个字节,最多可以加至第4个字节。故MQTT协议最多可以实现268 435 455 (0xFF, 0xFF, 0xFF, 0x7F)将近256M的数据。可谓能伸能缩。

单个字节最大值,01111111=>0x7f=>127

时MQTT协议最多允许4个字节表示剩余长度。那么最大长度为:0xFF,0xFF,0xFF,0x7F,二进制表示为:11111111,11111111,11111111,01111111,十进制:268435455 byte=261120KB=256MB=0.25GB 四个字节之间值的范围:

Digits

From

To

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)

可变头部

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

218212825500.png

这个是可变头部的全貌。

1、首先最上面的8个字节是Protocol Name(编码名),UTF编码的字符“MQIsdp”,头两个是编码名提长为6。

这里多说一些,接下去的协议多采用这种方式组合,即头两个字节表示下一部分的长,然后后面跟上内容。这里头两个字节长为6,下面跟6个字符“MQIsdp”。

2、Protocol Version,协议版本号,v3 也是固定的。

3、Connect Flag,连接标识,有点像固定头部的。8位分别代表不同的标志。第1个字节保留。

Clean Session,Will flag,Will Qos, Will Retain都是相对于CONNECT消息来说的。

Clean Session:0表示如果订阅的客户机断线了,那么要保存其要推送的消息,如果其重新连接时,则将这些消息推送。

1表示消除,表示客户机是第一次连接,消息所以以前的连接信息。

Will Flag,表示如果客户机在不是在发送DISCONNECT消息中断,比如IO错误等,将些置为1,要求重传。并且下且的WillQos和WillRetain也要设置,消息体中的Topic和MessageID也要设置,就是表示发生了错误,要重传。

Will Qos,在CONNECT非正常情况下设置,一般如果标识了WillFlag,那么这个位置也要标识。

Will RETAIN:同样在CONNECT中,如果标识了WillFlag,那么些位也一定要标识

usename flag和passwordflag,用来标识是否在消息体中传递用户和密码,只有标识了,消息体中的用户名和密码才用效,只标记密码而不标记用户名是不合法的。

4、Keep Alive,表示响应时间,如果这个时间内,连接或发送操作未完成,则断开tcp连接,表示离线。

5、Connect Return Code即通常于CONNACK消息中,表示返回的连接情况,我可以通过此检验连接情况。

bVyoew.png

6、Topic Name,订阅消息标识,MQTT是基于订阅/发布的消息,那么这个就是消息订阅的标识,像新闻客户端里的订阅不同的栏目一样。用于区别消息的推送类别。

主要用于PUBLISH和SUBSCRIBE中。最大可支持32767个字符,即4个字节。

消息体(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),翻译成中文就是,最高有效位,最低有效位。最高有效位在最低有效位左边/上面,表示这是一个大端字节/网络字节序,符合人的阅读习惯,高位在最左边。

bit

7

6

5

4

3

2

1

0

Message Identifier MSB

Message Identifier LSB

但凡如此表示的,都可以视为一个16位无符号short类型整数,两个字节表示。在JAVA中处理比较简单:

DataInputStream.readUnsignedShort

或者

in.read() * 0xFF + in.read();

最大长度可为: 65535

基于workmen的客户端实现:

MQTT协议--连接和心跳

截图.png

可变头部

协议名称和协议版本都是固定的。

连接标志(Connect Flags)

一个字节表示,除了第1位是保留未使用,其它7位都具有不同含义。

业务上很重要,对消息总体流程影响很大,需要牢记。

Clean Session

0