Net Core SignalR 測試,可以用於unity、Layair、白鷺引擎、大數據分析平台等高可用消息實時通信器。


SignalR介紹

SignalR介紹來源於微軟文檔,不過多解釋。https://docs.microsoft.com/zh-cn/aspnet/core/signalr/introduction?view=aspnetcore-2.1

ASP.NET Core SignalR 是一個開源代碼庫,它簡化了向應用添加實時 Web 功能的過程。 實時 Web 功能使服務器端代碼能夠即時將內容推送到客戶端。

SignalR 的適用對象:

  • 需要來自服務器的高頻率更新的應用。 例如:游戲、社交網絡、投票、拍賣、地圖和 GPS 應用。
  • 儀表板和監視應用。 示例包括公司儀表板、銷售狀態即時更新或行程警示。
  • 協作應用。 協作應用的示例包括白板應用和團隊會議軟件。
  • 需要通知的應用。 社交網絡、電子郵件、聊天、游戲、行程警示以及許多其他應用都使用通知。

以下是 ASP.NET Core SignalR 的一些功能:

  • 自動管理連接。
  • 同時向所有連接的客戶端發送消息。 例如,聊天室。
  • 將消息發送到特定的客戶端或客戶端組。
  • 擴展以處理增加的流量。

以上是SignalR介紹,除了以上說明外還有如下優點:

  • 它可支持各個平台通信。只要可以使用websocket就可以進行連接。
  • 而且進行了大量優化處理,非常方便開發者使用。
  • 擁有自由可擴展性,可通過redis 方式搭建分布式socket通信,支持成千上萬人不是夢。
  • 自定義協議,除了json協議外、還支持MessagePack協議。還可以自己定義相關協議進行擴展。
  • 可以用於unity、Layair、白鷺引擎、大數據分析平台等高頻率使用消息實時通信器。

SignalR使用

一、  新建項目,新建一個空Asp.net core 網站

二、引用SignalR中間件

1. 創建Hub中間件 HubHelper,及游戲簡單處理邏輯類

using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;

/// <summary>
/// 消息中心
/// </summary>
public class HubHelper : Hub
{
    /// <summary>
    /// 游戲邏輯
    /// </summary>
    static GameLogic gameLogic;
    static bool isStart = false;


    /// <summary>
    /// 獲取當前用戶Id
    /// </summary>
    /// <param name="data"></param>
    public void GetId(Data data)
    {
        Clients.Caller.SendAsync("GetId", Context.ConnectionId);
    }
    /// <summary>
    /// 獲取當前連接所有用戶
    /// </summary>
    /// <param name="data"></param>
    public void GetALLUser(Data data)
    {
        if (gameLogic != null)
            Clients.Caller.SendAsync("GetALLUser", gameLogic.list);
    }
    /// <summary>
    /// 用戶狀態改變
    /// </summary>
    /// <param name="user">用戶信息</param>
    public void UpdateSate(UserInfo user)
    {
        gameLogic.UpdateUserInfo(Context.ConnectionId, user);
    }
    /// <summary>
    /// 用戶加入
    /// </summary>
    /// <param name="data"></param>
    public void Join(Data data)
    {
        if (!isStart)
        {
            isStart = true;
            gameLogic = new GameLogic(this.Clients.All);
        }
        gameLogic.JoinUser(this.Context.ConnectionId, data);

    }

    /// <summary>
    /// 用戶斷開
    /// </summary>
    /// <param name="exception"></param>
    /// <returns></returns>
    public override Task OnDisconnectedAsync(Exception exception)
    {
        if (gameLogic != null)
            gameLogic.LevelUser(Context.ConnectionId);
        return base.OnDisconnectedAsync(exception);
    }
}
/// <summary>
/// 游戲邏輯
/// </summary>
public class GameLogic
{
    /// <summary>
    /// 全局客戶端通信信息
    /// </summary>
    readonly IClientProxy gloable;
    /// <summary>
    /// 游戲定時器
    /// </summary>
    readonly Timer gameTimer;
    /// <summary>
    /// 所有用戶
    /// </summary>
    public List<UserInfo> list = new List<UserInfo>();

    public GameLogic(IClientProxy _gloable)
    {
        gloable = _gloable;
        //啟動模擬游戲幀
        gameTimer = new Timer(30);
        gameTimer.Elapsed += GameTimer_Elapsed;
        gameTimer.Start();
    }

