基於XMPP的Android即時通訊應用設計方案。

這個文章想寫了好久沒有動筆。感覺都有點生疏了。

之前在一片文章裏提過Android Xmpp做IM的事。做了幾個月自己有了更深的瞭解。

Android IM應用,方案 :openfire + asmack

對於大多數IM應用,這個方案還是不錯的。我之前對這個XMPP適用移動通信產生質疑(比如:通信效率低,網絡穿透能力差)。不過現在我還是比較看好他的。

先說個成功案例吧--環信。環信就是基於XMPP協議開發,現在號稱可以支持千萬級的用戶。當然,環信做了大量的私有化在XMPP的基礎上。他們的Android端sdk也是在asmakc的基礎上開發的。


一、服務器端的設計方案。

服務器在Openfire的基礎上做插件開發。按客戶端和服務器數據庫數據交互的路徑可分爲三類(見下圖):

1.通過自己寫的插件直接操作數據庫。

2.通過自己寫的插件調用openfire的接口完成數據的交互。

3.直接通過openfire的接口完成數據的交互。


( 具體操作留坑待填)


客戶端服務器交互方式

客戶端獲取數據的方式有兩種,一種是推送,一種是拉取。在我們的應用設計中對應Message和IQ。

Message包的設計下面會講到。

IQ指info/Query。是一問一答的機制,當發送一個set或者get類型的IQ到服務器,服務器必須返回一個result或者error類型的IQ給客戶端。

使用http進行數據交互的時候,可以通過地址和參數來區分不同的請求。長連接的數據交互都是以包爲單位,區分不同的請求是通過包信息識別的。對應xmpp的設計中就是IQ的命名空間和其拓展數據。按xmpp的規則自定義IQ的話略顯複雜,所以我們採用了填充json的方式來擴展數據。比如我們通過IQ發送一條微博:

<iq from='[email protected]' type='set'>
    <data xmlns='com.example.test.weibo' action="add"/>
        {"content":"今天天氣不錯",
         "time":"2014-11-28 14:00:01",
         "images":["/image/test1.png",
                   "/image/test2.png",
                   "/image/test3.png"
                  ]
        }
    </data>
</iq>


IM軟件要解決的幾個基本問題

1.消息類

.不同類型的消息:圖片、聲音、文字等。離線消息

2.聯繫人(好友)

聯繫人的常規操作,添加、刪除、分組

3.連接管理

包括數據包的監聽,連接的創建、斷開、重連

4.其他

數據推送,多終端數據同步


先說消息的解決方案。

XMPP的所有數據包都是xml格式的數據,並且有自己的拓展規則。但不得不說的是xml格式的數據傳輸效率比較低,因爲存在大量標籤,所以數據包裏的有效數據比例降低。特別是在移動網絡的情況下,這種格式的數據包非常的浪費流量。我們採用的包結構式xml+json。一來提高了數據傳輸效率,二來一下子從繁瑣的xmpp拓展協議解脫出來,換成了喜聞樂見的json。例如:

文本類型的消息:

<message
    to='[email protected]'
    from='[email protected]/android1.0'
    type='chat'>
    <body>
        {"type": "text",
         "data": "今天天氣不錯"
        }
    </body>
</message>

圖片類型的消息:

<message
    to='[email protected]'
    from='[email protected]/android1.0'
    type='chat'>
    <body>
    {"type":"image",
     "data":{"url":"/image/test.png",
             "thumbUrl":"/image/thumb/test.png",
             "width":150,
             "height":120
            }
    }
    </body>
</message>

        openfire已經完成了個人消息的離線處理。服務器會在你登陸後,將所有的離線消息推送給你,你在登陸後監聽來自服務器的所有消息包即可。嚴格講openfire現在的羣聊應該叫做多人聊天室,openfire會自動記錄羣內的所有聊天內容並存儲(數據庫表ofmucconversationlog)。你需要做的是在每次退出登錄時,記錄一個時間,下次登錄的時候 將聊天記錄表中這個時間之後的所有消息推送給該用戶(這裏要注意,openfire的多人聊天記錄,不會被立刻寫入數據庫,openfire會把記錄寫入緩存,待到達一定條數或者一定時間過後一起寫入數據庫。針對這個情況,我們現階段用了個不好的方法,就是不使用緩存)。

        在xmpp的設計中好友關係分爲3種:雙向好友,單向好友,沒有關係。在好友關係變更的時候會有兩項數據會改變,一個是花名冊(roster),一個是訂閱關係,兩者是同事變更的。花名冊說白了就是你的好友列表,而訂閱關係就是指是否接收對方的在線狀態等(比如好友上線了你是否能知道)。

