干货!手把手教你如何使用第三方通讯服务实现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.先注册一个账号,然后选择创建应用。创建应用之后,不用上线,我们选择测试环境即可。我们要的就是拿到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