session
在WEB开发中,HTTP协议是⼀种⽆状态短链接的协议,这就导致⼀个客⼾端连接到服务器上之后,服务器不知道当前的连接对应的是哪个用户,也不知道客⼾端是否登录成功,这时候为客⼾端提所有服务是不合理的。因此,服务器为每个用户浏览器创建⼀个会话对象(session对象),注意:⼀个浏览器独占⼀个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使⽤浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,识别该连接对应的用户,并为用户提供服务。
这⾥简单设计⼀个session类,但是session对象不能⼀直存在,这样是⼀种资源泄漏,因此需要使⽤定时器对每个创建的session对象进⾏定时销毁(⼀个客⼾端连接断开后,⼀段时间内都没有重新连接则销毁session)。
session类
对于一个用户独占的session来说,需要有session的一个id,也就是标识符,需要对于的用户id,登录状态,以及session需要用到的定时器。
session类需要提供设置session状态,设置用户id和定时器的方法,需要提供给外界获取session标识符,用户id和定时器的方法,需要提供判断session是否处于登录状态的方法。
typedef enum {UNLOGIN, LOGIN} ss_statu;
class session
{
private:
uint64_t _ssid;//session标识符
uint64_t _uid;//用户id
ss_statu _statu;//用户状态:未登录,已登录
wsserver_t::timer_ptr _tp;//session关联的定时器
public:
session(uint64_t ssid): _ssid(ssid){ DLOG("SESSION %p 被创建!!", this); }
~session() { DLOG("SESSION %p 被释放!!", this); }
uint64_t ssid(){return _ssid;}/*获取session的标识符*/
uint64_t get_user(){return _uid;}/*获取用户id*/
wsserver_t::timer_ptr& get_timer() { return _tp; }/*获取定时器*/
void set_statu(ss_statu statu){_statu = statu;}/*设置session的状态*/
void set_user(uint64_t uid){_uid = uid;}/*设置session的用户id*/
void set_timer(wsserver_t::timer_ptr& tp){_tp = tp};/*设置定时器*/
bool is_login(){return (_statu == LOGIN);}/*判断是否处于登录状态*/
};
session管理类
将session类实现好之后,接下来就是需要将session统一管理起来了。
在session管理类中,需要对每一个session进行编号,而这个编号,就是session的标识符ssid,是不能乱编,为了避免session编号出现错误,因此采用计数器的方式,并且将session交由智能指针shared_ptr去管理,这样就方便进行操作了。
在session管理类中,主要实现这些方法:
创建session、通过ssid获取session、通过ssid判断session是否存在、销毁session和为session设置过期时间,过期后session被销毁。
using session_ptr = std::shared_ptr<session>;
#define SESSION_TIMEOUT 300000
#define SESSION_FOREVER -1
class session_manager
{
private:
uint64_t _next_ssid;
std::mutex _mutex;
std::unordered_map<uint64_t,session_ptr> _session;
wsserver_t * _server;
public:
session_manager(wsserver_t *srv)
:_next_ssid(1)
,_server(srv)
{
DLOG("session管理器初始化完毕!");
}
~session_manager()
{
DLOG("session管理器即将销毁!");
}
/*创建session*/
session_ptr create_session(uint64_t uid, ss_statu statu)
{
std::unique_lock<std::mutex> lock(_mutex);
/*创建出一个新的session,通过智能指针管理*/
session_ptr ssp(new session(_next_ssid));
/*设置session的状态、用户id*/
ssp->set_statu(statu);
ssp->set_user(uid);
/*将session的智能指针与session的ssid建立映射关系*/
_session.insert(std::make_pair(_next_ssid,ssp));
_next_ssid++;
return ssp;
}
/*将ssp进行管理*/
void append_session(const session_ptr &ssp)
{
std::unique_lock<std::mutex> lock(_mutex);
_session.insert(std::make_pair(ssp->ssid(),ssp));
}
/*通过ssid获取session信息*/
session_ptr get_session_by_ssid(uint64_t ssid)
{
std::unique_lock<std::mutex> lock(_mutex);
auto it = _session.find(ssid);
if(it==_session.end())
{
return session_ptr();
}
return it->second;
}
/*销毁session*/
void remove_session(uint64_t ssid)
{
std::unique_lock<std::mutex> lock(_mutex);
_session.erase(ssid);
}
/*为ssid对应的session设置生命周期*/
void set_session_expire_time(uint64_t ssid, int ms)
{
//依赖于websocketpp的定时器来完成session生命周期的管理。
//登录之后,创建session,session需要在指定时间无通信后删除
// 但是进入游戏大厅,或者游戏房间,这个session就应该永久存在
// 等到退出游戏大厅,或者游戏房间,这个session应该被重新设置为临时,在长时间无通信后被删除
/*首先通过ssid获取session信息*/
session_ptr ssp = get_session_by_ssid(ssid);
if (ssp.get() == nullptr)
{
return;
}
/*获取session中的定时器*/
wsserver_t::timer_ptr tp = ssp->get_timer();
/*根据情况进行设置*/
/*1.在session永久存在的情况下,设置永久存在*/
if(tp.get() == nullptr && ms == SESSION_FOREVER)
{
/*session本身就已经永久存在了*/
return;
}
else if(tp.get() == nullptr && ms != SESSION_FOREVER)
{
/*在session永久存在的情况下,设置指定时间之后被删除的定时任务*/
/*创建出一个临时的定时器,将该定时器设置为:过了ms的时间后,会调用remove_session函数进行session的销毁*/
wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms,std::bind(&session_manager::remove_session,this,ssid));
ssp->set_timer(tmp_tp);/*设置进去*/
}
else if(tp.get() != nullptr && ms == SESSION_FOREVER)
{
//3. 在session设置了定时删除的情况下,将session设置为永久存在
// 删除定时任务--- stready_timer删除定时任务会导致任务直接被执行
tp->cancel();//因为这个取消定时任务并不是立即取消的
//因此重新给session管理器中,添加一个session信息, 且添加的时候需要使用定时器,而不是立即添加
ssp->set_timer(wsserver_t::timer_ptr());//将session关联的定时器设置为空
_server->set_timer(0, std::bind(&session_manager::append_session, this, ssp));
}
else if(tp.get() != nullptr && ms != SESSION_FOREVER)
{
// 4. 在session设置了定时删除的情况下,将session重置删除时间。
tp->cancel();//因为这个取消定时任务并不是立即取消的
ssp->set_timer(wsserver_t::timer_ptr());
_server->set_timer(0, std::bind(&session_manager::append_session, this, ssp));
//重新给session添加定时销毁任务
wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms,
std::bind(&session_manager::remove_session, this, ssp->ssid()));
//重新设置session关联的定时器
ssp->set_timer(tmp_tp);
}
}
};