android 网络连接保活

转载自:
http://www.infoq.com/cn/articles/wechat-android-background-keep-alive
http://www.52im.net/thread-341-1-1.html
在开发某些安卓应用中,如何做到客户端的后台保活是很重要的。保活,按照我们的理解包含两部分:
1、网络连接保活:如何保证消息接收的实时性
2、进程保活:尽量保证应用的进程不被android系统回收。

网络连接保活

网络保活,业界的主要手段有:

  • GCM
  • 公共的第三方push通道(信鸽等)
  • 自身跟服务器通过轮询
  • 长连接

国产机器大多缺乏GMS,在国内GCM也不稳定(心跳原因),第三方通道需要考虑安全问题和承载能力。

轮询

轮询就是指客户端定期询问服务器有没有新的消息,这样服务器不用管客户端的地址是什么,客户端来问,直接告诉他就行。这种方案对于一些不追求实时性的客户端来说,很合适,只需要把时间间隔设定成几个小时取一次,就能很方便的解决问题。但是对于即时通讯产品来说,这种方案完全不能用,假设及时通讯软禁啊在网络畅通的情况下发送的消息要求对方10s就能收到,如果用轮询,那么客户端要每个5s连一次服务器,如果在移动端,手机的电量和流量很快就会被消耗殆尽。

长连接

最后微信选择使用自己的长连接。客户端主动和服务器建立TCP长连接之后,客户端定期向服务器发送心跳包,有消息的时候,服务器直接通过这个已经建立好的TCP连接通知客户端。
而国外,GCM作为辅助,微信无法建立长连接时,才使用GCM。

什么是长连接

先说短连接,短连接是通讯双份有数据交互时就建立一共连接,数据发送完成后,则断开此连接。
android 网络连接保活_第1张图片
长连接就是大家建立连接之后,不主动断开,双方互相发送数据,发完了也不主动断开连接,之后有需要发送的数据就继续通过这个连接发送。

TCP连接在默认的情况下就是所谓的长连接,也就是说连接双方都不主动关闭连接,这个连接就应该一直存在。
但是网络中的情况是复杂的,这个连接可能会被切断,比如客户端到服务器的链路因为故障断了,服务器宕机了等因素。

NAT超时

因为IPV4地址不足,或者我们想通过无线路由器上网,我们的设备可能会处在NAT设备的后面,NAT设备会在op封包通过设备时修改源/目的ip地址,这样就能让内网中的设备共用一个外网ip。对于家用路由器来说,使用的是网络地址端口转换(NAPT),他不仅改ip,还修改TCP和UDP协议的端口号,这样就能让内网中的设备共用一个外网ip,举个例子,NAPY维护一个类似下面的NAT表:
android 网络连接保活_第2张图片

国内移动无线网络运营商在链路上一段时间内没有数据通信后,会淘汰NAT表中的对应项,造成链路中断。

长连接的实现包括几个要素:

  • 网络切换或者初始化时server ip的获取。
  • 连接前的ip筛选,出错后的ip的抛弃
  • 维护长连接的心跳
  • 服务器通过长连notify
  • 选择使用长连通道的业务
  • 断开后重连的策略

本文重点讨论一下心跳和notify机制。

心跳机制

心跳的目的就是:通过定期的数据包,对抗NAT超时。以下是部分地区网络NAT超时统计:
android 网络连接保活_第3张图片

上表说明:
- GCM无法适应国内2G环境(GCM28分钟心跳)
- 为了兼容国内网络要求,我们至少5分钟心跳一次

老版本的微信是4.5分钟发送一次心跳,运行良好。

心跳的实现

android 网络连接保活_第4张图片

  • 连接后主动到服务器Sync拉取一次数据,确保连接过程的新消息。
  • 心跳周期的Alarm唤醒后,一般有几秒的cpu时间,无需wakelock。
  • 心跳后的Alarm是为了防止发送超时,如服务器正常会包,该Alarm取消。
  • 如果心跳后发送超时了,那么要和服务器重新建立连接拉取数据
  • 如果服务器回包,系统通过网络唤醒,无需wakelock。

流程基于两个系统特性:

  • Alarm唤醒后,足够cpu时间发包。
  • 网络回包可唤醒机器

特别是第二项,假如android封堵该特性,那就只能用GCM了。API level >= 23的doze就关闭所有的网络, alarm等。但进入doze条件苛刻,现在6.0普及低,至今微信没收到相关投诉。另Google也最终加入REQUEST_IGNORE_BATTERY_OPTIMIZATIONS权限。

动态心跳

4.5分钟的心跳周期是稳定可靠的,但是无法确定是最大值。通过终端尝试,可以获取到特定用户网络下,心跳的最大值。
引入该特性的背景:

  • 运营商的信令风暴
  • 运营商网络换代,NAT超时趋于增大
  • Alarm耗电,心跳耗流量

动态心跳引入下列状态:

  • 前台活跃态:亮屏,微信在前台,周期为minHeart(4.5min),保证体验
  • 后台活跃态:微信在后台10分钟内,周期为minHeart(4.5min),保证体验
  • 自适应计算态:步增心跳,尝试获取最大心跳周期(sucHeart)
  • 后台稳定态:通过最大周期,保持稳定心跳

自适应计算态流程:
android 网络连接保活_第5张图片

在自适应态:

  • curHeart初始值为minHeart,步增(heartStep)为1分钟
  • curHeart失败5次,意味着整个自适应态最多只有5分钟无法接受消息
  • 结束后,如果sucHeart > minHeart,会减去10s(避开临界),为该网络下的稳定周期

稳定态的退出:
sucHeart会对应网络存储下来,重启后正常使用。考虑到网络的不稳定,如NAT超时变小,用户地理位置变换。当发现sucHeart连续失败5次,sucHeart置为minHeart,重新进入自适应状态。

notify机制

网络保活的意义在于消息实时。通过长连接,微信有以下机制保证消息实时:

Sync

通过Sync CGI直接请求后台数据,SYnc通过后台和终端的seq值对比,判断该下发哪些消息。终端正常处理消息后,seq更新为最新值。

  • 长连无法建立时,通过Sync定期轮询
  • 微信切换到前台时,出发Sunc(保命机制)
  • 长连接建立完成后,立即出发Sync,防止连接过程漏消息
  • 接收到Notify或者GCM之后,终端触发Sync接收消息

    Notify

    类似于GCM。通过长连接,后台发出仅带seq的小包,终端根据seq决定是否出发Sync拉取消息。

    NotifyData

    在长连接稳定之后,Notify机制正常的情况下(保证seq的同步)后台直接推送消息内容,节省1个RTT(Sync)消息的接收时间。终端收到内容后,带上seq辉映NotifyAck,确认成功。这里会出现Notify和Notifydata状态互相切换的情况。
    如NotifyData后,服务器在没有收到NorifyAck,而有新消息的情况下,会切换回Notify。Sync可能需要冗余之前NotifyData的消息。终端要保证串行处理NotifyData和Sync,否则seq可能会回退。
    GCM:只要机器上有GMS,启动时就尝试注册GCM,并通知后台。服务器会根据终端是否保持长连,决定是否由GCM通知。GCM主要针对国外比较复杂的网络环境。

你可能感兴趣的:(android)