乾貨!手把手教你如何使用第三方通訊服務實現LayIM Socket元件開發。

丶Pz發表於2017-01-18

前言

  之前寫了一系列的文章,是關於使用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.先註冊一個賬號,然後選擇建立應用。建立應用之後,不用上線,我們選擇測試環境即可。我們要的就是拿到appkeyappsecret

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

相關文章