前言
之前寫了一系列的文章,是關於使用ASP.NET SignalR技術實現LayIM的功能對接,有興趣的同學移步:http://www.cnblogs.com/panzi/p/5767095.html。
此篇會從頭到尾詳細介紹開發流程,和對接方法。文章會比較長,准備點小零食,細細品讀吧,如果只能夠跟着實踐最好。
本篇文章主要講解內容如下:
- 融雲服務WebSDK的對接
- LayIM接口的對接
- 如何將外網js模塊化,使其符合layui標准
- 其他細節等
准備工作
首先,LayIM不多介紹了,想了解的同學移步:http://www.layui.com/doc/modules/layim.html 。已經了解過的同學可以忽略。因為這次用的是第三方融雲服務實現,所以,我們先去官網注冊一個賬號吧。融雲官網地址:http://rongcloud.cn.先注冊一個賬號,然后選擇創建應用。創建應用之后,不用上線,我們選擇測試環境即可。我們要的就是拿到appkey和appsecret 。

js模塊化
為什么叫js模塊化呢,一般正常情況下,我們直接按照融雲所給的文檔里面那么調用即可。文檔地址,如果不想去看融雲文檔的話,可以直接看開發完成后的源代碼即可。融雲Web開發文檔地址:http://www.rongcloud.cn/docs/web.html 。文檔中的調用方式如下:

其實如上圖調用也完全沒問題,但是我們要開發layui組件的話,就必須要改一下了。因為我們最終想使用 layui.use的方式,而不是直接像上圖那樣引用js。看一下文檔結構:

首先,rmlib對應RongIMLib-2.2.4.min.js,protobuf對應protobuf.2.1.5.min.js,socket 就是業務封裝層了。
rmlib的改造比較簡單,直接將js內容粘貼下來,然后根據layui語法exports即可。

為什么要加protobuf這個js呢,說一下原因,首先在原生的融雲js調用的時候,會加載一個protobuf.min.js

而由於公司網絡不好的原因,經常會出現加載該js卡住的情況,而導致通訊失敗,那么我們將他同樣復制粘貼下來,改造一下。同樣:

當我們美滋滋的執行程序的時候,會發現,這玩意還是會被加載,就會導致出現加載兩次的情況,當然這是由於rmlib.js中某段代碼加載了該js,我們要做的就是找到那段代碼,然后不讓它加載就可以了。如下圖:

