1、程序初始化的时候: init_proxy_conn(thread_num); 在其中注册一个定时器函数:netlib_register_timer(proxy_timer_callback, NULL, 1000);
2、然后在消息泵里面检测定时器:_CheckTimer(); 在其中遍历 m_timer_list 找出到达定时时间的定时器:curr_tick >= pItem->next_tick,更新其next_tick并调用回调: pItem->callback(pItem->user_data, NETLIB_MSG_TIMER, 0, NULL);,此处的回调 callback 即是初始化时注册的 proxy_timer_callback。
其中 curr_tick=get_tick_count(); 是获取当前时间:
uint64_t get_tick_count()
{
#ifdef _WIN32
LARGE_INTEGER liCounter;
LARGE_INTEGER liCurrent;
if (!QueryPerformanceFrequency(&liCounter))
return GetTickCount();
QueryPerformanceCounter(&liCurrent);
return (uint64_t)(liCurrent.QuadPart * 1000 / liCounter.QuadPart);
#else
struct timeval tval;
uint64_t ret_tick;
gettimeofday(&tval, NULL);
ret_tick = tval.tv_sec * 1000L + tval.tv_usec / 1000L;
return ret_tick;
#endif
}
3、在回调 proxy_timer_callback 中遍历 g_proxy_conn_map 对各个连接对象 CProxyConn* pConn 调用 pConn->OnTimer(cur_time);
void CProxyConn::OnTimer(uint64_t curr_tick)
{
if (curr_tick > m_last_send_tick + SERVER_HEARTBEAT_INTERVAL) {
CImPdu cPdu;
IM::Other::IMHeartBeat msg;
cPdu.SetPBMsg(&msg);
cPdu.SetServiceId(IM::BaseDefine::SID_OTHER);
cPdu.SetCommandId(IM::BaseDefine::CID_OTHER_HEARTBEAT);
SendPdu(&cPdu);
}
if (curr_tick > m_last_recv_tick + SERVER_TIMEOUT) {
log("proxy connection timeout %s:%d", m_peer_ip.c_str(), m_peer_port);
Close();
}
}
m_last_send_tick是上一次发送数据的时间,如果当前时间距上一次发送数据的时间已经超过了指定的时间间隔,则发送一个心跳包(这里的时间间隔是5000毫秒)。
m_last_recv_tick是上一次收取数据的时间,如果当前时间距离上一次接收时间已经超过了指定的时间间隔(相当于一段时间内,对端没有给当前服务发送任何数据),这个时候就关闭该连接(这里设置的时间间隔是30000毫秒,也就是30秒)。
这种心跳包机制特别值得推崇,也是常见的心跳包策略。