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、客户端。具体的实现中还要注意一些细节方面的东西,下面就分别说下实现。

服务端

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

站点服务端

添加 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");
        }
    }

宿主服务或客户端

对应的服务代码,加载、组装和启动 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();
            }
        }
    }

持久连接和集线器

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);
        }
    }

客户端

代理和不代理:贴代码

猜你喜欢

转载自www.cnblogs.com/zhurong/p/9710028.html