當我們取查代碼的時候,哇,忘記了,是壓縮過的代碼。從何查起呢,查找protobuf,不起作用,后來我就查找了2.1.5,就找到如下這段代碼
j=e.1o.iq(s,{a5:r+"5b.4e.2Z/a5-2.1.5.9n.js","ds":r+"5b.4e.2Z/sg.js"
可以分析一下,r應該是http或者https,而5b.4e.2z對應cdn.ronghub.com,a5應該就是protobuf了,剛開始我是直接把a5刪掉,后來發現會出現請求undefined的情況,后來將代碼中的,a5改成空值。即 a5:''.大功告成,終於不用再加載cdn的代碼了。如下圖,只會加載我們自己定義的protobuf.js

最后一個js,socket.由於它是直接封裝業務的,所以,我們將依賴加上,然后暴漏socket。到這里的我們的基本准備工作就算結束了,下面就是業務開發了。

核心業務-連接服務器
由於這里呢,我不想把太多.NET的東西帶進來,所以,LayIM的對接后台這次就不在闡述了,主要目的是讓大家拿到這個東西直接運行通訊功能。主要是前端的開發工作。首先呢,我們知道,既然使用了融雲,那就有必要了解一下它是怎么工作的。直接進入代碼階段。(里面內容不懂得可以看融雲官方文檔,下文中的lib即官網中的RongIMLib)
很簡單,先來一段 init,初始化,很簡單吧,這里用到了我們之前拿到的 appkey。
lib.RongIMClient.init(conf.key); this.initListener(); this.defineMessage();
下面呢,我們要監聽融雲的連接狀況,就用到了initListener,詳細代碼如下,直接從官網文檔copy即可。里面會監聽到各種狀態,包括監聽消息接收。
// 設置連接監聽狀態 ( status 標識當前連接狀態 ) // 連接狀態監聽器 log('注冊服務連接監聽事件'); var code = im.code.errorUnKnown; RongIMClient.setConnectionStatusListener({ onChanged: function (status) { switch (status) { case lib.ConnectionStatus.CONNECTED: break; case lib.ConnectionStatus.CONNECTING: break; case lib.ConnectionStatus.DISCONNECTED: break; case lib.ConnectionStatus.KICKED_OFFLINE_BY_OTHER_CLIENT: break; case lib.ConnectionStatus.DOMAIN_INCORRECT: break; case lib.ConnectionStatus.NETWORK_UNAVAILABLE: break; } listener(code); } }); // 消息監聽器 RongIMClient.setOnReceiveMessageListener({ // 接收到的消息 onReceived: function (message) { log(message); // 判斷消息類型 switch (message.messageType) { } } });
最后一個defineMessage,這個有點意思,就是說,融雲給我們提供了多種數據類型,但是都不滿足我們的需求,所以我們就要自己定義消息類型了。這里呢,我們需要的消息類型是什么呢?先不要看下文,想一下,這里接受消息要定義什么樣的類型,根據什么來定義?
想到了沒有?當然是layim想要的消息格式。根據文檔可知,就是如下消息類型:
layim.getMessage({ username: "紙飛機" //消息來源用戶名 ,avatar: "http://tp1.sinaimg.cn/1571889140/180/40030060651/1" //消息來源用戶頭像 ,id: "100000" //消息的來源ID(如果是私聊,則是用戶id,如果是群聊,則是群組id) ,type: "friend" //聊天窗口來源類型,從發送消息傳遞的to里面獲取 ,content: "嗨,你好!本消息系離線消息。" //消息內容 ,cid: 0 //消息id,可不傳。除非你要對消息進行一些操作(如撤回) ,mine: false //是否我發送的消息,如果為true,則會顯示在右方 ,fromid: "100000" //消息的發送者id(比如群組中的某個消息發送者),可用於自動解決瀏覽器多窗口時的一些問題 ,timestamp: 1467475443306 //服務端動態時間戳 });
為了方便,我只加了其中幾個必要的屬性。注冊自定義消息方法如下:
var defineMsg = function (obj) { RongIMClient.registerMessageType(obj.msgName, obj.objName, obj.msgTag, obj.msgProperties); } //注冊普通消息 var textMsg = { msgName: 'LAYIM_TEXT_MESSAGE',//自己定義的名稱 objName: 'LAYIM:CHAT', msgTag: new lib.MessageTag(false, false), msgProperties: ["username", "avatar", "id", "type", "content"] }; //注冊 defineMsg(textMsg);
init完了之后我們要做什么呢,就需要用戶連接服務器了,那么融雲連接服務器是需要根據用戶id生成一個token的,涉及到服務端的東西。這里怎么避免先不用服務端的呢,我們可以在融雲接口調試里面自己根據用戶id生成token,因為token是永久性的(可以設置),所以,我們如果想做測試,可以直接生成兩個token即可,不過,因為后台我已經完成了token的生成功能,所以,自己生成token需要大家自行去官網找一下。找到API調試,輸入用戶id得到token復制即可。

當我們拿到token之后呢,我們連接一下服務器:
RongIMClient.connect(token, { onSuccess: function (userId) { //連接成功 }, onTokenIncorrect: function () {
//token錯誤,如果出現token錯誤,我們重新生成一個即可
}, onError: function (errorCode) { var info = ''; var code = im.code.errorUnKnown; switch (errorCode) { case RongIMLib.ErrorCode.TIMEOUT: //超時break; case RongIMLib.ErrorCode.UNKNOWN_ERROR: //未知錯誤break; case RongIMLib.ErrorCode.UNACCEPTABLE_PaROTOCOL_VERSION: //版本不正確break; case RongIMLib.ErrorCode.IDENTIFIER_REJECTED: //被拒絕break; case RongIMLib.ErrorCode.SERVER_UNAVAILABLE: //服務不可用break; } } });
我們連接之后呢,打開調試看一下network,可以看到,websocket連接,已經成功了。

看一下日志打印:

核心業務-發送消息
那么上一步已經連接成功了,下一步,我們就要發送消息了。之前已經定義好了消息類型,根據融雲文檔提供的方法,我們將消息組織好,發送即可。首先呢,這里要用到layim的 sendMessage監聽方法了。
layim.on('sendMessage', function (data) {
//調用socket方法,發送消息
im.sendMsg(data);
});
是不是很簡單,先看一下 layim提供的data是神馬東東:

里面的內容不用我多解釋了吧,我們只要將mine和to解析一下,然后轉換成我們想要的格式即可,這里要注意,group 和 friend 有些區別。請看代碼:
sendMsg: function (data) { //根據layim提供的data數據,進行解析 var mine = data.mine; var to = data.to; var id = conf.uid || mine.id;//當前用戶id var group = to.type == 'group'; if (group) { id = to.id;//如果是group類型,id就是當前groupid,切記不可寫成 mine.id否則會出現一些問題。 } //構造消息 var msg = { username: mine.username , avatar: mine.avatar , id: id , type: to.type , content: mine.content } //這里要判斷消息類型 var conversationType = group ? lib.ConversationType.GROUP : lib.ConversationType.PRIVATE; //私聊,其他會話選擇相應的消息類型即可。 var targetId = to.id; //構造消息體,這里就是我們剛才已經注冊過的自定義消息 var detail = new RongIMClient.RegisterMessage.LAYIM_TEXT_MESSAGE(msg); //發送消息 RongIMClient.getInstance().sendMessage(conversationType, targetId, detail, { onSuccess: function (message) { log('發送消息成功'); }, onError: function (errorCode) { } }); },
當我們發送消息之后,對方肯定會收到。(除了初始化有問題之外可能收不到,第三方還是比較穩定的,消息都能送達)我們看一下接收到的消息格式,一大堆,其實對我們有用的有timestamp和content里面的內容。

因為之前我們已經初始化了消息接收監聽事件。所以,后邊我們簡單的一句調用就可以了。
layim.getMessage(message.content);
效果預覽

效果雖然有了,但是和真正的應用還有很大的差距,不過,那些都可以在修改socket.js來滿足需求,同樣,新手可以引用此組件輕松實現聊天功能對接,而且不用寫很多代碼。下面看一下最終的代碼編寫方式:

現在你無需關心layim是怎么接收消息的,以及他的消息格式類型是什么,你只要引入相應的組件,然后做一下簡單的配置,即可完成通訊功能,對於想快速開發並且,不需要保存消息歷史記錄的需求,這個東西就很方便了。對接起來也容易,因為並不是他簡單,而是核心內容已經給封裝好了,做一個簡單的配置即可使用。
細節補充
日志打印是否開放:
//記錄日志 var log = function (msg) { conf.log && console.log(msg); }
監聽方法的實現,抄襲layim 嘿嘿。
//事件監聽 var listener = function (code) { code && (call['status'] ? call['status'](code) : log(code)); }
如果初始化比較慢,為了不影響消息發送,采用臨時數組保存。
sendMsgWithQueue: function (data) { if (!im.connected) { log('當前服務器未連接,將消息加入到未發送隊列'); msgQueue.push(data); } else { this.sendMsg(data); } },
連接成功之后,消息發送
connectSuccess: function (uid) { im.code.usuccess.uid = uid; im.connected = true;//連接成功 listener(im.code.usuccess); if (msgQueue.length) { //隊列中有消息,發送出去 for (var i = 0; i < msgQueue.length; i++) { im.sendMsg(msgQueue[i]); } msgQueue = []; } },
其他細節有興趣的同學可以看看代碼,並給出修改意見,謝謝大家。請注意,本文依賴於Layim3.0+版本。
總結
寫一篇文章比我開發時間都多了,不過總結一下也是好的,希望能幫到一些需要的同學。同時,完整開發版我會繼續開發下去,由於使用了第三方,所以通訊邏輯我就不會在去關心,重點放在項目的架構上,以及其他東西的研究。如果你讀到了這里,非常感謝。
github地址:https://github.com/fanpan26/LayIM_NetClient/blob/master/LayIM_RongCloud_Chat/Scripts/im/rc/socket.js
