上篇: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