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