移动端心跳包

移动端心跳包

TCP的心跳机制

TCP协议,本身拥有一个KeepAlive机制,既然有了心跳机制,为什么还要在应用层面设计一个心跳包呢?

  1. TCPKeepAlive机制的局限, 应用层面的可用不等同于网络层面连接的存活状态

    TCP层面的keepalive存在更多意义上是为了检测两端连接是否正常,注重点是在于连接的本身,默认TCP的超时时间2小时太长,还会存在一些让keep-alive失效的场景

    • keepAlive只能检测连接存活,而不能检测连接可用,比如服务器因为负载过高导致无法响应请求但是连接仍然存在,此时keepalive无法判断连接是否可用

    • 如果TCP连接的另一端突然掉线,这个时候我们并不知道网络已经关闭。而此时,如果有发送数据失败,TCP会自动进行重传。重传包的优先级高于keepalive的包,那就意味着,我们的keepalive总是不能发送出去。 而此时,我们也并不知道该连接已经出错而中断。在较长时间的重传失败之后,我们才会知道。

  2. HTTPKeepAlive机制:复用tcp连接

    • HTTP/1.0之前,默认使用短连接,每进行一次HTTP操作,就简历一次连接,但任务结束就中断连接,

    • HTTP/1.1起,默认使用长连接,用以保持连接特性,复用同一个TCP连接,串行的来完成传递请求-响应数据,长连接的优势是节省了创建连接的耗时。

应用层面的HeartBeat设计

客户端开启一个定时任务,定时向已经建立连接的服务端发送请求(心跳包),服务端接收到后,就需要处理该特殊请求,返回响应,如果没有收到心跳响应多次,就认为连接不可用,进行重连

  • 心跳包的设计

    • 定时任务,频率设计(固定,)

    • 连接闲置才保活,避免流量浪费

    • 重试策略

    • 特定场景的触发,点亮屏幕,切换前台保证信息及时收到

    • 心跳间隔要小于NAT超时时间

MQTT Android心跳包 AlarmPingSender

  • 定义了一个心跳包发送接口
  public void init();
  //开发发送心跳包
  public void start();
  //停止发送心跳包,可能是出错,获取连接关闭
  public void stop();
  //下一次心跳包的发送
  public void schedule(long delayInMilliseconds);
  • Android端 心跳包实现类
private MqttService service = mqttService;
    ​
    public void start() {
     String action = "mqtt_heart_beat"
     //注册上对应广播接收器
     service.registerReceiver(alarmReceiver, new IntentFilter(action));
     //准备好 PendingIntent,每次都是定时发送一个广播
     pendingIntent = PendingIntent.getBroadcast(service, 0, 
     new Intent(action), PendingIntent.FLAG_UPDATE_CURRENT);
     //设置好下一次心跳包的发送
     schedule(interval_time);
    }
    //准备好下一次心跳
    public void schedule(long delayInMilliseconds) {
     //下一次心跳时间
     long nextAlarmInMilliseconds = System.currentTimeMillis()
     + delayInMilliseconds;
     //获取alarmManager
     AlarmManager alarmManager = (AlarmManager) service
     .getSystemService(Service.ALARM_SERVICE);

     //AlaramManager的兼容处理 定时任务
     if(Build.VERSION.SDK_INT >= 23){
     alarmManager.setExactAndAllowWhileIdle(
     AlarmManager.RTC_WAKEUP,nextAlarmInMilliseconds,pendingIntent);
     } else if (Build.VERSION.SDK_INT >= 19) {
     alarmManager.setExact(
     AlarmManager.RTC_WAKEUP,nextAlarmInMilliseconds,pendingIntent);
     } else {
     alarmManager.set(                            AlarmManager.RTC_WAKEUP,nextAlarmInMilliseconds,pendingIntent);
     }
     }
    ​

    //停止定时任务
    public void stop() {
     AlarmManager alarmManager = (AlarmManager)service.getSystemService(Service.ALARM_SERVICE);
     alarmManager.cancel(pendingIntent);
    }
    ​
    class AlarmReceiver extends BroadcastReceiver {
     private WakeLock wakelock;

     public void onReceive(Context context, Intent intent) {
     //AlaramManager持有的cpu wake_lock只能保证onReciver方法执行完成,但是我们需要在此处发送心跳包等,所以需要创建一个新的wake_lock
     PowerManager pm = (PowerManager) service
     .getSystemService(Service.POWER_SERVICE);
     wakelock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, wakeLockTag);
     wakelock.acquire();
    ​
     //心跳包处理相关
     IMqttToken token = comms.checkForActivity();
     wakelock.release();

     }
     }
    // ClientComms#checkForActivity -> ClientStat#checkForActivity
    public MqttToken checkForActivity() {
     //将pingComond加入到发送数据流中
     pendingFlows.insertElementAt(pingCommand, 0);
     //获取下次心跳包时间
     nextPingTime = getKeepAlive();
     //准备下一次心跳包的发送
     pingSender.schedule(nextPingTime);
    }

附录:

  • NAT超时

    • 因为 IP v4IP量有限,运营商分配给手机终端的 IP是运营商内网的IP,手机要连接Internet,就需要通过运营商的网关做一个网络地址转换(Network Address TranslationNAT)。简单的说运营商的网关需要维护一个外网IP、端口到内网 IP、端口的对应关系,以确保内网的手机可以跟Internet的服务器通讯。
    • 大部分移动无线网络运营商都在链路一段时间没有数据通讯时,会淘汰 NAT表中的对应项,造成链路中断。
    • 长连接心跳间隔必须要小于NAT超时时间(aging-time),如果超过aging-time不做心跳,TCP长连接链路就会中断,Server就无法发送Push给手机,只能等到客户端下次心跳失败后,重建连接才能取到消息。

思考

  • 心跳机制保证了连接的可用性,保证推送信息的及时收取,但是为了及时收到信息,保活进程也是一个逃不开的手段,后续分析一下,进程的保活的手段

参考链接

  • eclipse paho Mqtt心跳机制

  • 为什么说基于TCP的移动端IM仍然需要心跳保活?

  • 移动端IM实践:实现Android版微信的智能心跳机制1

你可能感兴趣的:(移动端心跳包)