移動(dòng)端心跳包
TCP
的心跳機(jī)制
TCP
協(xié)議,本身擁有一個(gè)KeepAlive
機(jī)制蔓倍,既然有了心跳機(jī)制,為什么還要在應(yīng)用層面設(shè)計(jì)一個(gè)心跳包呢?
-
TCP
的KeepAlive
機(jī)制的局限, 應(yīng)用層面的可用不等同于網(wǎng)絡(luò)層面連接的存活狀態(tài)TCP
層面的keepalive
存在更多意義上是為了檢測兩端連接是否正常贯被,注重點(diǎn)是在于連接的本身,默認(rèn)TCP
的超時(shí)時(shí)間2小時(shí)太長,還會(huì)存在一些讓keep-alive
失效的場景keepAlive
只能檢測連接存活,而不能檢測連接可用,比如服務(wù)器因?yàn)樨?fù)載過高導(dǎo)致無法響應(yīng)請(qǐng)求但是連接仍然存在暗膜,此時(shí)keepalive
無法判斷連接是否可用如果
TCP
連接的另一端突然掉線映挂,這個(gè)時(shí)候我們并不知道網(wǎng)絡(luò)已經(jīng)關(guān)閉。而此時(shí)藏鹊,如果有發(fā)送數(shù)據(jù)失敗皮假,TCP
會(huì)自動(dòng)進(jìn)行重傳。重傳包的優(yōu)先級(jí)高于keepalive
的包衩椒,那就意味著蚌父,我們的keepalive
總是不能發(fā)送出去哮兰。 而此時(shí),我們也并不知道該連接已經(jīng)出錯(cuò)而中斷苟弛。在較長時(shí)間的重傳失敗之后喝滞,我們才會(huì)知道。
-
HTTP
的KeepAlive
機(jī)制:復(fù)用tcp
連接HTTP/1.0
之前,默認(rèn)使用短連接,每進(jìn)行一次HTTP
操作,就簡歷一次連接,但任務(wù)結(jié)束就中斷連接,HTTP/1.1
起,默認(rèn)使用長連接,用以保持連接特性,復(fù)用同一個(gè)TCP
連接,串行的來完成傳遞請(qǐng)求-響應(yīng)數(shù)據(jù),長連接的優(yōu)勢是節(jié)省了創(chuàng)建連接的耗時(shí)膏秫。
應(yīng)用層面的HeartBeat
設(shè)計(jì)
客戶端開啟一個(gè)定時(shí)任務(wù),定時(shí)向已經(jīng)建立連接的服務(wù)端發(fā)送請(qǐng)求(心跳包),服務(wù)端接收到后,就需要處理該特殊請(qǐng)求,返回響應(yīng),如果沒有收到心跳響應(yīng)多次,就認(rèn)為連接不可用,進(jìn)行重連
-
心跳包的設(shè)計(jì)
定時(shí)任務(wù),頻率設(shè)計(jì)(固定,)
連接閑置才庇以猓活,避免流量浪費(fèi)
重試策略
特定場景的觸發(fā),點(diǎn)亮屏幕,切換前臺(tái)保證信息及時(shí)收到
心跳間隔要小于
NAT
超時(shí)時(shí)間
MQTT
Android
心跳包 AlarmPingSender
- 定義了一個(gè)心跳包發(fā)送接口
public void init();
//開發(fā)發(fā)送心跳包
public void start();
//停止發(fā)送心跳包,可能是出錯(cuò),獲取連接關(guān)閉
public void stop();
//下一次心跳包的發(fā)送
public void schedule(long delayInMilliseconds);
-
Android
端 心跳包實(shí)現(xiàn)類
private MqttService service = mqttService;
?
public void start() {
String action = "mqtt_heart_beat"
//注冊(cè)上對(duì)應(yīng)廣播接收器
service.registerReceiver(alarmReceiver, new IntentFilter(action));
//準(zhǔn)備好 PendingIntent,每次都是定時(shí)發(fā)送一個(gè)廣播
pendingIntent = PendingIntent.getBroadcast(service, 0,
new Intent(action), PendingIntent.FLAG_UPDATE_CURRENT);
//設(shè)置好下一次心跳包的發(fā)送
schedule(interval_time);
}
//準(zhǔn)備好下一次心跳
public void schedule(long delayInMilliseconds) {
//下一次心跳時(shí)間
long nextAlarmInMilliseconds = System.currentTimeMillis()
+ delayInMilliseconds;
//獲取alarmManager
AlarmManager alarmManager = (AlarmManager) service
.getSystemService(Service.ALARM_SERVICE);
//AlaramManager的兼容處理 定時(shí)任務(wù)
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);
}
}
?
//停止定時(shí)任務(wù)
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方法執(zhí)行完成,但是我們需要在此處發(fā)送心跳包等,所以需要?jiǎng)?chuàng)建一個(gè)新的wake_lock
PowerManager pm = (PowerManager) service
.getSystemService(Service.POWER_SERVICE);
wakelock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, wakeLockTag);
wakelock.acquire();
?
//心跳包處理相關(guān)
IMqttToken token = comms.checkForActivity();
wakelock.release();
}
}
// ClientComms#checkForActivity -> ClientStat#checkForActivity
public MqttToken checkForActivity() {
//將pingComond加入到發(fā)送數(shù)據(jù)流中
pendingFlows.insertElementAt(pingCommand, 0);
//獲取下次心跳包時(shí)間
nextPingTime = getKeepAlive();
//準(zhǔn)備下一次心跳包的發(fā)送
pingSender.schedule(nextPingTime);
}
附錄:
-
NAT
超時(shí)- 因?yàn)?
IP v4
的IP
量有限,運(yùn)營商分配給手機(jī)終端的IP
是運(yùn)營商內(nèi)網(wǎng)的IP
缤削,手機(jī)要連接Internet
窘哈,就需要通過運(yùn)營商的網(wǎng)關(guān)做一個(gè)網(wǎng)絡(luò)地址轉(zhuǎn)換(Network Address Translation
,NAT
)亭敢。簡單的說運(yùn)營商的網(wǎng)關(guān)需要維護(hù)一個(gè)外網(wǎng)IP
滚婉、端口到內(nèi)網(wǎng)IP
、端口的對(duì)應(yīng)關(guān)系吨拗,以確保內(nèi)網(wǎng)的手機(jī)可以跟Internet
的服務(wù)器通訊满哪。
- 大部分移動(dòng)無線網(wǎng)絡(luò)運(yùn)營商都在鏈路一段時(shí)間沒有數(shù)據(jù)通訊時(shí)婿斥,會(huì)淘汰
NAT
表中的對(duì)應(yīng)項(xiàng)劝篷,造成鏈路中斷。
- 長連接心跳間隔必須要小于
NAT
超時(shí)時(shí)間(aging-time
)民宿,如果超過aging-time
不做心跳娇妓,TCP
長連接鏈路就會(huì)中斷,Server
就無法發(fā)送Push
給手機(jī)活鹰,只能等到客戶端下次心跳失敗后哈恰,重建連接才能取到消息。
- 因?yàn)?
思考
- 心跳機(jī)制保證了連接的可用性,保證推送信息的及時(shí)收取,但是為了及時(shí)收到信息,敝救海活進(jìn)程也是一個(gè)逃不開的手段,后續(xù)分析一下,進(jìn)程的弊疟粒活的手段