概述
本文结合mqttjs和mqtt 5.0协议介绍各属性的作用
连接属性说明
var mqtt = require('mqtt')
var topic = 'test_topic'
// 连接选项
const options = {
// 客户标识符(ClientID)识别客户端。连接服务端的每个客户端都有唯一的客户标识符(ClientID)。客户端和服务端都必须使用客户标识符(ClientID)识别两者之间的 MQTT 会话相关的状态
// 客户标识符只能包含大写字母、小写字母和数字
// 客户端在实现时可以提供一个便于生成随机客户标识符的算法。使用此算法时,客户端需要注意避免创建长期孤儿会话。
clientId: 'mqttjs_oden_test',
port: 8083,
protocolId: 'MQTT',
protocolVersion: 5,
username: 'oden',
password: '123456',
// incomingStore: a Store for the incoming packets
// outgoingStore: a Store for the outgoing packets
// queueQoSZero: if connection is broken, queue outgoing QoS zero messages (default true)
// set to false to receive QoS 1 and 2 messages while offline
// 如果收到新开始(Clean Start)为1的CONNECT报文,客户端和服务端必须丢弃任何已存在的会话,并开始一个新的会话
// 如果收到新开始(Clean Start)为0的CONNECT报文,并且存在一个关联此客户标识符的会话,服务端必须基于此会话的状态恢复与客户端的通信。
// 如果收到新开始(Clean Start)为0的CONNECT报文,并且不存在任何关联此客户标识符的会话,服务端必须创建一个新的会话
clean: true,
// 单位s, set to 0 to disable,保持连接(Keep Alive)指在客户端传输完成一个MQTT控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。
// 客户端负责保证控制报文发送的时间间隔不超过保持连接的值。如果没有任何其它的MQTT控制报文可以发送,客户端必须发送一个PINGREQ 报文
// 如果服务端返回的CONNACK报文中包含服务端保持连接(Server Keep Alive),客户端必须使用此值代替其发送的保持连接
// 服务端在1.5倍的保持连接时间内没有收到客户端的控制报文,它必须断开客户端的网络连接,并判定网络连接已断开
// 客户端发送了PINGREQ报文之后,如果在合理的时间内仍没有收到PINGRESP报文,它应该关闭到服务端的网络连接。
// 如果保持连接(Keep Alive)值为零,客户端不必按照任何特定的时间发送MQTT控制报文。
keepalive: 10,
connectTimeout: 4000, // 超时时间 ms
reschedulePings: true, // reschedule ping messages after sending packets (default true)
queueQoSZero: true, // if connection is broken, queue outgoing QoS zero messages (default true)
reconnectPeriod: 1000, // 1000 milliseconds, interval between two reconnections. Disable auto reconnect by setting to 0
transformWsUrl: (url, options, client) => {
console.log('transformWsUrl========')
console.log(url)
console.log(options)
console.log(client)
return url
},
// 遗嘱必须在网络连接被关闭、遗嘱延时间隔到期或者会话结束之后被发布,除非服务端收到包含原因码为0x00(正常关闭)的DISCONNECT报文之后删除了遗嘱消息(Will Message),或者一个关于此客户标识符的新的网络连接在遗嘱迟发时间(Will Delay Interval)超时之前被创建
// 一旦遗嘱消息(Will Message)被发布或者服务端收到包含原因码为0x00(正常关闭)的DISCONNECT报文,遗嘱消息(Will Message)必须从服务端的会话中删除
// 服务端关闭或出错的情况下,可以在服务重新启动之后发布遗嘱消息
// 通过设置晚于会话过期间隔(Session Expiry Interval)的遗嘱迟发时间(Will Delay Interval)并发送包含原因码0x04(包含遗嘱的断开连接),客户端得以发出会话过期(Session Expiry)通告。
// 如果某个连接到服务端的网络连接使用已存在的客户标识符,此已存在的网络连接的遗嘱消息将会被发布,除非新的网络连接设置了新开始(Clean Start)为0并且遗嘱延时大于0。如果遗嘱延时为0,遗嘱消息将在网络连接断开时发布。如果新开始为1,遗嘱消息也将被发布,因为此会话已结束。
will: {
topic: '',
payload: 'the message to publish',
// 遗嘱服务质量可以被设置为0(0x00),1(0x01)或2(0x02)
qos: 0,
// 指定遗嘱消息(Will Message)在发布时是否会被保留,如果遗嘱保留被设置为1,则服务端必须将遗嘱消息当做保留消息发布
retain: 0,
// properties of will by MQTT 5.0
properties: {
// 遗嘱延时间隔默认值将为0,即不用延时发布遗嘱消息,单位s
// 遗嘱时间间隔的一个用途是避免在频繁的网络连接临时断开时发布遗嘱消息,因为客户端往往会很快重新连上网络并继续之前的会话。
willDelayInterval: 0,
// Will Message is UTF-8 Encoded Character Data or not boolean
payloadFormatIndicator: true,
// 如果设定了消息过期间隔(Message Expiry Interval)(秒),并在服务端发布遗嘱消息时被当做发布过期间隔
// 如果没有设定消息过期间隔,服务端发布遗嘱消息时将不发送消息过期间隔(Message Expiry Interval)。
messageExpiryInterval: 100,
// 用来描述遗嘱消息(Will Message)的内容
contentType: 'describing the content of the Will Message string',
// 用来表示响应消息的主题名
responseTopic: 'String which is used as the Topic Name for a response message string',
// 二进制数据,对比数据被请求消息发送端在收到响应消息时用来标识相应的请求。
// 对比数据只对请求消息(Request Message)的发送端和响应消息(Response Message)的接收端有意义。
correlationData: '',
// 用户属性,此属性旨在提供一种传递应用层名称-值标签的方法,其含义和解释仅由负责发送和接收它们的应用程序所有。
userProperties: {}
}
},
},
// mqtt 5.0支持的属性
properties: {
// 单位s,会话过期间隔(Session Expiry Interval)值未指定,则使用0。
// 如果设置为0或者未指定,会话将在网络连接(Network Connection)关闭时结束。0xFFFFFFFF (UINT_MAX),则会话永不过期。
// 网络连接关闭时会话过期间隔(Session Expiry Interval)大于0,则客户端与服务端必须存储会话状态
// 把新开始(Clean Start)设置为1且会话过期间隔(Session Expiry Interval)设置为0,等同于在MQTT v3.1.1中把清理会话(CleanSession)设置为1。把新开始(Clean Start)设置为0且不设置会话过期间隔(Session Expiry Interval),等同于在MQTT v3.1.1中把清理会话标志设置为0。
// 当希望只处理连接上服务端之后才发布的消息,客户端应该把新开始(Clean Start)设置为1且会话过期间隔(Session Expiry Interval)设置为0,这样客户端就不会收到它连接之前被服务端所发布的消息
// 某些客户端使用的网络可能只能提供断断续续的连接,这种客户端可以使用较短的会话过期间隔(Session Expiry Interval)以便在网络再次可用后重新连接到服务端时获得持续的消息交付
// 当客户端认定其将来不会使用本次会话时,应该在断开时把会话过期间隔(Session Expiry Interval)设置为0。
// 客户端应当使用CONNACK报文中的会话存在(Session Present)来判定服务端是否存储了其会话。
sessionExpiryInterval: 100,
// 客户端使用此值限制客户端愿意同时处理的QoS为1和QoS为2的发布消息最大数量。没有机制可以限制服务端试图发送的QoS为0的发布消息。
// 如果没有设置最大接收值,将使用默认值65535。
receiveMaximum: 1000,
// 表示的客户端愿意接收的最大报文长度,客户端使用最大报文长度通知服务端其所能处理的单个报文长度限制。
// 如果没有设置最大报文长度(Maximum Packet Size),则按照协议由固定报头中的剩余长度可编码最大值和协议报头对数据包的大小做限制。
// 服务端不能发送超过最大报文长度(Maximum Packet Size)的报文给客户端。收到长度超过限制的报文将导致协议错误,客户端发送包含原因码0x95(报文过大)的DISCONNECT报文给服务端
// 当报文过大而不能发送时,服务端必须丢弃这些报文,然后当做应用消息发送已完成处理
// 共享订阅的情况下,如果一条消息对于部分客户端来说太长而不能发送,服务端可以选择丢弃此消息或者把消息发送给剩余能够接收此消息的客户端。
maximumPacketSize: 1024,
// 此值指示了客户端能够接收的来自服务端的主题别名(Topic Alias)最大数量。客户端使用此值来限制本次连接可以拥有的主题别名的数量。
// 值为零表示本次连接客户端不接受任何主题别名(Topic Alias)。如果主题别名最大值(Topic Alias)没有设置,或者设置为零,则服务端不能向此客户端发送任何主题别名
topicAliasMaximum: 100,
// 如果没有请求响应信息(Request Response Information),则请求响应默认值为0。
// 客户端使用此值向服务端请求CONNACK报文中的响应信息(Response Information)。值为0,表示服务端不能返回响应信息。值为1,表示服务端可以在CONNACK报文中返回响应信息。
requestResponseInformation: true,
// 如果没有请求问题信息(Request Problem Information),则请求问题默认值为1。
// 客户端使用此值指示遇到错误时是否发送原因字符串(Reason String)或用户属性(User Properties)。
// 如果请求问题信息的值为0,服务端可以选择在CONNACK或DISCONNECT报文中返回原因字符串(Reason String)或用户属性(User Properties),但不能在除PUBLISH,CONNACK或DISCONNECT之外的报文中发送原因字符串(Reason String)或用户属性(User Properties)
// 如果此值为1,服务端可以在任何被允许的报文中返回原因字符串(Reason String)或用户属性(User Properties)。
requestProblemInformation: true,
// 用户属性(User Property)可以出现多次,表示多个名字/值对。相同的名字可以出现多次。
// CONNECT报文中的用户属性可以被用来发送客户端到服务端的连接相关的属性。
userProperties: {},
// 证方法(Authentication Method)标识符。包含了扩展认证的认证方法(Authentication Method)名称
// 如果没有认证方法,则不进行扩展验证。
// 如果客户端在CONNECT报文中设置了认证方法,则客户端在收到CONNACK报文之前不能发送除AUTH或DISCONNECT之外的报文 [MQTT-3.1.2-30]。
authenticationMethod: '',
// 认证数据(Authentication Data)标识符,二进制的认证数据。
authenticationData: '',
},
// authPacket: settings for auth packet object
}
var client = mqtt.connect('ws://120.79.69.236/mqtt', options)
订阅属性说明
function mqttSubscribe() {
// 如果服务端收到的SUBSCRIBE报文中的一个主题过滤器与当前会话的一个非共享订阅(Non-shared Subscription)相同,那么必须使用新的订阅替换现存的订阅。新订阅的主题过滤器与之前的订阅相同,但其订阅选项可能不同。如果保留处理选项为0,任何匹配该主题过滤器的保留消息必须被重发,但替换订阅不能造成应用消息的丢失
client.subscribe(topic, {
// 此字段给出服务端可以向此客户端发送的应用消息的最大QoS等级
// 如果订阅客户端的某个主题过滤器已被授予的最大QoS等级为1,那么匹配此过滤器的QoS等级为0的应用消息按照QoS等级为0分发给此客户端。这意味着客户端最多只能收到该消息的一个副本。
// 另一方面,发布到相同主题的QoS等级为2的消息,其QoS等级被服务端降级为1以便分发给该客户端。因此该客户端可能收到此消息的多个副本。
// 使用QoS等级2订阅某个主题过滤器,等于是说:我想要按照消息被发布时的QoS等级接收匹配此过滤器的消息 。这意味着发布者负责决定消息可以被发布的最大QoS等级,但订阅端可以要求服务端降低该消息的QoS到更适合它的等级。
qos: 2,
// No Local MQTT 5.0 flag,非本地(No Local)选项。值为1,表示应用消息不能被转发给发布此消息的客户标识符。共享订阅时把非本地选项设为1将造成协议错误
nl: false,
// Retain as Published MQTT 5.0 flag,值为1,表示向此订阅转发应用消息时保持消息被发布时设置的保留(RETAIN)标志。值为0,表示向此订阅转发应用消息时把保留标志设置为0。当订阅建立之后,发送保留消息时保留标志设置为1。
// 对于某个指示其不支持保留消息的服务端,发布保留和保留处理选项的所有有效值都将得到同样的结果:订阅时不发送任何保留消息,且所有消息的保留标志都会被设置为0。
rap: true,
// Retain Handling MQTT 5.0 ,此选项指示当订阅建立时,是否发送保留消息。此选项不影响之后的任何保留消息的发送。如果没有匹配主题过滤器的保留消息,则此选项所有值的行为都一样。
// 0 = 订阅建立时发送保留消息 1 = 订阅建立时,若该订阅当前不存在则发送保留消息 2 = 订阅建立时不要发送保留消息
// 已存在订阅的情况下不发送保留消息是很有用的,比如重连完成时客户端不确定订阅是否在之前的会话连接中被创建。
// 不发送保存的保留消息给新创建的订阅是很有用的,比如客户端希望接收变更通知且不需要知道最初的状态。
// 如果服务端收到的主题过滤器与服务端已存在的某个共享订阅(Shared Subscription)主题过滤器相同,则将此会话添加到该共享订阅中。不发送任何保留消息。
rh: 0,
properties:{
// 长字节整数表示订阅标识符。订阅标识符取值范围从1到268,435,455,如果包含订阅标识符,它将与订阅一起被存储。如果未指定此属性,则订阅被存储时将不包含订阅标识符。
// 当服务端收到客户端的UNSUBSCRIBE报文时,服务端将此会话标识符从服务端的会话状态中移除
subscriptionIdentifier: 111,
userProperties:{}
},
},function (err, granted) {
if (!err) {
console.log('subscribe success!')
} else {
console.log('err========')
console.log(err)
}
})
}
取消订阅说明
/**
* 当服务端收到UNSUBSCRIBE报文:
* 它必须停止添加为了交付给客户端的与主题过滤器相匹配的任何新消息
* 它必须完成任何已经开始发送给客户端的、与主题过滤器相匹配的、QoS等级为1或2的消息
* 它可以继续交付任何为交付给客户端而缓存的消息。
*/
function mqttUnsubscribe() {
client.unsubscribe(topic, {
properties:{
userProperties:{}
}
}, function (err) {
if (!err) {
console.log('unsubscribe success!')
} else {
console.log('err========')
console.log(err)
}
})
}
发布消息说明
client.publish(topic, 'Hello mqtt', {
// 0:最多分发一次,1:至少分发一次,2:只分发一次
qos:0,
// 客户端发给服务端的PUBLISH报文的保留(Retain)标志被设置为1,服务端必须存储此应用消息,并用其替换此话题下任何已存在的消息
// 如果载荷为空,消息可以正常被服务端所处理,但是此话题下的任何保留消息必须被丢弃,并且此话题未来的订阅者将不会收到保留消息
// 如果服务端收到保留标志设置为1且QoS设置为0的PUBLISH报文,服务端应该把此QoS为0的消息存储为其主题下最新的保留消息,但服务端可以选择在任何时间丢弃此消息
retain:1,
// 如果DUP标志被设置为0,表示这是客户端或服务端第一次请求发送这个PUBLISH报文。如果DUP标志被设置为1,表示这可能是一个早前报文请求的重发。
// 客户端或服务端请求重发一个PUBLISH报文时,必须将DUP标志设置为1。对于QoS为0的消息,DUP标志必须设置为0
// 服务端发送PUBLISH报文给订阅者时,收到(入站)的PUBLISH报文的DUP标志的值不会被传播。发送(出站)的PUBLISH报文与收到(入站)的PUBLISH报文中的DUP标志是独立设置的,它的值必须单独的根据发送(出站)的PUBLISH报文是否是一个重发来确定
dup:0,
// fired when message is put into outgoingStore if QoS is 1 or 2
cbStorePut: function() {},
properties: {
payloadFormatIndicator: 'Payload is UTF-8 Encoded Character Data or not boolean',
// 以秒为单位, 如果消息过期间隔(Message Expiry Interval)已过期,服务端还没开始向匹配的订阅者交付该消息,则服务端必须删除该订阅者的消息副本
// 如果消息过期间隔不存在,应用消息不会过期。
// 服务端发送给客户端的PUBLISH报文中必须包含消息过期间隔,值为接收时间减去消息在服务端的等待时间
messageExpiryInterval: 'the lifetime of the Application Message in seconds number',
// 主题别名是一个整数,用来代替主题名对主题进行识别。主题别名可以减小PUBLISH报文的长度,这对某个网络连接中发送的很长且反复使用的主题名来说很有用。
// 发送端决定是否使用主题别名及别名值如何选取。发送端通过在PUBLISH报文中包含的非0长度主题名和主题别名来设置主题别名映射。接收端正常处理该PUBLISH报文,但同样将指定的主题别名映射到主题名。
// 如果接收端已经设置了某个主题别名映射,发送端可以发送包含主题别名和长度为0的主题名的PUBLISH报文。接收端把此PUBLISH报文的主题名当做其包含的主题别名所映射的主题名。
// 发送端可以通过在同一个网络连接中发送另一个包含同样主题别名和不同非0长度主题名的PUBLISH报文来修改主题别名映射关系。
// 主题别名不允许为0。发送端不能发送包含主题别名值为0的PUBLISH报文
topicAlias: 'value that is used to identify the Topic instead of using the Topic Name number',
// 响应主题不能包含通配符
// 服务端在收到应用消息时必须将响应主题原封不动的发送给所有的订阅者
// 包含响应主题的应用消息接收端使用响应主题作为主题名,发送作为响应消息的PUBLISH报文。如果请求消息中包含对比数据,接收端应当在发送作为对此请求消息进行响应的PUBLISH报文中包含此对比数据。
responseTopic: 'String which is used as the Topic Name for a response message string',
// 二进制数据。对比数据被请求消息发送端在收到响应消息时用来标识相应的请求。
// 如果没有设置对比数据,则请求方(Requester)不需要任何对比数据。
correlationData: 'used by the sender of the Request Message to identify which request the Response Message is for when it is received binary',
userProperties: {},
// 长字节整数表示的订阅标识符。订阅标识符取值范围从1到268,435,455。订阅标识符的值为0将造成协议错误。如果某条发布消息匹配了多个订阅,则将包含多个订阅标识符。这种情况下他们的顺序并不重要。
// 如果客户端在重叠的订阅中指定了订阅标识符,服务端在发布这些订阅相匹配的消息时必须包含这些订阅标识符
// 对于共享订阅,发送给某个客户端的PUBLISH报文中将只包含该客户端的SUBSCRIBE报文中发送的订阅标识符。
subscriptionIdentifier: 'representing the identifier of the subscription number',
// 用来描述应用消息的内容。内容类型必须是UTF-8编码的字符串,服务端必须把收到的应用消息中的内容类型原封不动的发送给所有的订阅者
contentType: 'String describing the content of the Application Message string'
},
}, function (err) {
if (!err) {
console.log('publish success!')
} else {
console.log('err========')
console.log(err)
}
})
断开连接说明
/**
* 客户端发给服务端的最后一个MQTT控制报文。表示客户端为什么断开网络连接的原因。
* 客户端和服务端在关闭网络连接之前可以发送一个DISCONNECT报文。
* 如果在客户端没有首先发送包含原因码为0x00(正常断开)DISCONNECT报文并且连接包含遗嘱消息的情况下,遗嘱消息会被发布。
*/
function mqttDisconnect() {
client.end(false, {
reasonCode: 0, // 0:正常断开
properties:{
// 秒为单位的会话过期间隔(Session Expiry Interval),如果没有设置会话过期间隔,则使用CONNECT报文中的会话过期间隔。
sessionExpiryInterval: 'representing the Session Expiry Interval in seconds number',
reasonString: 'representing the reason for the disconnect string',
// 客户端可以使用它来识别其他要使用的服务端
serverReference: 'String which can be used by the Client to identify another Server to use string',
userProperties:{}
}
}, function () {
console.log('mqttDisconnect success!')
})
}
通配符说明
- 主题层级分隔符 Topic level separator:斜杠(“/” U+002F)用于分割主题的每个层级,为主题名提供一个分层结构。
- 多层通配符 Multi-level wildcard:数字符号(“#” U+0023)是用于匹配主题中任意层级的通配符。多层通配符表示它的父级和任意数量的子层级。多层通配符必须单独指定,或者跟在主题层级分隔符后面。不管哪种情况,它都必须是主题过滤器的最后一个字符
- 单层通配符 Single-level wildcard:加号(“+” U+002B) 是只能用于单个主题层级匹配的通配符。在主题过滤器的任意层级都可以使用单层通配符,包括第一个和最后一个层级。
- 服务端不能将$字符开头的主题名匹配通配符(#或+)开头的主题过滤器
- $SYS/被广泛用作包含服务器特定信息或控制接口的主题的前缀。
- 应用不能使用$字符开头的主题。
- 如果客户端想同时接受以“$SYS/”开头主题的消息和不以$开头主题的消息,它需要同时订阅“#”和“$SYS/#”。
- 共享订阅:$share/{ShareName}/{filter} ,共享订阅主题过滤器必须以$share/开始,且必须包含至少一个字符长度的共享名(ShareName) [MQTT-4.8.2-1]。共享名不能包含字符“/”,“+”或“#”,但必须跟在“/”字符后面。此“/”字符后面必须跟随一个主题过滤器