zoukankan      html  css  js  c++  java
  • 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 {
                 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;
                     100%;
                }
    
            #content {
                 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

  • 相关阅读:
    Mongo 应用查询
    Rocket MQ 问题排查命令
    阿里云部署杂记-节约时间
    linux shell 杂
    垃圾回收算法学习
    Hbase数据读写流程
    TCP 协议相关
    Netty
    ELK
    MiniGUI
  • 原文地址:https://www.cnblogs.com/dwjkj/p/10701642.html
Copyright © 2011-2022 走看看