干货!手把手教你如何使用第三方通讯服务实现LayIM Socket组件开发。

前言

  之前写了一系列的文章,是关于使用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

posted @ 2017-01-18 16:15  丶Pz  阅读(...)  评论(...编辑  收藏