且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

我的mqtt协议和emqttd开源项目个人理解(25) - 协议里面Clean Session为0和1的区别

更新时间:2022-06-13 19:32:15

一、基本概念


Session 会话


定义


定义:某个客户端(由ClientID作为标识)和某个服务器之间的逻辑层面的通信

生命周期(存在时间):会话 >= 网络连接

CleanSession 标记

在Connect时,由客户端设置

0 —— 开启会话重用机制。网络断开重连后,恢复之前的Session信息。需要客户端和服务器有相关Session持久化机制。

1 —— 关闭会话重用机制。每次Connect都是一个新Session,会话仅持续和网络连接同样长的时间。



客户端 Session

已经发送给服务端,但是还没有完成确认的 QoS 1 和 QoS 2 级别的消息

已从服务端接收,但是还没有完成确认的 QoS 2 级别的消息



服务器端 Session

会话是否存在,即使会话状态的其它部分都是空  (SessionFlag)

客户端的订阅信息  (ClientSubcription)

已经发送给客户端,但是还没有完成确认的 QoS 1 和 QoS 2 级别的消息

即将传输给客户端的 QoS 1 和 QoS 2 级别的消息

已从客户端接收,但是还没有完成确认的 QoS 2 级别的消息

(可选)准备发送给客户端的 QoS 0 级别的消息


二、关于协议的问答


问:问个mqtt协议的问题,cleansession为什么要区别0和1,他们各自的用途和应用场景是什么?


答:为 1 的时候,服务器每次session 都要重新建立,这也是大多数的场景使用情况;


为 0 的时候可以复用历史的session, 服务端session 是有状态的,可以记录很多信息。


为 0的时候就可以保留 session 了,这个保留的session 是有用的,如果服务端实现了的话,像历史没发出去的消息等等,鉴权信息,等等,可以接着使用。你可以类比我们登陆网页的那个session,没过期的话,可以不用登陆了。


说白了,想接收离线消息,就必须使用cleansession=0,这个就是=0的应用场景:


不管clean session的值是什么,当终端设备离线时,QoS=0,1,2的消息一律接收不到。

当clean session的值为1,当终端设备离线再上线时,离线期间发来QoS=0,1,2的消息一律接收不到。

当clean session的值为0,当终端设备离线再上线时,离线期间发来QoS=0,1,2的消息仍然可以接收到。如果同个主题发了多条就接收多条,一条不差,照单全收。


三、emq源码里面,hook上下线流程


1、emq v2当服务器进程重启之后,session会清除掉,没有实现本地持久化。


2、cleansession为0和1的时候,hook里面的上下线,0和1流程大不相同。相同clientid冲突登录。


以下的打印信息来自hook插件源码文件

emqttd:hook('client.connected', fun ?MODULE:on_client_connected/3, [Env]),

emqttd:hook('client.disconnected', fun ?MODULE:on_client_disconnected/3, [Env]),

emqttd:hook('session.created', fun ?MODULE:on_session_created/3, [Env]),

emqttd:hook('session.terminated', fun ?MODULE:on_session_terminated/4, [Env]),


(1)、cleansession==1的情况

场景1,客户端自身上线下线

正常上线

session(firecat_heartbeat001/firecatGTerm) created.

client firecat_heartbeat001 connected, connack: 0

正常下线

client firecat_heartbeat001 disconnected, reason: normal

session(firecat_heartbeat001/firecatGTerm) terminated: normal.


场景2,客户端先正常上线,后相同clientid客户端在别的地方又上线,造成clientid冲突

先正常上线

session(firecat_heartbeat001/firecatGTerm) created.

client firecat_heartbeat001 connected, connack: 0

后冲突上线

session(firecat_heartbeat001/firecatGTerm) terminated: {shutdown,conflict}.

session(firecat_heartbeat001/firecatGTerm) created.

client firecat_heartbeat001 connected, connack: 0

注意,冲突上线,后者会把前者挤掉,但是前者不会触发离线函数,仅仅把session毁掉重建而已.

后者正常下线

client firecat_heartbeat001 disconnected, reason: normal

session(firecat_heartbeat001/firecatGTerm) terminated: normal.



(2)、cleansession==0的情况

场景1,客户端自身第一次上线下线

正常上线

session(firecat_heartbeat001/firecatGTerm) created.

client firecat_heartbeat001 connected, connack: 0

正常下线

client firecat_heartbeat001 disconnected, reason: normal

注意,这里不销毁session

最终,session会有过期时间,时间到会销毁,时间(貌似2个小时?)详见emqttd_session.erl

session(firecat_heartbeat001/firecatGTerm) terminated: {shutdown,expired}.


场景2,客户端自身第N次(N>1)上线下线

正常上线

注意,这里不创建session

client firecat_heartbeat001 connected, connack: 0

正常下线

client firecat_heartbeat001 disconnected, reason: normal

注意,这里不销毁session

最终,session会有过期时间,时间到会销毁,时间详见emqttd_session.erl

session(firecat_heartbeat001/firecatGTerm) terminated: {shutdown,expired}.


场景3,客户端先正常上线,后相同clientid客户端在别的地方又上线,造成clientid冲突

先正常上线

session(firecat_heartbeat001/firecatGTerm) created.

client firecat_heartbeat001 connected, connack: 0

后冲突上线

client firecat_heartbeat001 connected, connack: 0

注意,冲突上线,后者会把前者挤掉,但是前者不会触发离线函数,session也不毁掉不重建.

后者正常下线

client firecat_heartbeat001 disconnected, reason: normal

注意,这里不销毁session

最终,session会有过期时间,时间到会销毁,时间详见emqttd_session.erl

session(firecat_heartbeat001/firecatGTerm) terminated: {shutdown,expired}.