移動IM開發那些事:技術選型和常見問題

1.jpg

最近在做一個iOS IM SDK,在內部試用的階段,不斷有兄弟部門或者合作伙伴過來問各種技術細節,所以統一寫一篇文章記錄,統一介紹下一個IM APP的方方面面,包括技術選型(包括通訊方式,網絡連接方式,協議選擇)和常見問題。

通訊方式選擇

IM通訊方式無非兩種選擇:設備直連(P2P)和通過服務器中轉。

P2P

P2P多見于局域網內聊天工具,典型的應用有:飛鴿傳書,天網Maze(你懂的)等。這類軟件在啟動后一般做兩件事情

進行UDP廣播:發送自己信息和接受同局域網內其他端信息

開啟TCP監聽:等待其他端進行連接

詳細的流程可以參考飛鴿傳書源碼。但是這種方式在有種種限制和不便:一方面它只適合在線的點對點消息傳輸,而對離線,群組等業務支持不夠。另一方面由于?NAT?的存在,使得不同局域網內機器互聯的難度大大上升,在某些網絡類型(對稱NAT)下無法建立連接。

服務器中轉

幾乎所有互聯網IM產品都采用服務器中轉這種方式進行消息傳輸,相對于P2P的方式,它有如下的優點:

  • 能夠支持更多P2P無法支持或支持不好的業務,如離線消息,群組,聊天室服務

  • 方便業務邏輯的拓展和新舊版本的兼容

當然它也有自己的問題:服務器架構復雜,并發要求高。

網絡連接方式

IM主流網絡連接方式有兩種:

  • 基于TCP的長連接

  • 基于HTTP短連接PULL的方式

后者常見于WEB IM系統(當然現在很多WEB IM都是基于WebSocket實現),它的優點是實現簡單,方便開發上手,問題是流量大,服務器負載較大,消息及時性無法很好地保證,對大規模的用戶量支持不夠,比較適合小型的IM系統,如一個小網站的客戶系統。

基于TCP長連接則夠更好地支持大批量用戶,問題是客戶端和服務器的實現比較復雜。當然也還有一些變種,如下行使用MQTT進行服務器通知/消息的下發,上行使用HTTP短連接進行指令和消息的上傳。這種方式能夠保證下行消息/指令的及時性,但是在弱網絡下上行慢的問題還是比較嚴重。早期的來往就是基于這種方式。

協議選擇

IM協議選擇原則一般是:易于拓展,方便覆蓋各種業務邏輯,同時又比較節約流量。后一點的需求在移動端IM上尤其重要。

常見的協議有:

  • XMPP

  • SIP

  • MQTT

  • 私有協議

XMPP協議的優點在于:協議開源,可拓展性強,在各個端(包括服務器)有各種語言的實現,開發者接入方便。但是缺點也是不少:XML表現力弱,有太多冗余信息,流量大,實際使用時有大量天坑。

SIP協議多用于VOIP相關的模塊,是一種文本協議,由于我并沒有實際用過,所以不做評論,但從它是文本協議這一點幾乎可以斷定它的流量不會小。

MQTT的優點是協議簡單,流量少,但是它并不是一個專門為IM設計的協議,多使用于推送。

而市面上幾乎所有主流IM APP都是是使用私有協議,一個被良好設計的私有協議一般有如下優點:高效,節約流量(一般使用二進制協議),安全性高,難以破解。缺點則是在開發初期沒有現有樣列可以參考,對于設計者的要求比較高。

一個好的協議需要滿足如下條件:高效,簡潔,可讀性好,節約流量,易于拓展,同時又能夠匹配當前團隊的技術堆棧。基于如上原則,我們可以推出: 如果團隊小,團隊技術在IM上積累不夠可以考慮使用XMPP或者MQTT+HTTP短連接的實現。反之可以考慮自己設計和實現私有協議。

私有協議的設計

序列化選擇

