Mosquitto感知客户端上下线的方法 MQTT 遗嘱消息(Will Message)的使用

2020/07 17 14:07

当客户端断开连接时,发送给相关的订阅者的遗嘱消息。以下情况下会发送 Will Message:

服务端发生了I/O 错误或者网络失败;

客户端在定义的心跳时期失联;

客户端在发送下线包之前关闭网络连接;

服务端在收到下线包之前关闭网络连接。

遗嘱消息一般通过在客户端 CONNECT 的时候指定。如下所示,在连接的时候通过调用 MqttConnectOptions 实例的 setWill 方法来设定。任何订阅了下面的主题的客户端都可以收到该遗嘱消息。

//方法1
MqttConnectOptions.setWill(MqttTopic topic, byte[] payload, int qos, boolean retained)

//方法2
MqttConnectOptions.setWill(java.lang.String topic, byte[] payload, int qos, boolean retained)

使用场景

在客户端 A 进行连接时候,遗嘱消息设定为”offline“,客户端 B 订阅这个遗嘱主题。当 A 异常断开时,客户端 B 会收到这个”offline“的遗嘱消息,从而知道客户端 A 离线了。
Connect Flag 报文字段
Bit     7     6     5     4     2     1     0
User Name Flag     Password Flag     Will Retain     Will QoS     Will Flag     Clean Start     Reserved
byte 8     X     X     X     X     X     X     X

遗嘱消息在客户端正常调用 disconnect 方法之后并不会被发送。
Will Flag 作用

简而言之,就是客户端预先定义好,在自己异常断开的情况下,所留下的最后遗愿(Last Will),也称之为遗嘱(Testament)。这个遗嘱就是一个由客户端预先定义好的主题和对应消息,附加在CONNECT的可变报文头部中,在客户端连接出现异常的情况下,由服务器主动发布此消息。

当Will Flag位为1时,Will QoS和Will Retain才会被读取,此时消息体中要出现Will Topic和Will Message具体内容,否则Will QoS和Will Retain值会被忽略掉。

当Will Flag位为0时,则Will Qos和Will Retain无效。
命令行示例

下面是一个Will Message的示例:

Sub端clientid=sub预定义遗嘱消息:

mosquitto_sub --will-topic test --will-payload die --will-qos 2 -t topic -i sub -h 192.168.1.1

客户端 clientid=alive 在 192.168.1.1(EMQ服务器) 订阅遗嘱主题

mosquitto_sub -t test -i alive -q 2 -h 192.168.1.1

异常断开Sub端与Server端(EMQ服务器)连接,Pub端收到Will Message 。

高级使用场景

这里介绍一下如何将 Retained 消息与Will 消息结合起来进行使用。

客户端 A 遗嘱消息设定为”offline“,该遗嘱主题与一个普通发送状态的主题设定成同一个 A/status;
当客户端 A 连接时,向主题 A/status 发送 “online” 的 Retained 消息,其它客户端订阅主题 A/status的时候,获取 Retained 消息为 “online” ;
当客户端 A 异常断开时,系统自动向主题 A/status 发送”offline“的消息,其它订阅了此主题的客户端会马上收到”offline“消息;如果遗嘱消息被设定了 Retained 的话,这时有新的订阅A/status主题的客户端上线的时候,获取到的消息为“offline”。

 

 

 

1、怎么知道设备下线?

异常断线(Mosquitto没有收到MQTT 的DISCONNECT报文)时,使用遗嘱机制,mqtt客户端在建立mqtt连接的时候,设置遗嘱(向什么topic发送什么数据):当前这个tcp连接断开的时候,Mosquitto自动按照遗嘱设置发送消息,这时利用这个机制,topic应用设定好,例如:iot/status/onoffline;所设置的遗嘱消息内容就是一个JSON字符串,里面包含当前连接的ID等信息,上线状态,例如:

{"id":11232232432,//这个id就是当前设备的ID

"status":"offline"//表示这是一个设备下线的通知;

}

这样这个连接断开的时候(无论正常还是异常)谁订阅了这个topic就能收到这个消息.