連接管理

        因爲移動設備網絡環境的不穩定性,連接的處理顯的尤爲重要。asmack中ConnectionListener定義了連接的各種事件,可以通過XMPPConnection的addConnectionListener方法添加連接監聽。asmack也有個自動重連的類(ReconnectionManager),但是單純的在配置中設置是無效的config.setReconnectionAllowed(true);這個類構造方法私有化了,可以通過反射機制去加載這個類使其生效Class.forName("org.jivesoftware.smack.ReconnectionManager");除此之外,長連接通常還設置心跳包,asmack也有對應的實現。如果有客戶端發送ping包的話PingManager.getInstanceFor(conn).setPingInterval(20);(這裏的單位是s...剛開始還以爲是ms,困惑好久),添加ping的同時還可以添加ping失敗的監聽(registerPingFailedListener),來處理ping失敗後的事項。儘管這些已經能保持連接的可靠性,但仍有問題的存在。在連接斷開和知道連接斷開這段時間內的Message包會丟失,這時候就要引入一個回執機制來確保發送的所有包都已到達,對應的xmpp協議XEP-0184。asmack中對應的實現類爲DeliveryReceiptManager。


多終端、版本兼容問題

應用上線一段時間後,可能會有多個版本在流通,甚至是多平臺多版本。比如Android1.5,Android2.0,IOS1.0等等。如果一開始設計沒有很好地考慮這個問題,後期必將是個坑。xmpp在這方面已經有相應的解決方案。在用戶登錄的時候有個resource的參數,在這裏我們可以傳入當前應用的版本及平臺信息。比如上面的例子,我們想要廢棄Android2.0之前的版本,可以在登錄的時候是否是Android平臺版本號是否<2.0 。在多版本通信的時候如果數據結構不一致,高版本需要兼容低版本。從Message的from參數中可以看到消息來自哪個平臺哪個版本的終端。


推送

對於IM軟件來講推送就變得非常簡單了。其實就是發消息,根據不同需求發不同消息就可以了。項目中我把推送內容分爲兩類:一類是通知,服務器可以通過Message的from參數來控制消息發向哪個會話。一類是同步數據,目的就是爲了保證服務器和客戶端的數據一致性。

爲了保證客戶端和服務器數據的一致性通常有兩種方案。

方案一,本地應用不保存數據,每次從服務器獲取全量的數據。

方案二,本地保存數據,每次獲取增量數據,保證與服務器的數據同步。

IM這種應用第二種方案最爲適合。特別是移動應用,每次拉取全量的數據不僅浪費流量,而且應用也無法離線使用。

        這裏說說我對這種增量數據同步的一些想法。爲了保證數據同步的實時性,如果應用離線,數據同步操作在登陸成功到進入應用這段時間完成,如果應用在線,應用後臺服務器應直接處理。關於這個數據同步還有一些問題沒有完美的解決方案。像這種增量的數據操作,錯誤都會累積,如果上次的數據沒有能正確處理,可能導致下次數據同步時出錯。我想了想大概有這麼兩種可能。一是丟包,從服務器推送的同步數據丟失,導致客戶端的數據並沒有和服務器保持一致。針對這種情況,可以設計一個反饋機制。如果客戶端收到數據包的話,給服務器一個再發一個數據包,告知服務器數據已收到。二是本地數據處理異常。由於一些未知的問題導致數據未能正確存儲到本地。數據的不一致可能導致應用發生一些不可預知的錯誤,這時候最好是強制關閉應用,同時保持一個數據同步失敗的狀態到本地,下次啓動的時候檢查到上次同步失敗的狀態的時候,向服務器請求全量的數據並更新到本地數據庫。

應用場景,比如自己所在的羣有個成員被刪除了,這個時候需要更新羣成員列表。


應用的啓動流程


如上圖,應用啓動要處理的事情包括:身份驗證,數據庫、監聽等的初始化,離線數據處理、同步數據處理,進入應用。

登陸之前要先建立匿名連接,然後在進行身份驗證。

身份驗證成功後,要做相應的初始化操作。如監聽的初始化,數據庫的初始化,用戶配置的初始化。監聽初始化,是爲了下一步監聽服務器發來的數據包。(之所以初始化在登陸成功後進行,是因爲在登陸成功前你不知道要使用哪個配置文件、哪個數據庫,比如一個手機切換多個賬號登陸)

在初始化完成,這時候客戶端已經準備就緒,發送一個presence包通知服務器可以接收數據了,服務器開始向客戶端推送數據,推送的數據包括離線消息,同步數據。處理這些數據(通常是寫入數據庫)。完成這些就可以進入應用了。



 ( 還有些東西沒寫完,有時間再更新)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章