移動互聯網相對于有線網絡最大特點是:帶寬低,延遲高,丟包率高和穩定性差,流量費用高。所以在私有協議的序列化上一般使用二進制協議,而不是文本協議。常見的二進制序列化庫有protobuf和MessagePack,當然你也可以自己實現自己的二進制協議序列化和反序列的過程,比如蘑菇街的TeamTalk。但是前面二者無論是可拓展性還是可讀性都完爆TeamTalk(TeamTalk連Variant都不支持,一個int傳輸時固定占用4個字節),所以大部分情況下還是不推薦自己去實現二進制協議的序列化和反序列化過程。

協議格式設計

基于TCP的應用層協議一般都分為包頭和包體(如HTTP),IM協議也不例外。包頭一般用于表示每個請求/反饋的公共部分,如包長,請求類型,返回碼等。 而包頭則填充不同請求/反饋對應的信息。

一個最簡單的包頭可以定義為

1
2
3
4
5
6
7
struct?PackHeader
{
????int32_t?????length_;????//包長度
????int32_t?????serial_;????//包序列號
????int32_t?????command_;???//包請求類型
????int32_t?????code_;??????//返回碼
};

以心跳包為例,假設當前的serial為1,心跳包的command為10,那么使用MessagePack做序列化時:length=4,serial=1,command=10,code=0,每個字段各占一個字節,包體為空,僅需要4個字節。

當然這是最簡單的一個例子,面對真正的業務邏輯時,包體里面會需要塞入更多地信息,這個需要開發根據自己的業務邏輯總結公共部分,如為了兼容加入的協議版本號,為了負載均衡加入的模塊id等。

其他問題

上面就是一個IM系統大致的選型過程:通訊方式,連接方式,協議選擇,協議設計。但是實際開發過程中還有大量的問題需要處理。

協議加密

為了保證協議不容易被破解,市面上幾乎所有主流IM都會對協議進行加密傳輸。常見的流程和HTTPS加密相似:建立連接后,客戶端和服務器進行進行協商,最終客戶端獲得一個當前Sessino的秘鑰,后續的數據傳輸都通過這個秘鑰進行加解密。一般出于效率的考慮都會采用流式加密,如RC4。而前期協商過程則推薦使用AES等非對稱加密以增加破解難度。

快速連接(登錄)

對iOS APP而言,因為沒有真后臺的存在,APP每次啟動基本都需要一次重連登錄(短時間內切換除外),所以如何快速重連重登就非常重要。常見的優化思路如下:

  • 本地緩存服務器IP并定期刷新。移動網絡調優可以參考《iOS移動網絡調優那些事》。

  • 合并部分請求。如加密和登錄操作可以合并為同一個操作,這樣就可以減少一次不必要的網絡請求來回的時間。

  • 簡化登錄后的同步請求,部分同步請求可以推遲到UI操作時進行,如群成員信息刷新。

連接保持

一般APP實現連接保持的方式無非是采用應用層的心跳,通過心跳包的超時和其他條件(網絡切換)來執行重連操作。那么問題來了:為什么要使用應用層心跳和如何設計應用層心跳。

眾所周知TCP協議是有KEEPALIVE這個設置選項,設置為KEEPALIVE后,客戶端每隔N秒(默認是7200s)會向服務器發送一個發送心跳包。但實際操作中我們更多的時是使用應用層心跳。原因如下:

  • KEEPALIVE對服務器負載壓力比較大(服務器大大是這么說的...)

  • socks代理對KEEPALIVE并不支持

  • 部分復雜情況下KEEPALIVE會失效,如路由器掛掉,網絡直接被拔除

移動端在實際操作時為了節約流量和電量一般會在心跳包上做一些小優化

  • 盡量精簡心跳包,保證一個心跳包大小在10字節之內

  • 心跳包只在空閑時發送 (收到最后一個數據包n秒內再也沒有收到包則進行一次心跳)

  • 根據APP前后臺狀態調整心跳包間隔 (主要是安卓)

消息可達

在移動網絡下,丟包,網絡重連等情況非常之多,為了保證消息的可達,一般需要做消息回執和重發機制。參考易信,每條消息會最多會有3次重發,超時時間為15秒,同時在發送之前會檢測當前連接狀態,如果當前連接并沒有正確建立,緩存消息切定時檢查(每隔2秒檢查一次,檢查15次)。所以一條消息在最差的情況下會有2分多的重試時間,以保證消息的可達。

