zoukankan      html  css  js  c++  java
  • SignalR 初体验

    目录

    一、前言

    微软官方给的说明:ASP.NET SignalR是ASP.NET开发人员的新库,可以轻松开发实时Web功能。SignalR允许服务器和客户端之间的双向通信。服务器现在可以在连接的客户端可用时立即将其推送到连接的客 SignalR支持Web套接字,并且可以回退到旧版浏览器的其他兼容技术。SignalR包括用于连接管理的API(例如,连接和断开事件),分组连接和授权。来源:https://www.asp.net/signalr;可以从这里找到SignalR的简介、教程等,以及在最新的平台 .NET Core 2.1 中使用。

    下面就说说自己在实际操作中遇到的一些问题或者所悟。

    首先说一下当时为什么选择SignalR。

    1. 需求:项目中需要知道每个人的实时待办数量的数据;
    2. 原先的解决方案:后台定时去计算这些待办数量存库,前端定时的请求接口进行查询,这样有一些问题,首先效率的问题,还有就是实时性问题,后面考虑改变方案;
    3. 新的解决方案:在案件有变动的时候,触发计算该案件影响到的人员的代办数量,并实时发送到客户端,这里实时发送用到的就是SignalR;

    已经确定了方案,那就是按照这个方案的思路来具体的实现。细分开来里面有这两个大的部分:1、服务端;2、客户端。具体的实现中还要注意一些细节方面的东西,下面就分别说下实现。

    二、服务端

    服务类型:发布站点,宿主服务、客户端

    2.1、站点服务端

    添加 Startup 类,对应代码(注册服务,启动 SignalR 服务)

    public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888
                app.UseCors(CorsOptions.AllowAll);
    
                var hubConfig = new HubConfiguration() { EnableJavaScriptProxies = false };
                app.MapSignalR(hubConfig);
    
                // 配置集群底板数据库
                RedisConfiguration redisConfig = RedisSectionHandler.GetConfig();
                GlobalHost.DependencyResolver.UseRedis(redisConfig.RedisHosts[0].Host, redisConfig.RedisHosts[0].Port, redisConfig.Password, "signalR_loginHub");
            }
        }

    2.2、宿主服务或客户端

    对应的服务代码,加载、组装和启动 Web 应用程序

    public class SignalRServer
        {
            private IDisposable app;
            private static string domain = "http://localhost:10110";
    
            static SignalRServer()
            {
                domain = ConfigurationManager.AppSettings["Domain"] ?? domain;
                Console.WriteLine("获取配置:" + domain);
            }
    
            public void StartSignalR()
            {
                Console.WriteLine("消息服务运行在:" + domain);
    
                app = WebApp.Start(domain, builder =>
                {
                    //// 该值表示连接在超时之前保持打开状态的时间长度。
                    ////默认为110秒
                    //GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(110);
    
                    ////该值表示在连接停止之后引发断开连接事件之前要等待的时间长度。
                    ////默认为30秒
                    //GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(30);
    
                    ////用于表示两次发送保持活动消息之间的时间。如果启用,此值必须至少为两秒。设置为 null 可禁用。
                    ////默认为10秒,设置DisconnectTimeout后默认为DisconnectTimeout的3分之一
                    //GlobalHost.Configuration.KeepAlive = TimeSpan.FromSeconds(10);
    
                    builder.UseCors(CorsOptions.AllowAll);
                    builder.MapSignalR(new HubConfiguration
                    {
                        EnableJSONP = true,
                        EnableDetailedErrors = true,
                        EnableJavaScriptProxies = true
                    });
                });
            }
    
            public void StopSignalR()
            {
                if (app != null)
                {
                    app.Dispose();
                }
            }
        }

    2.3、持久连接和集线器

    signalR内部有两类对象:

    1. Http持久连接(Persisten Connection)对象:用来解决长时间连接的功能。还可以由客户端主动向服务器要求数据,而服务器端不需要实现太多细节,只需要处理PersistentConnection 内所提供的五个事件:OnConnected, OnReconnected, OnReceived, OnError 和 OnDisconnect 即可。
    2. Hub(集线器)对象:用来解决实时(realtime)信息交换的功能,服务端可以利用URL来注册一个或多个Hub,只要连接到这个Hub,就能与所有的客户端共享发送到服务器上的信息,同时服务端可以调用客户端的脚本。SignalR将整个信息的交换封装起来,客户端和服务器都是使用JSON来沟通的,在服务端声明的所有Hub信息,都会生成JavaScript输出到客户端,.NET则依赖Proxy来生成代理对象,而Proxy的内部则是将JSON转换成对象。

    这里贴下在项目中用到的集线器的用法

    public class MyHub : Hub
        {public override Task OnConnected()
            {
                LogHelper.WriteInfo($"用户ConnectionId是:{Context.ConnectionId},连接到服务器。");
    
                string userId = Context.QueryString["userId"];
                // 业务代码处理return base.OnConnected();
            }
    
            /// <summary>
            /// 超时或者断开连接时调用
            /// </summary>
            /// <param name="stopCalled">客户端正常关闭:true,超时连接:false</param>
            /// <returns></returns>
            public override Task OnDisconnected(bool stopCalled)
            {
                LogHelper.WriteInfo($"用户ConnectionId是:{Context.ConnectionId},断开连接。");
    
                // 业务代码处理return base.OnDisconnected(stopCalled);
            }
    
            /// <summary>
            /// 服务器端发送消息的方法
            /// </summary>
            /// <param name="msg"></param>
            public void Send(string msg)
            {
                // 调用所有客户注册的本地JS方法
                Clients.All.receive(msg);
            }
        }
    
        /// <summary>
        /// 服务端主动向浏览器发送消息(向单独用户发送消息)
        /// </summary>
        public class SignalrServerToClient
        {
            static readonly IHubContext _myHubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
    
            public static void SendAll(string connectionId, string message)
            {
                _myHubContext.Clients.All.receive(message);
            }
    
            public static void SendToUser(string connectionId, string message)
            {
                _myHubContext.Clients.Client(connectionId).receive(message);
            }
        }

    三、客户端

    3.1、使用代理客户端

    在使用 Hub类的实时,客户端的类名第一个字母要小写:MyHub =》myHub

    使用代理的代码如下:

    <!DOCTYPE html>
    <html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>SignalR Test</title>
        <script src="Scripts/jquery-3.3.1.min.js"></script>
        <script src="Scripts/jquery.signalR-2.3.0.min.js"></script>
        <script src="http://localhost:10110/signalr/hubs"></script>   // 要添加这个引用才能使用代理
        <link href="CSS/Index.css" rel="stylesheet" />
    </head>
    <body>
        <div class="box">
            <ul id="list"></ul>
            <div id="userBox" class="sendBox">
                <div>
                    <textarea id="user"></textarea>
                    <button id="btn_senduser">发送用户信息</button>
                </div>
            </div>
            <div id="msgBox" class="sendBox" style="display:none">
                <div>
                    <textarea id="msg"></textarea>
                    <button id="btn_send">发送</button>
                </div>
            </div>
        </div>
    </body>
    </html>
    <script type="text/javascript">
        $(function () {
            $.connection.hub.url = "http://localhost:10110/signalr";
            $.connection.hub.qs = { "userId": 66 };
    
            var chat = $.connection.myHub;
    
            chat.client.receive = function (msg) {
                var $list = $("#list");
                if ($list.find("li").length <= 0) {
                    $list.append("<li>" + msg + "</li>");
                } else {
                    $("<li>" + msg + "</li>").insertBefore("#list li:first");
                }
            };
    
            $.connection.hub.start().done(function (data) {
                $("#list").append("<li>连接情况:" + data + "我的ID是:" + data.id + "</li>");
    
                $("#btn_senduser").bind("click", function () {
                    chat.server.receive($("#user").val());
                    $("#user").val("");
                    $("#userBox").hide();
                    $("#msgBox").show();
                });
    
                $("#btn_send").bind("click", function () {
                    chat.server.send($("#msg").val());
                    $("#msg").val("");
                });
    
                $("#msg").bind("keyup", function (e) {
                    if (e.keyCode === 13) {
                        chat.server.send($("#msg").val());
                        $("#msg").val("");
                    }
                });
    
            }).fail(function () {
                alert("实时消息服务连接失败!");
            });
    
        });
    </script>

     3.2、不使用代理客户端

    不使用代理的客户端代码:

    <html>
    
    <head>
        <script src="Scripts/jquery-1.6.4.js" type="text/javascript"></script>
        <script src="Scripts/jquery.signalR-2.3.0.min.js" type="text/javascript"></script>
        <script type="text/javascript">
            $(function () {
                var loginhub = null;
    
                $("#btnLogin").click(function () {
                    if ($("#txtUserId").val() == "") {
                        alert("请填写 userid");
                        return;
                    }
    
                    var conn = $.hubConnection("http://192.168.199.71:10200/signalr");
                    conn.qs = {"userid": $("#txtUserId").val()};
                    loginhub = conn.createHubProxy('loginHub');
                    loginhub.on("beLogout", function (message) {
                        $("#msg").html(message);
                    });
    
                    conn.start().done(function (data) {
                        $("#msg").html("登陆成功,connectionid:" + data.id);
                    });
                });
    
                $("#btnLogout").click(function () {
                    if (loginhub == null) {
                        alert("没有登陆,不能进行退出操作");
                        return;
                    }
                    loginhub.invoke("logout", $("#txtUserId").val()).done(function () {
                        $("#msg").html("退出成功");
                    });
                });
    
            });
        </script>
    </head>
    
    <body>
        userid:<input id="txtUserId" /><span id="msg"></span>
        <br />
        <button id="btnLogin">登陆</button><br />
        <button id="btnLogout">退出</button><br />
    </body>
    
    </html>

    这两者的区别:

    1、使用代理要在头部添加这样的引用—— <script src="http://localhost:10110/signalr/hubs"></script> ;

    2、使用代理可以直接点出方法,不使用代理要自己手动绑定方法;

  • 相关阅读:
    C#中的接口和类的不同点
    值类型和引用类型的区别?
    时隔两年再次操刀NPOI合并单元格
    二.Docker下安装和运行Mysql
    一.CentOS8下的Docker安装
    .NetCore3.1使用Autofac
    .NET Core 3.1使用Swagger
    数组排序和数组对象排序
    C# 操作Excel , 支持超链接 跳转Sheet 页面,HSSFHyperlink函数
    MVC导入Excel通过NPOI
  • 原文地址:https://www.cnblogs.com/zhurong/p/9710028.html
Copyright © 2011-2022 走看看