一、基本概念
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}.