因為重發的存在,接受端偶爾會收到重復的兩條消息,這種情況下就需要接收端進行去重。一般的做法是每條消息都有自己唯一的message id(一般是uuid)。

文件上傳優化

IM消息(包括SNS模塊)內包含大量的文件上傳的需求,如何優化文件的上傳就成了一個比較大的主題。常見有下面這些優化思路:

  • 將上傳流程提前:音頻提供邊錄邊傳。朋友圈的圖片進行預上傳,選擇圖片后用戶一般會進行文本輸入,在這段時間內后臺就可以默默將選好的圖片進行上傳。

  • 提供閃電上傳的方式:服務器根據MD5進行文件去重。

  • 優化和上傳服務器的連接(參考快速連接),提供連接重用的功能。

  • 文件分塊上傳:因為移動網絡丟包嚴重,將文件分塊上傳可以使得一個分組包含合理數量的TCP包,使得重試概率下降,重試代價變小,更容易上傳到服務器。

  • 在分包的前提下支持上傳的pipeline,避免不必要的網絡等待時間。

  • 支持斷點續傳

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/247895.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/247895.shtml
英文地址,請注明出處:http://en.pswp.cn/news/247895.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

webstrom打開通過頂部瀏覽器打開網頁,被跳轉到默認主頁

重新開始工作啦,希望以后認真一點,并把遇到的問題都記錄下來,雖然是小小白,但能無意間幫助到別人就更開心了呀 通過webstrom打開本地的文件時,發現跳轉到了默認主頁上,吐槽下,有些主頁真的超級流…

mockjs(接口服務代理)

mock官網:http://mockjs.com/ 一、搭建一個練習項目 1.利用vue的項目腳手架進行搭建 命令: vue create mock-demo 截圖: 2.安裝相關的依賴 命令: #使用 axios 發送 ajax npm install axios --save #使用 mock.js 產生隨機數據…

MD5算法原理

MD5(單向散列算法) 的全稱是Message-Digest Algorithm 5(信息-摘要算法),經MD2、MD3和MD4發展而來。MD5算法的使用不需要支付任何版權費用。MD5功能:輸入任意長度的信息,經過處理,輸…

函數-函數進階-裝飾器流程分析

老王:算了,估計你也想不出來。。。學過嵌套函數沒有? 你:yes,然后呢? 老王:想實現一開始你寫的america login(america)不觸發你函數的執行,只需要在這個login里面再定義一層函數&am…

制作手寫簽名

<!DOCTYPE html> <!-- saved from url(0056)http://hao2013.cn/canvas-special-master/brush/index.html --> <html><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><title>簽名板(支持移動…

python第五次作業——陳靈院

習題1&#xff1a;讀入文件pmi_days.csv&#xff0c;完成以下操作&#xff1a;1.統計質量等級對應的天數&#xff0c;例如&#xff1a;優&#xff1a;5天良&#xff1a;3天中度污染&#xff1a;2天2.找出PMI2.5的最大值和最小值&#xff0c;分別指出是哪一天。 import csv impo…

iOS 二叉樹相關算法實現

什么是二叉樹&#xff1f; 在計算機科學中&#xff0c;二叉樹是每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”和“右子樹”&#xff0c;左子樹和右子樹同時也是二叉樹。二叉樹的子樹有左右之分&#xff0c;并且次序不能任意顛倒。二叉樹是遞歸定義的&#xff0c;所…

vux 組件庫首次使用安裝

1、首先通過腳手架新建一個項目&#xff0c;過程略。 創建完項目后&#xff0c;在項目里安裝vux&#xff0c; 通過命令 npm install vux --save 安裝 2、安裝vux-loader&#xff0c; 通過命令 npm install vux-loader --save-dev 安裝&#xff08;vux文檔沒說明&#xff09; 3、…

@Component 和 @Bean 的區別

