移动端心跳包
TCP
的心跳机制
TCP
协议,本身拥有一个KeepAlive
机制,既然有了心跳机制,为什么还要在应用层面设计一个心跳包呢?
-
TCP
的KeepAlive
机制的局限, 应用层面的可用不等同于网络层面连接的存活状态TCP
层面的keepalive
存在更多意义上是为了检测两端连接是否正常,注重点是在于连接的本身,默认TCP
的超时时间2小时太长,还会存在一些让keep-alive
失效的场景-
keepAlive
只能检测连接存活,而不能检测连接可用,比如服务器因为负载过高导致无法响应请求但是连接仍然存在,此时keepalive
无法判断连接是否可用 -
如果
TCP
连接的另一端突然掉线,这个时候我们并不知道网络已经关闭。而此时,如果有发送数据失败,TCP
会自动进行重传。重传包的优先级高于keepalive
的包,那就意味着,我们的keepalive
总是不能发送出去。 而此时,我们也并不知道该连接已经出错而中断。在较长时间的重传失败之后,我们才会知道。
-
-
HTTP
的KeepAlive
机制:复用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 v4
的IP
量有限,运营商分配给手机终端的IP
是运营商内网的IP
,手机要连接Internet
,就需要通过运营商的网关做一个网络地址转换(Network Address Translation
,NAT
)。简单的说运营商的网关需要维护一个外网IP
、端口到内网IP
、端口的对应关系,以确保内网的手机可以跟Internet
的服务器通讯。
- 大部分移动无线网络运营商都在链路一段时间没有数据通讯时,会淘汰
NAT
表中的对应项,造成链路中断。
- 长连接心跳间隔必须要小于
NAT
超时时间(aging-time
),如果超过aging-time
不做心跳,TCP
长连接链路就会中断,Server
就无法发送Push
给手机,只能等到客户端下次心跳失败后,重建连接才能取到消息。
- 因为
思考
- 心跳机制保证了连接的可用性,保证推送信息的及时收取,但是为了及时收到信息,保活进程也是一个逃不开的手段,后续分析一下,进程的保活的手段
网友评论