    private void GameTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        for (int i = 0; i < list.Count; i++)
        {
            var item = list[i];
            //發送已改變狀態用戶信息廣播
            if (item.Change)
            {
                item.Change = false; 
                gloable.SendAsync("update", list[i]);
            }
        }
    }
    /// <summary>
    /// 用戶加入
    /// </summary>
    /// <param name="id">用戶編號</param>
    /// <param name="data">加入信息</param>
    public void JoinUser(string id, Data data)
    {
        //隨機獲取角色圖片
        var user = new UserInfo() { Id = id, Name = data.Name, Role = "role" + new Random().Next(1, 4) + ".jpg", Change = true };
        list.Add(user);
        gloable.SendAsync("add", user);
    }
    /// <summary>
    /// 用戶斷開處理
    /// </summary>
    /// <param name="id">用戶編號</param>
    public void LevelUser(string id)
    {
        var user = list.FirstOrDefault(mm => mm.Id == id);
        if (user != null)
        {
            gloable.SendAsync("level", user);
            list.Remove(user);
        }
    }
    /// <summary>
    /// 更新用戶信息
    /// </summary>
    /// <param name="id">用戶編號</param>
    /// <param name="p">用戶信息</param>
    public void UpdateUserInfo(string id, UserInfo changeUser)
    {
        var user = list.FirstOrDefault(mm => mm.Id == id);
        if (user != null)
        {
            user.X = changeUser.X;
            user.Y = changeUser.Y;
            user.Change = true;
        }
    }
}
/// <summary>
///用戶信息
/// </summary>
public class UserInfo
{
    public string Id { get; set; }
    /// <summary>
    /// 用戶名
    /// </summary>
    public string Name { get; set; }
    /// <summary>
    /// 用戶角色圖片
    /// </summary>
    public string Role { get; set; }
    public int X { get; set; }
    public int Y { get; set; }
    /// <summary>
    /// 是否動作變化
    /// </summary>
    public bool Change { get; set; }
}
/// <summary>
/// 傳輸數據
/// </summary>
public class Data
{
    public string Name { get; set; }
    public string Value { get; set; }
}

 

2. 注冊SignalR中間件,注冊文件瀏覽。

    public class Startup
    {
        
        public void ConfigureServices(IServiceCollection services)
        { 
            services.AddSignalR();
        }
         
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseDefaultFiles().UseStaticFiles(); 
            app.UseSignalR(router => {
                router.MapHub<HubHelper>("/game");
            });
        }
    }

三、前端代碼部分

1.實現 MSGGame.js 游戲消息處理js, 重寫原因是:無代碼提示,而且重寫后方便自己擴展增加功能和相關管理。NetCore SingalR無重連功能,需要自己實現。

//消息處理
var MSG = {
    isStart: false,
    connection: null,

    /**
     * 初始化信息處理 返回promis
     * @param {string} _url 消息地址
     * @param {Function} _func 相關綁定函數
     */
    init: function (url) {
        if (url == null)
            url = "/game";
        MSG.connection = new signalR.HubConnectionBuilder().withUrl(url).build(); 
    },
    /**
     * 注冊監聽函數
     * @param {any} key 監聽鍵名
     * @param {any} func 監聽執行函數
     */
    reg: function (key,func) {
        MSG.connection.on(key, function (result) {
            func(result);
        });
    },
    /**啟動消息 返回promis*/
    start: function () {
        if (MSG.isStart) {
            return;
        }
        MSG.isStart = true;
        var _result = MSG.connection.start();
        _result.then(function (_return) {

        }).catch(function (err) {
            MSG.isStart = false;
            return console.error(err.toString());
        });
        return _result;
    },

   /**
    * 停止消息 返回promis
    * */
    stop: function () {
        if (!MSG.isStart) {
            return;
        }
        var _result = MSG.connection.stop();
        _result.then(function (_return) {

        }).catche(function (err) {

        });
        MSG.isStart = false;
        return _result;
    }, 
    /**
     * 
     * @param {any} api
     * @param {any} msg
     */
    send: function (api, msg) {
        if (!MSG.isStart) {
            return;
        }
        var _result = MSG.connection.invoke(api, msg);
        //回調處理
        _result.then(function (_return) {

        });
        //錯誤處理
        _result.catch(function (_error) {

        });
        return _result;
    },
    /**重新連接 未實現
     *@param  {number} num 嘗試連接次數
     */
    reconnection: function (num) {
        if (MSG.isStart) {

        }
    }
}; 

 

2.在wwwroot下建立index.html 然后編寫簡單邏輯

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
    <style>
        body {
            position: relative;
        }

        .role {
            position: absolute;
        }

        .role, img {
            width: 150px;
            height: 150px;
        }

            .role > p {
                position: absolute;
                bottom: 0;
                left: 0;
                background: rgba(102, 102, 102, 0.73);
                color: red;
                overflow: hidden;
                text-align: center;
                width: 100%;
            }

        #content {
            width: 200px;
            height: 200px;
            overflow-y: scroll
        }
    </style>
