前言
最近在寫一個即時通訊的項目,有一些心得,寫出來給大家分享指正一下。
簡單描述一下這個項目:
- 實時查詢車輛運行狀態的項目,走TCP通迅。
- 接口采用GZIP壓縮。
- 后臺是通過Apache的Mina框架
- 每隔30秒需要發一個心跳包來維持在線狀態,如果服務器長時間收不到心跳包,會主動斷開鏈接。
- 客戶端發送命令消息均采用Protobuff3.0協議進行封裝。
關于Protobuff3.0不太懂的,可以看一下我上一篇簡書Proto3 語言指南
關于此項目會遇到的難點
- APP的保活
- 目前越來越多國產手機Rom大多都有像小米那樣的神隱模式,長連接就根本無法持續。
- 耗電(在android中耗電主要是因為占用cpu時間長和一些感應器的使用,而長連接基本上分分秒秒都在跑著兩個線程:一個接收一個發送數據)
- TCP粘包問題(就是發送方發送的多個數據包,到接收方后粘連在一起,導致數據包不能完整的體現發送的數據:可能是發送方的原因,也有可能是接受方的原因。)
解決TCP粘包
自定協議,將數據包分為了封包和解包兩個過程。在發送方發送數據時,對發送的數據進行封包操作。在接收方接收到數據時對接收的數據包需要進行解包操作。
自定協議時,封包就是為發送的數據增加包頭,包頭包含數據的大小的信息,數據就跟隨在包頭之后。當然包頭也可以有其他的信息,比如一些做校驗的信息。
針對這個項目,我們來分解需要確定的知識點,如下:
關于Android推送的幾種方式
推送就像訂餐一樣,只要留下你的地址, 送餐員就能如期把飯送到你手里。但在目前的網絡上,這是辦不到的,因為不是每個人都有一個唯一的地址,服務器想要給我們推送一條消息, 必須知道我們的地址,但服務器不知道我們在哪。
關于推送的常用幾種方案:
輪詢
客戶端定期詢問服務器有沒有新的消息,這樣服務器不用管客戶端的地址是什么,客戶端來問,直接告訴它就行。
這種方案最簡單,對于一些不追求實時性的客戶端來說,很適合,只需要把時間間隔設定成幾個小時取一次, 就能很方便的解決問題。
但對于即時通訊產品來說, 這種方案完全不能用。 假設即時通訊軟件在網絡暢通的情況下發送的消息要求對方10s內就能收到, 如果用輪詢,那么客戶端要每隔5s連一次服務器, 如果在移動端, 手機的電量和流量很快就會被消耗殆盡。
定時廣播機制實現
我們可以設定廣播時間然后再廣播接收器中發送心跳包,這個心跳包我們可以直接發送不適用線程,對于發送心跳來說比較頻繁,使用線程還是會耗電,第二,我們心跳其實不需要一天到晚得發送,我們可以在用戶使用完或者鎖屏后25分鐘就暫停發送,然后再過25分鐘喚醒連接看看有沒有消息有就接收,沒有繼續斷開,如果用戶打開應用到停止使用有等待25分鐘斷開然后再連接查看離線消息,這一個循環又能保證新消息的接收又不會一直占用CPU。
長連接
這大概是目前情況下最佳的方案了,,客戶端主動和服務器建立TCP長連接之后, 客戶端定期向服務器發送心跳包,有消息的時候,服務器直接通過這個已經建立好的TCP連接通知客戶端。
下面普及一些概念:
- 短連接:是通訊雙方有數據交互時就建立一個連接, 數據發送完成后,則斷開此連接
- 長連接:大家建立連接之后, 不主動斷開。 雙方互相發送數據,發完了也不主動斷開連接, 之后有需要發送的數據就繼續通過這個連接發送。TCP連接在默認的情況下就是所謂的長連接。(也就是說連接雙方都不主動關閉連接, 這個連接就應該一直存在)
但是一些外在的因素也會將長連接斷開,例如或者服務器宕機、客戶端網絡異、手機網絡和WIFI網絡切換(IP不一樣了)、DHCP的租期等等。
關于手機網絡和WIFI網絡切換為什么會導致長連接斷開?
因為平時我們使用的NAT設備最常見就是路由器。NAT設備會在IP封包通過設備時修改源/目的IP地址:它不僅改IP, 還修改TCP和UDP協議的端口號,這樣就能讓內網中的設備共用同一個外網IP。 舉個例子, NAPT維護一個類似下表的NAT表
NAT設備會根據NAT表對出去和進來的數據做修改,比如將192.168.0.3:8888發出去的封包改成120.132.92.21:9202,外部就認為他們是在和120.132.92.21:9202通信。 同時NAT設備會將120.132.92.21:9202收到的封包的IP和端口改成192.168.0.3:8888, 再發給內網的主機, 這樣內部和外部就能雙向通信了,但如果其中192.168.0.3:8888 == 120.132.92.21:9202這一映射因為某些原因被NAT設備淘汰了, 那么外部設備就無法直接與192.168.0.3:8888通信了。
關于DHCP的租期
目前測試發現安卓系統對DHCP的處理有Bug, DHCP租期到了不會主動續約并且會繼續使用過期IP, 這個問題會造成TCP長連接偶然的斷連。
TCP長連接本質上不需要心跳包來維持
TCP是有?;疃〞r器的,默認是用?;疃〞r器來維持長連接,?;疃〞r器的周期是兩小時。
那為什么要有心跳包呢? 1個服務端會對應很多客戶端,如果都用保活定時器心跳,那么這些客戶端在兩小時內都會維持一個長連接,那么問題來了,很多長鏈接是可以關閉的,而且服務器帶寬有限,所以需要心跳包,來及時把不需要維持的鏈接關閉掉。所以需要手動發心跳。?;疃〞r器的兩小時是在是太長了。
心跳包的時間間隔
發送心跳包勢必要先喚醒設備, 然后才能發送, 如果喚醒設備過于頻繁, 或者直接導致設備無法休眠, 會大量消耗電量, 而且移動網絡下進行網絡通信, 比在wifi下耗電得多. 所以這個心跳包的時間間隔應該盡量的長, 最理想的情況就是根本沒有NAT超時, 比如剛才我說的兩臺在同一個wifi下的電腦, 完全不需要心跳包. 這也就是網上常說的長連接, 慢心跳.
根據網上的一些說法, 中移動2/3G下, NAT超時時間為5分鐘, 中國電信3G則大于28分鐘, 理想的情況下, 客戶端應當以略小于NAT超時時間的間隔來發送心跳包。
所以心跳間隔逼近NAT超時的間隔, 同時自動適應NAT超時間隔的變化。
服務器如何處理心跳包
如果客戶端心跳間隔是固定的,那么服務器在連接閑置超過這個時間還沒收到心跳時, 可以認為對方掉線, 關閉連接。 如果客戶端心跳會動態改變, 如上節提到的微信心跳方案, 應當設置一個最大值, 超過這個最大值才認為對方掉線。 還有一種情況就是服務器通過TCP連接主動給客戶端發消息出現寫超時, 可以直接認為對方掉線。
心跳包和輪詢的區別
心跳包和輪詢看起來類似, 都是客戶端主動聯系服務器, 但是區別很大。
- 輪詢是為了獲取數據, 而心跳是為了?;頣CP連接.
- 輪詢得越頻繁, 獲取數據就越及時, 心跳的頻繁與否和數據是否及時沒有直接關系
- 輪詢比心跳能耗更高, 因為一次輪詢需要經過TCP三次握手, 四次揮手, 單次心跳不需要建立和拆除TCP連接.
關于Android TCP 耗電
首先Android手機有兩個處理器, 一個叫Application Processor(AP), 一個叫Baseband Processor(BP). AP是ARM架構的處理器,用于運行Android系統; BP用于運行實時操作系統(RTOS), 通訊協議棧運行于BP的RTOS之上. 非通話時間, BP的能耗基本上在5mA左右,而AP只要處于非休眠狀態, 能耗至少在50mA以上, 執行圖形運算時會更高. 另外LCD工作時功耗在100mA左右, WIFI也在100mA左右. 一般手機待機時, AP, LCD, WIFI均進入休眠狀態, 這時Android中應用程序的代碼也會停止執行。
Android為了確保應用程序中關鍵代碼的正確執行, 提供了Wake Lock的API, 使得應用程序有權限通過代碼阻止AP進入休眠狀態. 但如果不領會Android設計者的意圖而濫用Wake Lock API, 為了自身程序在后臺的正常工作而長時間阻止AP進入休眠狀態, 就會成為待機電池殺手.
完全沒必要擔心AP休眠會導致收不到消息推送。通訊協議棧運行于BP,一旦收到數據包, BP會將AP喚醒, 喚醒的時間足夠AP執行代碼完成對收到的數據包的處理過程. 其它的如Connectivity事件觸發時AP同樣會被喚醒. 那么唯一的問題就是程序如何執行向服務器發送心跳包的邏輯. 你顯然不能靠AP來做心跳計時. Android提供的Alarm Manager就是來解決這個問題的. Alarm應該是BP計時(或其它某個帶石英鐘的芯片,不太確定,但絕對不是AP), 觸發時喚醒AP執行程序代碼. 那么Wake Lock API有啥用呢? 比如心跳包從請求到應答, 比如斷線重連重新登陸這些關鍵邏輯的執行過程, 就需要Wake Lock來保護. 而一旦一個關鍵邏輯執行成功, 就應該立即釋放掉Wake Lock了. 兩次心跳請求間隔5到10分鐘, 基本不會怎么耗電. 除非網絡不穩定. 頻繁斷線重連, 那種情況辦法不多.
耗電跟push這塊,還有一個心跳對齊的功能。這也是推薦用開源項目的原因。比如說多個app都集成了個推,這些app會公用同一個心跳包,來節省耗電之類的。
關于Mina可能遇到的問題
一個是Android端發一個漢字給服務器, 服務器filter崩潰, 發超過一個漢字, 客戶端filter崩潰, 寫個IoFilter做一下編解碼就好了. 另外User Guide里面的代碼也有錯誤. 第二個是IoSessionConfig的寫超時設置了完全不起作用。
關于小米手機Socket問題
后來又發現客戶端只要在后臺超過一定時間,對socket的寫操作就會變得非常詭異, 表現為socket把數據吞了, 告知應用數據已經被對方接收,但是服務器什么都沒收到,而且服務器發送的消息客戶端也收不到。只要讓app進到前臺,之前消失的數據會一股腦發給服務器, 客戶端會收到服務器重傳的消息。
這個bug的臨時解決方案是:用神隱模式里的自定義配置, 把自己想改的設置好就行。
參考文章
Android微信智能心跳方案
android設備休眠
Optimizing Downloads for Efficient Network Access
關于socket長連接的心跳包
Network address translation
C/C++網絡編程中的TCP?;?/a>
TCP/IP,http,socket,長連接,短連接——小結。
Android實現推送方式解決方案
[1] 網絡編程基礎資料:
《TCP/IP詳解-第11章·UDP:用戶數據報協議》
《TCP/IP詳解-第17章·TCP:傳輸控制協議》
《TCP/IP詳解-第18章·TCP連接的建立與終止》
《TCP/IP詳解-第21章·TCP的超時與重傳》
《理論經典:TCP協議的3次握手與4次揮手過程詳解》
《理論聯系實際:Wireshark抓包分析TCP 3次握手、4次揮手過程》
《計算機網絡通訊協議關系圖(中文珍藏版)》
《NAT詳解:基本原理、穿越技術(P2P打洞)、端口老化等》
《UDP中一個包的大小最大能多大?》
《Java新一代網絡編程模型AIO原理及Linux系統AIO介紹》
《NIO框架入門(三):iOS與MINA2、Netty4的跨平臺UDP雙向通信實戰》
《NIO框架入門(四):Android與MINA2、Netty4的跨平臺UDP雙向通信實戰》
更多同類文章 ……
[2] 有關IM/推送的通信格式、協議的選擇:
《為什么QQ用的是UDP協議而不是TCP協議?》
《移動端即時通訊協議選擇:UDP還是TCP?》
《如何選擇即時通訊應用的數據傳輸格式》
《強列建議將Protobuf作為你的即時通訊應用數據傳輸格式》
《移動端IM開發需要面對的技術問題(含通信協議選擇)》
《簡述移動端IM開發的那些坑:架構設計、通信協議和客戶端》
《理論聯系實際:一套典型的IM通信協議設計詳解》
《58到家實時消息系統的協議設計等技術實踐分享》
更多同類文章 ……
[3] 有關IM/推送的心跳?;钐幚恚?/h6>
《Android進程?;钤斀猓阂黄恼陆鉀Q你的所有疑問》
《Android端消息推送總結:實現原理、心跳保活、遇到的問題等》
《為何基于TCP協議的移動端IM仍然需要心跳保活機制?》
《微信團隊原創分享:Android版微信后臺?;顚崙鸱窒?進程保活篇)》
《微信團隊原創分享:Android版微信后臺?;顚崙鸱窒?網絡?;钇?》
《移動端IM實踐:實現Android版微信的智能心跳機制》
《移動端IM實踐:WhatsApp、Line、微信的心跳策略分析》
更多同類文章 ……
[4] 有關WEB端即時通訊開發:
《新手入門貼:史上最全Web端即時通訊技術原理詳解》
《Web端即時通訊技術盤點:短輪詢、Comet、Websocket、SSE》
《SSE技術詳解:一種全新的HTML5服務器推送事件技術》
《Comet技術詳解:基于HTTP長連接的Web端實時通信技術》
《WebSocket詳解(一):初步認識WebSocket技術》
《socket.io實現消息推送的一點實踐及思路》
更多同類文章 ……
[5] 有關IM架構設計:
《淺談IM系統的架構設計》
《簡述移動端IM開發的那些坑:架構設計、通信協議和客戶端》
《一套原創分布式即時通訊(IM)系統理論架構方案》
《從零到卓越:京東客服即時通訊系統的技術架構演進歷程》
《蘑菇街即時通訊/IM服務器開發之架構選擇》
《騰訊QQ1.4億在線用戶的技術挑戰和架構演進之路PPT》
《微信技術總監談架構:微信之道——大道至簡(演講全文)》
《如何解讀《微信技術總監談架構:微信之道——大道至簡》》
《快速裂變:見證微信強大后臺架構從0到1的演進歷程(一)》
《17年的實踐:騰訊海量產品的技術方法論》
更多同類文章 ……
[6] 有關IM安全的文章:
《即時通訊安全篇(一):正確地理解和使用Android端加密算法》
《即時通訊安全篇(二):探討組合加密算法在IM中的應用》
《即時通訊安全篇(三):常用加解密算法與通訊安全講解》
《即時通訊安全篇(四):實例分析Android中密鑰硬編碼的風險》
《傳輸層安全協議SSL/TLS的Java平臺實現簡介和Demo演示》
《理論聯系實際:一套典型的IM通信協議設計詳解(含安全層設計)》
《微信新一代通信安全解決方案:基于TLS1.3的MMTLS詳解》
《來自阿里OpenIM:打造安全可靠即時通訊服務的技術實踐分享》
更多同類文章 ……
有關實時音視頻開發:
《即時通訊音視頻開發(一):視頻編解碼之理論概述》
《即時通訊音視頻開發(二):視頻編解碼之數字視頻介紹》
《即時通訊音視頻開發(三):視頻編解碼之編碼基礎》
《即時通訊音視頻開發(四):視頻編解碼之預測技術介紹》
《即時通訊音視頻開發(五):認識主流視頻編碼技術H.264》
《即時通訊音視頻開發(六):如何開始音頻編解碼技術的學習》
《即時通訊音視頻開發(七):音頻基礎及編碼原理入門》
《即時通訊音視頻開發(八):常見的實時語音通訊編碼標準》
《即時通訊音視頻開發(九):實時語音通訊的回音及回音消除概述》
《即時通訊音視頻開發(十):實時語音通訊的回音消除技術詳解》
《即時通訊音視頻開發(十一):實時語音通訊丟包補償技術詳解》
《即時通訊音視頻開發(十二):多人實時音視頻聊天架構探討》
《即時通訊音視頻開發(十三):實時視頻編碼H.264的特點與優勢》
《即時通訊音視頻開發(十四):實時音視頻數據傳輸協議介紹》
《即時通訊音視頻開發(十五):聊聊P2P與實時音視頻的應用情況》
《即時通訊音視頻開發(十六):移動端實時音視頻開發的幾個建議》
《簡述開源實時音視頻技術WebRTC的優缺點》
《良心分享:WebRTC 零基礎開發者教程(中文)》
更多同類文章 ……
[8] IM開發綜合文章:
《移動端IM開發需要面對的技術問題》
《開發IM是自己設計協議用字節流好還是字符流好?》
《請問有人知道語音留言聊天的主流實現方式嗎?》
《IM系統中如何保證消息的可靠投遞(即QoS機制)》
《談談移動端 IM 開發中登錄請求的優化》
《完全自已開發的IM該如何設計“失敗重試”機制?》
《微信對網絡影響的技術試驗及分析(論文全文)》
《即時通訊系統的原理、技術和應用(技術論文)》
《開源IM工程“蘑菇街TeamTalk”的現狀:一場有始無終的開源秀》
更多同類文章 ……
[9] 開源移動端IM技術框架資料:
《開源移動端IM技術框架MobileIMSDK:快速入門》
《開源移動端IM技術框架MobileIMSDK:常見問題解答》
《開源移動端IM技術框架MobileIMSDK:壓力測試報告》
《開源移動端IM技術框架MobileIMSDK:Android版Demo使用幫助》
《開源移動端IM技術框架MobileIMSDK:Java版Demo使用幫助》
《開源移動端IM技術框架MobileIMSDK:iOS版Demo使用幫助》
《開源移動端IM技術框架MobileIMSDK:Android客戶端開發指南》
《開源移動端IM技術框架MobileIMSDK:Java客戶端開發指南》
《開源移動端IM技術框架MobileIMSDK:iOS客戶端開發指南》
《開源移動端IM技術框架MobileIMSDK:Server端開發指南》
[10] 有關推送技術的文章:
《iOS的推送服務APNs詳解:設計思路、技術原理及缺陷等》
《Android端消息推送總結:實現原理、心跳保活、遇到的問題等》
《掃盲貼:認識MQTT通信協議》
《一個基于MQTT通信協議的完整Android推送Demo》
《求教android消息推送:GCM、XMPP、MQTT三種方案的優劣》
《移動端實時消息推送技術淺析》
《掃盲貼:淺談iOS和Android后臺實時消息推送的原理和區別》
《絕對干貨:基于Netty實現海量接入的推送服務技術要點》
《移動端IM實踐:谷歌消息推送服務(GCM)研究(來自微信)》
《為何微信、QQ這樣的IM工具不使用GCM服務推送消息?》
更多同類文章 ……
[11] 更多即時通訊技術好文分類:
http://www.52im.net/forum.php?mod=collection&op=all
附錄:有關QQ、微信的文章匯總
[1] 有關QQ、微信的技術文章:
《微信后臺團隊:微信后臺異步消息隊列的優化升級實踐分享》
《微信團隊原創分享:微信客戶端SQLite數據庫損壞修復實踐》
《騰訊原創分享(一):如何大幅提升移動網絡下手機QQ的圖片傳輸速度和成功率》
《騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(下篇)》
《騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(上篇)》
《微信Mars:微信內部正在使用的網絡層封裝庫,即將開源》
《如約而至:微信自用的移動端IM網絡層跨平臺組件庫Mars已正式開源》
《開源libco庫:單機千萬連接、支撐微信8億用戶的后臺框架基石 [源碼下載]》
《微信新一代通信安全解決方案:基于TLS1.3的MMTLS詳解》
《微信團隊原創分享:Android版微信后臺保活實戰分享(進程?;钇?》
《微信團隊原創分享:Android版微信后臺?;顚崙鸱窒?網絡?;钇?》
《Android版微信從300KB到30MB的技術演進(PPT講稿) [附件下載]》
《微信團隊原創分享:Android版微信從300KB到30MB的技術演進》
《微信技術總監談架構:微信之道——大道至簡(演講全文)》
《微信技術總監談架構:微信之道——大道至簡(PPT講稿) [附件下載]》
《如何解讀《微信技術總監談架構:微信之道——大道至簡》》
《微信海量用戶背后的后臺系統存儲架構(視頻+PPT) [附件下載]》
《微信異步化改造實踐:8億月活、單機千萬連接背后的后臺解決方案》
《微信朋友圈海量技術之道PPT [附件下載]》
《微信對網絡影響的技術試驗及分析(論文全文)》
《一份微信后臺技術架構的總結性筆記》
《架構之道:3個程序員成就微信朋友圈日均10億發布量[有視頻]》
《快速裂變:見證微信強大后臺架構從0到1的演進歷程(一)》
《快速裂變:見證微信強大后臺架構從0到1的演進歷程(二)》
《微信團隊原創分享:Android內存泄漏監控和優化技巧總結》
《全面總結iOS版微信升級iOS9遇到的各種“坑”》
《微信團隊原創資源混淆工具:讓你的APK立減1M》
《微信團隊原創Android資源混淆工具:AndResGuard [有源碼]》
《Android版微信安裝包“減肥”實戰記錄》
《iOS版微信安裝包“減肥”實戰記錄》
《移動端IM實踐:iOS版微信界面卡頓監測方案》
《微信“紅包照片”背后的技術難題》
《移動端IM實踐:iOS版微信小視頻功能技術方案實錄》
《移動端IM實踐:Android版微信如何大幅提升交互性能(一)》
《移動端IM實踐:Android版微信如何大幅提升交互性能(二)》
《移動端IM實踐:實現Android版微信的智能心跳機制》
《移動端IM實踐:WhatsApp、Line、微信的心跳策略分析》
《移動端IM實踐:谷歌消息推送服務(GCM)研究(來自微信)》
《移動端IM實踐:iOS版微信的多設備字體適配方案探討》
更多同類文章 ……
[2] 有關QQ、微信的技術故事:
《技術往事:創業初期的騰訊——16年前的冬天,誰動了馬化騰的代碼》
《技術往事:史上最全QQ圖標變遷過程,追尋IM巨人的演進歷史》
《開發往事:深度講述2010到2015,微信一路風雨的背后》
《開發往事:微信千年不變的那張閃屏圖片的由來》
《開發往事:記錄微信3.0版背后的故事(距微信1.0發布9個月時)》
《一個微信實習生自述:我眼中的微信開發團隊》
更多同類文章 ……