GB/T 28181-2016心跳机制
通过周期性的状态信息报送,实现注册服务器与源设备之间的状态检测即心跳机制。
心跳发送方、接收方需统一配置“心跳间隔”参数,按照“心跳间隔”定时发送心跳消息,默认心跳间隔60s。心跳发送方、接收方需统一配置“心跳超时次数”参数,心跳消息连续超时达到“心跳超时次数”则认为对方下线,默认心跳超时次数3次。
心跳接收方在心跳发送方上线状态态下检测到心跳消息连续超时达到商定次数则认为心跳发送方离线;心跳发送方在心跳接收方上线状态下检测到心跳消息响应消息连续超时达到商定次数则认为心跳接收方离线。
状态信息流程如下:
具体技术实现
本文以我们实现的Android平台GB28181国标接入模块为例,探讨下大概实现。
相关参数定义:
private int gb28181_reg_expired_ = 3600; // 注册有效期时间最小3600秒 private int gb28181_heartbeat_interval_ = 20; // 心跳间隔GB28181默认是60, 目前调整到20秒 private int gb28181_heartbeat_count_ = 3; // 心跳间隔3次失败,表示和服务器断开了
设置到GB28181实现层:
// GB28181配置 gb28181_agent_.config(gb28181_reg_expired_, gb28181_heartbeat_interval_, gb28181_heartbeat_count_);
MESSAGE组成:
MESSAGE sip:34020000002000000001@3402000000 SIP/2.0 Via: SIP/2.0/TCP 192.168.0.120:60771;rport;branch=z9hG4bK741568174 From: <sip:34020000002000000005@3402000000>;tag=790237397 To: <sip:34020000002000000001@3402000000> Call-ID: 282416456 CSeq: 20 MESSAGE Content-Type: Application/MANSCDP+xml Max-Forwards: 70 User-Agent: IP Camera Content-Length: 176 <?xml version="1.0" encoding="GB2312"?> <Notify> <CmdType>Keepalive</CmdType> <SN>2</SN> <DeviceID>34020000002000000005</DeviceID> <Status>OK</Status> <Info> </Info> </Notify>
底层针对异常的处理,如果超过设定的异常次数,状态回调上去,便于上层做进一步处理:
int count = mExcepitonCount.incrementAndGet(); if ( count >= mConfig.getHeartBeatCount() ) { Log.e(GBDefine.LOGTAG, "KeepAlive.sendRequest() KeepAlive reached HeartBeatTimeoutCount:" + mConfig.getHeartBeatCount() + ", notify listener"); GBSIPAgentListener listener = this.getListener(); if ( listener != null) { listener.ntsOnHeartBeatException(count, e.getMessage()); } }
上层收到底层发过来的心跳异常回调,并做相应的处理:
/* * CameraPublishActivity.java * CameraPublishActivity * 针对心跳异常处理 * * Github: https://github.com/daniulive/SmarterStreaming */ @Override public void ntsOnHeartBeatException(int exceptionCount, String lastExceptionInfo) { Log.e(TAG, "ntsOnHeartBeatException heart beat timeout count reached, count:" + exceptionCount+ ", exception info:" + (lastExceptionInfo!=null?lastExceptionInfo:"")); // 10毫秒后,停止信令, 然后重启 handler.postDelayed(new Runnable() { @Override public void run() { Log.i(TAG, "gb28281_heart_beart_timeout"); stopGB28181Stream(); destoryRTPSender(); if (gb28181_agent_ != null) { Log.i(TAG, "gb28281_heart_beart_timeout sip stop"); gb28181_agent_.stop(); Log.i(TAG, "gb28281_heart_beart_timeout sip start"); gb28181_agent_.start(); } } },10); }
感兴趣的开发者,可以自行参考实现。