ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(二) 之 ChatServer搭建,连接服务器,以及注意事项。
上篇:ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(一) 之 基层数据搭建,让数据活起来(数据获取)
上一篇我们已经完成了初步界面的搭建工作,本篇将介绍IM的核心内容了,就是SignalR的Hub类。整个即时通讯机制都是以它为基础的。至于原理我也不再讲解,讲了也不如专业的文章讲得好。所以我们直接看业务,上代码。有一部分原理 在文章 ASP.NET SignalR 与LayIM配合,轻松实现网站客服聊天室(二) 实现聊天室连接 (当时是LayIM1.0版本)。原理是一样的,不过这次我把Server端单独提取出来,为了防止 Server端和UI端过度耦合,拆分不方便。进入正题:
打开项目 LayIM.ChatServer,新建LayIMHub类继承自Hub。(这里要添加Microsoft.AspNet.SignalR.Core,Microsoft.AspNet.SignalR.SystemWeb的引用)核心方法如下:
上边三个重写的方法一看就很明白,通过这几个方法,客户端很容易知道自己的连接状态。我们主要看下边四个方法。
- 单聊连接(ClientToClient):这个方法就是当用户点击某个人的头像打开聊天界面的时候,要调用的方法,目的是让当前聊天的两个人分配到一个组里。(当然:Clients.Client(connectionId)才是真正的给某个Client发送消息的方法),我这里用的原理就是群组,就算是两个人聊天,相当于一个群里面只有两个人,所以,两个人和多个人聊天原理是相同的。文字有些抽象,画个图大家应该就明白了
上图就是用户A想和用户B聊天打开聊天窗口时的流程,同理如果B恰好也打开了A的窗口,那么组 FRIEND_10001_10000 里面就是存在A和B两个客户端,那么他们俩发的消息就是他们两个人能够收到了。之所以把自己发送的消息还返回给自己,那是因为,这样客户端能够知道我的消息是否发送成功,因为layim在客户端发送消息时已经做了处理,直接将消息加入到聊天框的,但是服务端可能没有收到。所以可能会出现客户端疯狂发消息,但是服务端(对方)一条也没有接收到的情况。还有另外一种情况,A,B都在线,但是B没有打开A的聊天窗口怎么办呢?这里先卖个关子,涉及到后边的在线人员统计机制了。用这个可以做好多东西,只要是客户端涉及是否在线的都可以用到。
然后我们在UI端引用ChatServer.dll ,新建Startup(如果没有的话),代码如下:
using Microsoft.AspNet.SignalR; using Microsoft.Owin; using Owin; [assembly: OwinStartupAttribute(typeof(LayIM_SignalR_Chat.V1._0.Startup))] namespace LayIM_SignalR_Chat.V1._0 { public partial class Startup { public void Configuration(IAppBuilder app) { //ConfigureAuth(app); app.Map("/layim", map => { var hubConfiguration = new HubConfiguration() { EnableJSONP = true }; map.RunSignalR(hubConfiguration); }); } } }
页面上需要引用 jquery.js,signalr.js,/layim/hubs (这个是客户端代理自动生成的),另外,我自己写了个简单的服务调用封装 hub.js,先运行项目,看看自动生成的代码有没有:(http://localhost:8496/layim/hubs)
/*! * ASP.NET SignalR JavaScript Library v2.1.2 * http://signalr.net/ * * Copyright Microsoft Open Technologies, Inc. All rights reserved. * Licensed under the Apache 2.0 * https://github.com/SignalR/SignalR/blob/master/LICENSE.md * */ /// <reference path="..\..\SignalR.Client.JS\Scripts\jquery-1.6.4.js" /> /// <reference path="jquery.signalR.js" /> (function ($, window, undefined) { /// <param name="$" type="jQuery" /> "use strict"; if (typeof ($.signalR) !== "function") { throw new Error("SignalR: SignalR is not loaded. Please ensure jquery.signalR-x.js is referenced before ~/signalr/js."); } var signalR = $.signalR; function makeProxyCallback(hub, callback) { return function () { // Call the client hub method callback.apply(hub, $.makeArray(arguments)); }; } function registerHubProxies(instance, shouldSubscribe) { var key, hub, memberKey, memberValue, subscriptionMethod; for (key in instance) { if (instance.hasOwnProperty(key)) { hub = instance[key]; if (!(hub.hubName)) { // Not a client hub continue; } if (shouldSubscribe) { // We want to subscribe to the hub events subscriptionMethod = hub.on; } else { // We want to unsubscribe from the hub events subscriptionMethod = hub.off; } // Loop through all members on the hub and find client hub functions to subscribe/unsubscribe for (memberKey in hub.client) { if (hub.client.hasOwnProperty(memberKey)) { memberValue = hub.client[memberKey]; if (!$.isFunction(memberValue)) { // Not a client hub function continue; } subscriptionMethod.call(hub, memberKey, makeProxyCallback(hub, memberValue)); } } } } } $.hubConnection.prototype.createHubProxies = function () { var proxies = {}; this.starting(function () { // Register the hub proxies as subscribed // (instance, shouldSubscribe) registerHubProxies(proxies, true); this._registerSubscribedHubs(); }).disconnected(function () { // Unsubscribe all hub proxies when we "disconnect". This is to ensure that we do not re-add functional call backs. // (instance, shouldSubscribe) registerHubProxies(proxies, false); }); proxies['layimHub'] = this.createHubProxy('layimHub'); proxies['layimHub'].client = { }; proxies['layimHub'].server = { clientSendMsgToClient: function (message) { return proxies['layimHub'].invoke.apply(proxies['layimHub'], $.merge(["ClientSendMsgToClient"], $.makeArray(arguments))); }, clientSendMsgToGroup: function (message) { return proxies['layimHub'].invoke.apply(proxies['layimHub'], $.merge(["ClientSendMsgToGroup"], $.makeArray(arguments))); }, clientToClient: function (fromUserId, toUserId) { return proxies['layimHub'].invoke.apply(proxies['layimHub'], $.merge(["ClientToClient"], $.makeArray(arguments))); }, clientToGroup: function (fromUserId, toGroupId) { return proxies['layimHub'].invoke.apply(proxies['layimHub'], $.merge(["ClientToGroup"], $.makeArray(arguments))); } }; return proxies; }; signalR.hub = $.hubConnection("/layim", { useDefaultPath: false }); $.extend(signalR, signalR.hub.createHubProxies()); }(window.jQuery, window));
可以看到,上述代码server已经对应了服务端我们自定义的方法。(这里注意,当我们调用server端的方法的时候,首字母都是小写的。)
客户端连接服务器核心代码:
//连接服务器 connect: function () { $.connection.hub.url = _this.option.serverUrl; _this.proxy.proxyCS = $.connection.layimHub; $.connection.hub.start({ jsonp: true }).done(function () { //连接服务器 //TODO处理聊天界面之前的逻辑 //console.log('自定义连接服务器成功方法:' + success); if (call.ready) { for (var i = 0; i < call.ready.length; i++) { call.ready[i]({ connected: true }); } } console.log('连接服务器成功'); }).fail(function () { //console.log('自定义连接服务器成功方法:' + success); if (call.failed) { for (var i = 0; i < call.failed.length; i++) { call.failed[i]({ connected: false }); } } }); },
不知不觉竟然写成了倒叙,给大家解释一下,上文中A和B能够连接的前提条件是 初始化连接服务器能够成功,也就是说客户端调用connect方法然后能够打印 ‘连接服务器成功’。
最后呢,跟Layim1.0不同的是Layim2.0引用js的方法有所变化,类似seajs的语法。所以,我们的hub代码要改成如下形式:
autohub.js
signalr.2.1.2.min.js
hub.js
我们都以这种形式定义成模块之后,需要在页面中添加模块:
//自定义模块 layui.extend({ signalr: '/scripts/signalr/signalr', autohub: '/scripts/signalr/autohub',//自动生成的 hub: '/Scripts/signalr/hub', });
最后,我们以use的形式开启服务端:
运行项目看一下,打印输出:
到此为止,连接服务器工作已经完成了,总结一下:
1.创建Hub文件
2.修改相应config(startup)
3.修改相应的js代码规范(layui.use)
4.界面初始化之后调用connect方法。
本篇就到这里了,由于项目还在进行中,暂时不能提供源代码,不过思路应该没问题吧,如果对博客中有什么疑问或者看法的话,欢迎在底下评论交流。
下篇预告:【初级】ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(三) 之 实现单聊,群聊,发送图片,文件。
想要学习的小伙伴,可以关注我的博客哦,我的QQ:645857874,Email:fanpan26@126.com