目录
定时器
websocketpp库提供了定时器接口set_timer
cpp
timer_ptr set_timer(long duration, timer_handler callback);
- duration为定时的毫秒数
- callback为到时间时触发的定时任务
其中,callback的类型timer_handler如下:
cpp
using timer_handler =
std::function<void(websocketpp::lib::error_code)>;
例如,想要在5000ms后执行一个输出任务:
cpp
int main()
{
websocketpp::server<websocketpp::config::asio> server;
server.init_asio();
server.set_timer(5000, [](const websocketpp::lib::error_code &err)
{ std::cout << "无敌" << std::endl; });
server.run();
return 0;
}
error_code是干什么的?
在设置定时器后, set_timer返回一个timer_ptr 定时器对象,通过它还可以取消定时任务 (这里的取消只是取消定时 ,没有取消任务,相当于立即执行任务)
cpp
int main()
{
websocketpp::server<websocketpp::config::asio> server;
server.init_asio();
auto timer_ptr = server.set_timer(5000, [](const websocketpp::lib::error_code &err)
{ std::cout << "无敌" << std::endl; });
timer_ptr->cancel(); // 取消定时任务
server.run();
return 0;
}
但这样取消后会立刻进入回调Lambda ,但此时该回调的 error_code 就不为0了,因此可以加个判断:
cpp
int main()
{
websocketpp::server<websocketpp::config::asio> server;
server.init_asio();
auto timer_ptr = server.set_timer(5000, [](const websocketpp::lib::error_code &err)
{ if(err) // 错误码被设置
return;
std::cout << "无敌" << std::endl; });
timer_ptr->cancel(); // 取消定时任务
server.run();
return 0;
}
这样当取消定时任务后,就不会执行回调函数内的语句了
Session类
定义一个Session
cpp
enum class Session_Status
{
UNLOGIN,
LOGIN
};
class Session
{
using websocket_server = websocketpp::server<websocketpp::config::asio>;
public:
Session(uint32_t ssid) : _ssid(ssid) { LOG(LogLv::DEBUG, "Session[%p]被创建", this); }
~Session() { LOG(LogLv::DEBUG, "Session[%p]被释放", this); }
void Set_User(int uid) { _uid = uid; } // 设置该Session的用户
void Set_Statu(Session_Status statu) { _ss_statu = statu; }; // 设置Session状态
uint32_t Get_Ssid() { return _ssid; } // 获取 Session ID
int Get_User() { return _uid; } // 获取 Session用户
bool Is_Login() { return _ss_statu == Session_Status::LOGIN; } // 判断该用户是否已登录
void Set_Timer(const websocket_server::timer_ptr &timer) { _timer = timer; } // 设置Session定时任务
websocket_server::timer_ptr &Get_Timer() { return _timer; } // 获取定时器
private:
uint32_t _ssid; // Session ID
int _uid; // 用户id
Session_Status _ss_statu; // 用户状态 (登录 / 未登录)
websocket_server::timer_ptr _timer; // session关联的定时器
};
Session管理类
对于多个Session,需要管理起来,因此这里要有一个 Session 管理类
其中,每个Session用智能指针管理
cpp
using session_ptr = std::shared_ptr<Session>;
class Session_Manager
{
using websocket_server = websocketpp::server<websocketpp::config::asio>;
public:
Session_Manager(websocket_server *server) : _next_ssid(1), _server(server)
{
LOG(LogLv::DEBUG, "Session管理器初始化成功");
}
~Session_Manager() { LOG(LogLv::DEBUG, "Session管理器即将被销毁"); }
session_ptr Create_Session(uint32_t uid, Session_Status statu) // 创建session
{
std::lock_guard<std::mutex> lock(_mutex); // 加锁保证线程安全
session_ptr ssp(new Session(_next_ssid)); // 创建一个Session对象
ssp->Set_User(uid); // 设置该Session的用户
ssp->Set_Statu(statu); // 设置该Session的状态
_sessions.insert(std::make_pair(_next_ssid, ssp)); // 建立起ssid与Session的映射
_next_ssid++; // 下一次创建Session时就用下一个值了
return ssp;
}
void Remove_Session(uint32_t ssid) // 删除session
{
std::lock_guard<std::mutex> lock(_mutex); // 加锁保证线程安全
_sessions.erase(ssid);
}
session_ptr Get_Session_FromSsid(uint32_t ssid)
{
std::lock_guard<std::mutex> lock(_mutex); // 加锁保证线程安全
auto it = _sessions.find(ssid);
if (it == _sessions.end()) // 如果没有该session,返回空智能指针
return session_ptr();
return it->second;
}
void Set_Session_ExpireTime(uint32_t ssid, int ms) // 依赖于websocketpp的定时器完成Session生命周期管理
// 在登录后创建Session,在指定时间无通信后删除
{
// 进入游戏大厅/房间时,Session永久存在
// 退出游戏大厅/房间时,Session就要开始计时
session_ptr ssp = Get_Session_FromSsid(ssid);
if (ssp.get() == nullptr) // 根本没有这个Session(你只是怕了。。)
return;
websocket_server::timer_ptr timer = ssp->Get_Timer();
// 1. 在Session永久存在的情况下,设置永久
if (timer.get() == nullptr && ms == SESSION_FOREVER)
return;
// 2. 在Session永久存在的情况下,设置定时
if (timer.get() == nullptr && ms != SESSION_FOREVER)
{
websocket_server::timer_ptr tmp_tp = // 创建一个定时删除任务
_server->set_timer(ms, [this, ssid](const websocketpp::lib::error_code &err)
{ if(err) return; // 当主动取消(cancel)时,不会执行删除
this->Remove_Session(ssid); });
ssp->Set_Timer(tmp_tp); // 绑定 定时器 到 目标Session
return;
}
// 3. 在Session设置定时的情况下,设置永久
if (timer.get() != nullptr && ms == SESSION_FOREVER)
{
// 取消原来的定时任务
// 取消后,其回调会被调用,并收到 operation_aborted
timer->cancel();
ssp->Set_Timer(websocket_server::timer_ptr()); // 将该会话的定时器清除
return;
}
// 4. 在Session设置定时的情况下,重置定时
if (timer.get() != nullptr && ms != SESSION_FOREVER)
{
// 删除原先的定时任务,这会导致立即执行回调,但回调函数内设置了对err的判断,不会删除映射
timer->cancel();
websocket_server::timer_ptr tmp_tp = // 创建一个新定时删除任务
_server->set_timer(ms, [this, ssid](const websocketpp::lib::error_code &err)
{ if(err) return; // 当主动取消(cancel)时,不会执行删除
this->Remove_Session(ssid); });
ssp->Set_Timer(tmp_tp); // 绑定 定时器 到 目标Session
}
return;
}
private:
uint32_t _next_ssid; // 下一个创建的session的id
std::mutex _mutex; // 保证线程安全
std::unordered_map<uint32_t, session_ptr> _sessions; // session id 与 session会话指针的映射
websocket_server *_server; // 用于创建定时器任务
};