正常断线(mqtt客户端主动发送了DISCONNECT报文)时,客户端要在发送DISCONNECT报文之前(及调用paho包的disconnect接口之前)主动向上下线topic发布一条设备下线的消息。

2、怎么知道设备上线?
mqtt客户端在建立mqtt连接之后,无条件向某个topic(例如:iot/status/onoffline)发送一个消息,消息内容包括自己的设备ID和上线状态,例如:

{"id":11232232432,//这个id就是当前设备的ID

"status":"online"//表示这是一个设备上线的通知;

}

,这样一旦自己的设备上线了,订阅了这个topic的应用就能收到该设备发过来的消息;

在上述说明中:正常的上下线通知都是由客户端主动发送,即:建立mqtt连接之后的第一条pub消息和断开mqtt连接之前的最后一条pub消息;对于异常断开的连接使用MQTT的遗嘱机制让mqtt broker来自动发送异常下线消息。

 

如果觉得这种方式比较麻烦,可加入群:221779856,群里开源的软件在Mosquitto上做了优化修改,不需要客户端做任何配合就能实现:
(1)自动上下线通知,无需客户端做任何配合,详细可参考开源软件的说明,其思路如下:
【功能说明】向指定topic发送上下线通知的消息

【使用说明】:

打开下面两个配置项“topic_notice_online”(对应上线消息)和“topic_notice_offline”(对应下线消息),并为他们分别设置一个参数,这个设置的参数将被作为一个topic,也可以将这两个topic参数设置成一样,这样,上下线消息都会发送到同一个topic上;在有连接建立或断开时mosquitto将向这两个topic发送消息,

【消息格式说明】mosquitto向下面这两个配置topic发送的上下线消息为JSON字符串,共包含三个字段:

clientid:当前通知所涉及的连接ID;

type:连接的状态:1:连接建立;0:连接断开;

time:当前系统时间,1970年1月1日到现在的时间;

例如:

{

"clientid","clientId-test-001",

"type","1",

"time",""

}

topic_notice_online $SYS/NOTICE/STATUS/ONLINE

topic_notice_offline $SYS/NOTICE/STATUS/OFFLINE

(2)直接向Mosquitto查询某个设备当前是否在线?
【功能说明】查询指定连接ID是否在线,返回JSON格式字符串,JSON格式与新增功能2一致:1:在线;0:不在线;

【使用方式】开启下面配置,该配置将指定一个topic,任何一个客户端只要向这个topic发布一个连接ID(即pub过来的消息内容就是要查询的连接ID),mosquitto就会给这个当前pub消息的客户端回复一条消息,查询的客户端无需订阅任何topic,只要向这里配置的topic发布连接ID,就能收到mosquitto发布过来的查询结果。

topic_query_conn_status client/query/connection

这种方式的缺点是:这里Mosquitto自动感知的是连接状态,为了让连接和设备关联起来,需要使用设备标识作为连接标识,由于MQTT协议规定连接ID不能大于23个字符,否则mqtt broker就会拒绝该连接,因此设备id长度大于23个字符时此方式不可用。

 

关于遗嘱机制的说明:

MQTT协议规定,遗嘱消息只在异常断开时才发送,相关描述为:

The Will Message defines the content of the message that is published to the Will Topic if the client is unexpectedly disconnected.

 

在Mosquitto源码里(以1.4.11版本为例),其遗嘱发送在函数mqtt3_context_disconnect中发送,在发送之前会判断:ctxt->state != mosq_cs_disconnecting,正常断开连接的时候,该state将被在喊上mqtt3_handle_disconnect中设置为:mosq_cs_disconnecting,函数mqtt3_handle_disconnect是在收到MQTT的DISCONNECT报文后调用。

--转载请注明: http://91o.cc/mosquitto%e6%84%9f%e7%9f%a5%e5%ae%a2%e6%88%b7%e7%ab%af%e4%b8%8a%e4%b8%8b%e7%ba%bf%e7%9a%84%e6%96%b9%e6%b3%95-mqtt-%e9%81%97%e5%98%b1%e6%b6%88%e6%81%af%ef%bc%88will-message%ef%bc%89%e7%9a%84%e4%bd%bf/