Spring幫助我們管理Bean分為兩個部分&#xff0c;一個是注冊Bean&#xff0c;一個裝配Bean。完成這兩個動作有三種方式&#xff0c;一種是使用自動配置的方式、一種是使用JavaConfig的方式&#xff0c;一種就是使用XML配置的方式。 Compent 作用就相當于 XML配置 Component pub…

js動態驗證碼獲取

<!DOCTYPE html> <html lang"cn"> <head><meta charset"UTF-8"><title>短信驗證碼</title> </head> <body> <input type"number" id"tel" value"13303861063"> <…

Base64 算法原理,以及編碼、解碼【加密、解密】 介紹

Base64編碼&#xff0c;是我們程序開發中經常使用到的編碼方法。它是一種基于用64個可打印字符來表示二進制數據的表示方法。它通常用作存儲、傳輸一些二進制數據編碼方法&#xff01;也是MIME&#xff08;多用途互聯網郵件擴展&#xff0c;主要用作電子郵件標準&#xff09;中…

js通過身份證獲取年齡

// 獲取用戶的身份證號碼let identityCard this.idNum.replace(/\s/g, "");//判斷長度let len identityCard.length;//設置新的變量var strBirthday "";//根據長度獲取年月日if (len 18) {strBirthday identityCard.substr(6, 4) "/" identi…

爬取豆瓣top250

#xpath #第一種方法 可在開發者工具中找到標簽&#xff0c;右鍵copy xpath&#xff0c;有時需去掉tbody標簽 #第二種方法 簡單學習xpath&#xff0c;自己書寫&#xff0c;掌握基本語法即可&#xff0c;簡單的層級關系#先將csv文件以記事本打開&#xff0c;更改編碼為ASNI&…

TCP/IP,Http,Socket,XMPP的區別

網絡由下往上分為 物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層和應用層。 通過初步的了解&#xff0c;我知道IP協議對應于網絡層&#xff0c;TCP協議對應于傳輸層&#xff0c;而HTTP協議對應于應用層&#xff0c; 三者從本質上來說沒有可比性&#xff0c; socket則是對…

LED音樂頻譜之點陣

轉載請注明出處&#xff1a;http://blog.csdn.net/ruoyunliufeng/article/details/37967455 一.硬件 這里的LED選擇直插的霧面LED&#xff0c;亮度可以還不失美觀。注意每行要加上限流電阻。74HC138&#xff08;三八譯碼器&#xff09;作為列選&#xff0c;每行都連著74HC595&a…

上架相關——App Store 上架流程

說實話&#xff0c;公司要上架一個自己做的一個小項目。為了完成這個任務&#xff0c;菜鳥的我一遍找資料一遍跟著做&#xff0c;一遍修改錯誤一遍查找解決方案。網上的資料大部分都是2015年以前的資料&#xff0c;資料有點不夠過時&#xff0c;而且步驟配圖也不是很詳細&#…

this.$router 的三種跳轉頁面方法

第一種&#xff1a; this.$router.push(需要跳轉到的路徑名稱)此方法跳轉后&#xff0c;會在歷史欄目中保存路勁地址&#xff0c;當點擊歷史標簽時可以進行訪問 第二種&#xff1a; this.$router.replace(需要跳轉到的路徑名稱)此方法跳轉后&#xff0c;會在歷史欄目中不保存…

cf777c

題意&#xff1a;給你一個n*m的數陣 對于一行到另一行&#xff0c;若存在一列從上到下遞減&#xff0c;則稱之符合題意 The first line of the input contains two positive integers n and m (1?≤?nm?≤?100?000) — the number of rows and the number of columns in t…

上架相關——appstore 更新app版本

注&#xff1a;此片文章是基于app已經上架&#xff0c;也就是證書都已經配置好的前提下。 首先是還是app打包 修改版本號 修改project處的pp文件 檢查無誤后打包打包完成后upload to app store 漫長的等待。。 上傳到appstore進入iTunesConnect 選擇我的app 選擇對應app點…

輸入框輸入數字,且不能有小數點存在

基于Vue項目進行設置 <template><comp v-if"update"></comp><button click"reload()">刷新comp組件</button></template><script>import comp from /views/comp.vueexport default {name: parentComp,data() {r…