前言
之前寫了一系列的文章,是關於使用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