使用示例
// ************************************************************// ************************************************************auto heartBeat = new HeartBeat; // 新建一个心跳heartBeat->setTimeGap( 3.0f ) ->setTimeOut( 5.0f );// 当heart beat计时超过TimeGap,状态切换为Checking,触发onGapTimeOut。// 如果没有及时reset,计时超过timeOut,则状态切换为Broken,触发onHeartBroken,表示确定没有心跳了heartBeat->onGapTimeOut = [&](){ // 表示socket一直没有收到消息,已经超时 // 一般地,我们应该在这里主动发送一个心跳包 CCLOG( "gap time out, we should send a heart beat" );};heartBeat->onHeartBroken = [&](){ // 表示接收心跳包超时,这时我们一般会主动关闭连接,并多次尝试重连 // 如果还是重连失败,则意味着无法访问当前网络。 CCLOG( "heart beat broken. there's problem with the network." );};// ************************************************************// ************************************************************WebSocket* websocket = new WebSocket();websocket->init(......); // 发起连接websocket.onOpen = [](){ // 连接成功 heartBeat->start(); // 心跳开始计时};websocket.onMessage = [](){ // 收到消息 heartBeat->reset(); // 收到一个消息,表示网络正常,重置心跳的计时时间和状态};websocket.onError = [](){ // 消息出错};websocket.onClose = [](){ // 消息出错 heartBeat->reset(); // 链接关闭,停止心跳};
HeartBeat.h
class HeartBeat : public cocos2d::Ref{ enum Status { STOP, Beating, Checking, Broken, DEFAULT = STOP, };public: HeartBeat(); ~HeartBeat(); HeartBeat* start(); HeartBeat* stop(); HeartBeat* reset(); HeartBeat* setTimeGap( const float &timegap ); HeartBeat* setTimeOut( const float &timeout ); std::function<void()> onGapTimeOut; std::function<void()> onHeartBroken;private: void update( float dt );private: float _timeGap; float _timeOut; float _lastBeatInterval; Status _status;};
HeartBeat.cpp
HeartBeat::HeartBeat() : _lastBeatInterval( -1 ), _status( Status::DEFAULT ), _timeGap( 5.0f ), _timeOut( 5.0f ){ cocos2d::Director::getInstance()->getScheduler()->schedule( CC_SCHEDULE_SELECTOR( HeartBeat::update ), this, 1.0f, // 每秒执行1次 CC_REPEAT_FOREVER, // 一直重复 0.0, // 0.0s后开始执行第1次 true ); // 初始状态是pause}HeartBeat::~HeartBeat(){ _lastBeatInterval = 0.0f; _status = STOP; cocos2d::Director::getInstance()->getScheduler()->unschedule( CC_SCHEDULE_SELECTOR( HeartBeat::update ), this );}HeartBeat* HeartBeat::start(){ if( _status != Beating ) { _status = Beating; _lastBeatInterval = 0.0f; cocos2d::Director::getInstance()->getScheduler()->resumeTarget( this ); } return this;}HeartBeat* HeartBeat::stop(){ if( _status != STOP ) { _status = STOP; _lastBeatInterval = 0.0f; cocos2d::Director::getInstance()->getScheduler()->pauseTarget( this ); } return this;}HeartBeat* HeartBeat::reset(){ switch( _status ) { case HeartBeat::Beating: case HeartBeat::Checking: { _lastBeatInterval = 0.0f; _status = Beating; } break; } return this;}HeartBeat* HeartBeat::setTimeGap( const float &timegap ){ _timeGap = timegap; return this;}HeartBeat* HeartBeat::setTimeOut( const float &timeout ){ _timeOut = timeout; return this;}void HeartBeat::update( float dt ){ switch( _status ) { case Beating: { if( _lastBeatInterval + dt > _timeGap ) { _status = Checking; _lastBeatInterval = 0.0f; if( onGapTimeOut ) { onGapTimeOut(); } } else { _lastBeatInterval += dt; } } break; case Checking: { if( _lastBeatInterval + dt > _timeOut ) { _status = Broken; _lastBeatInterval = 0.0f; if( onHeartBroken ) { onHeartBroken(); } } else { _lastBeatInterval += dt; } } break; default: break; }}