</head>

<body>
    <div>
        <div id="clone_role" class="role" style="display: none;">
            <p>克隆對象</p>
            <img src="role1.jpg" />
        </div>
        <input type="text" name="name"    id="userName" placeholder="你的名稱!" />
        <button id="sendButton">開始測試</button>
        <div id="content">
            輸入名稱,點擊開始進行測試。隨后隨便點擊屏幕。
        </div>
    </div> 
    <script src="jquery-1.10.2.min.js"></script>
    <script src="signalr.min.js"></script>
    <script src="game.js"></script>
    <script>
        $(function () { 
            var content = document.getElementById("content");//內容信息
            var $body = $(document.body);//body容器
            var userArray = new Array();//所有用戶
            var userPostion = { id: "", name: "", x: 0, y: 1, start: false }; //本人信息
            var GameMain = {
                //加載所有監聽事件
                load: function () {
                    //狀態更新
                    MSG.reg("update", function (result) {
                        var user = userArray[result.id];
                        if (user) {
                            user.img.style.left = result.x + "px";
                            user.img.style.top = result.y + "px";
                        }
                    });
                    //獲取編號
                    MSG.reg("GetId", function (result) {
                        userPostion.id = result;
                    });
                    //獲取當前連接所有用戶
                    MSG.reg("GetALLUser", function (result) {
                        if (result) {
                            for (var i = 0; i < result.length; i++) {
                                GameMain.joinUser(result[i]);
                            }
                        }
                    });
                    //用戶加入
                    MSG.reg("add", function (result) {
                        content.innerText += result.name + " :用戶加入";
                        GameMain.joinUser(result);
                    });
                    //用戶離開
                    MSG.reg("level", function (result) {
                        content.innerText += result.name + ":離開";
                        GameMain.levelUser(result);
                    });
                },
                //用戶加入展示
                joinUser: function (user) {
                    userArray[user.id] = user;
                    //克隆模板
                    var clone = $("#clone_role").clone();
                    clone.children("img").attr("src", user.role);
                    clone.children("p").text(user.name);
                    userArray[user.id].img = clone[0];
                    $body.append(userArray[user.id].img);
                    clone.slideDown(400);
                },
                //用戶離開
                levelUser: function (p) {
                    var user = userArray[p.id];
                    if (user) {
                        document.body.removeChild(user.img);
                        userArray[p.id] = undefined;
                    }
                },
                //系統事件
                sysEvent: function () {
                    //點擊發送坐標
                    $(document).click(function (e) {
                        userPostion.x = e.clientX;
                        userPostion.y = e.clientY;
                        if (userPostion.start) {
                            MSG.send("UpdateSate", userPostion);
                        }
                    })
                    //點擊發送加入房間信息
                    $("#sendButton").click(function () {
                        if (MSG.isStart && !userPostion.start) {
                            var userName = $("#userName").val();
                            MSG.send("Join", { name: userName, value: "0" });
                            userPostion.start = true;
                        }
                    })
                }
            };

            //初始化函數
            MSG.init("game");
            //加載消息監聽及系統事件
            GameMain.load();
            GameMain.sysEvent();
            //啟動消息
            MSG.start().then(function () {
                MSG.send("GetId", { name: "0", value: "0" });
                MSG.send("GetALLUser", { name: "0", value: "0" });
            });
        })
    </script>
</body>
</html>

 3. 最終目錄結構為:

 

四、運行測試,打開多個網頁。輸入相關名稱。然后點擊屏幕。看看是否能聯動。 可以發給自己好朋友測測看看。

 

五、Signalr問題說明、相關建議經驗

1.Signalr 發布到 winserver 2008R2 時 無法使用websocket通信,需要進行特殊處理。

2.對於實時性要求非常高時,沒有進行高並發處理過。希望處理過的人,通知我下。

3.我這有白鷺引擎、Layaair 連接Signalr 的TS代碼, 也有unity 連接 net core Signalr 代碼(因 unity  我用時只提供了 老版本連接方法,現在沒去看是否有新可用的), 需要的可以聯系我。

4. Net Core 開源后,群眾呼聲很高。無論是機器學習、微服務方面 微軟都在逐步大力支持中, 希望大家共同努力讓.Net 這么好用的東西 走得更遠。

5.曾經不寫博客什么的感覺沒用,不過眼看.net 人員越來越少。    感嘆!

6.如果想實現分布式的可參考官網,自己動手豐衣足食。

以上僅供娛樂測試。代碼地址:https://github.com/840900649/SignalRTest

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM