ASP.NET SignalR 與 LayIM2.0 配合輕鬆實現Web聊天室(二) 之 ChatServer搭建,連線伺服器,以及注意事項。

丶Pz發表於2016-08-08

  上篇: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));
自動生成的hubs程式碼

  可以看到,上述程式碼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

   GitHub:https://github.com/fanpan26/LayIM_NetClient/

相關文章