• SignalR 的应用


    一.应用场景: 在项目中有一个地方需要定时查询数据库是否有数据,如果有则显示在界面上。

    二.可以使用ajax定时查询来做:

    var inter = window.setInterval(refreshData,30000);   //每三十秒调用一次
    function refreshData() {
            $.ajax({
                cache: false,
                type: "POST",
                url: "@(Url.Action("TJInfoData", "TJInfo"))",
                success: function (data) {
                    if (data.code == "200") {
                        //alert(data.msg.Name);
                        //更新数据
                    }
                    else {
                        //alert(data.msg);
                        //无数据时,界面要清数据
                        //基本信息
                        $("#Lsh").text("");
                        
                    }
                },
                error: function(xhr, ajaxOptions, thrownError) {
                    alert(thrownError);
                }
            });
        }
    View Code

    三.ajax定时查询虽然简单明了,但是听说了signalr 后决定试下。

    1. 添加包,nuget 搜索SignaR 安装即可。

    2. 在前端页面添加js:

    <!--Reference the SignalR library. -->
    <script src="~/Scripts/jquery.signalR-2.2.2.js"></script>
    <!--Reference the autogenerated SignalR hub script. -->
    <script src="~/signalr/hubs"></script>
    <!--Reference the StockTicker script. -->
    <script src="~/Scripts/TJDataQueryScript.js"></script>
    View Code

    其中 TJDataQueryScript.js 为利用signaR 进行通信的方法。

    3. 首先创建路由:

    using Microsoft.Owin;
    using Owin;
    
    [assembly: OwinStartup(typeof(OfficialAgentWeb.SignalRHub.Startup))]
    
    namespace OfficialAgentWeb.SignalRHub
    {
        public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                // Any connection or hub wire up and configuration should go here
                app.MapSignalR();
            }
        }
    }
    View Code

    再创建连接器:

    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Data.Domain;
    using Data.Infrastructure;
    using Data.Log4Net;
    using Data.Services;
    using Microsoft.AspNet.SignalR;
    using Microsoft.AspNet.SignalR.Hubs;
    
    namespace OfficialAgentWeb.SignalRHub
    {
        [HubName("TJDataHubMini")]
        public class TJDataHub : Hub
        {
            private readonly TJDataQuery _tjdata;
    
    
            public TJDataHub() : this(TJDataQuery.Instance) { }
    
            public TJDataHub(TJDataQuery stockTicker)
            {
                _tjdata = stockTicker;
            }
    
            public override Task OnConnected()
            {
                var AdminId = Context.QueryString["AdminId"];
                var YewuIsSuper = Context.QueryString["YewuIsSuper"];
                WriteLog.Info("OnConnected: ConnectionId=" + Context.ConnectionId + ",AdminId="+ AdminId + ",YewuIsSuper=" + YewuIsSuper);
                if (!_tjdata.ConnectIdAdminIdDic.ContainsKey(Context.ConnectionId))
                {
                    _tjdata.ConnectIdAdminIdDic.Add(Context.ConnectionId, AdminId);
                }
                if (!_tjdata.ConnectIdIsSuperDic.ContainsKey(Context.ConnectionId))
                {
                    _tjdata.ConnectIdIsSuperDic.Add(Context.ConnectionId, YewuIsSuper);
                }
                //
                int adminIdInt = 0;
                int.TryParse(AdminId, out adminIdInt);
                bool YewuIsSuperBool = YewuIsSuper.ToLower().Equals("true") ? true : false;
                var signalRConnectIdService = EngineContext.Current.Resolve<ISignalRConnectIdService>();
                signalRConnectIdService.Insert(new M_SignalRConnectId { ConnectId = Context.ConnectionId, AdminId = adminIdInt, YewuIsSuper = YewuIsSuperBool, IsDeleted = false, IsActive = false });
    
                return base.OnConnected();
            }
    
    
            public override Task OnDisconnected(bool stopCalled)
            {
                var AdminId = Context.QueryString["AdminId"];
                var YewuIsSuper = Context.QueryString["YewuIsSuper"];
                WriteLog.Info("OnDisconnected: ConnectionId=" + Context.ConnectionId + ",AdminId="+ AdminId + ", YewuIsSuper=" + YewuIsSuper);
                if (_tjdata.ConnectIdAdminIdDic.ContainsKey(Context.ConnectionId))
                {
                    _tjdata.ConnectIdAdminIdDic.Remove(Context.ConnectionId);
                }
                if (_tjdata.ConnectIdIsSuperDic.ContainsKey(Context.ConnectionId))
                {
                    _tjdata.ConnectIdIsSuperDic.Remove(Context.ConnectionId);
                }
                //
                int adminIdInt = 0;
                int.TryParse(AdminId, out adminIdInt);
                var signalRConnectIdService = EngineContext.Current.Resolve<ISignalRConnectIdService>();
                var cidQ = signalRConnectIdService.GetByConnectId(Context.ConnectionId);
                if (cidQ != null)
                {
                    var cidL = cidQ.ToList();
                    if (!(cidL == null || cidL.Count<1))
                    {
                        cidL.ForEach(p => p.IsDeleted = true);
                        signalRConnectIdService.Update(cidL);
                    }
                }
    
                return base.OnDisconnected(stopCalled);
            }
    
            public override Task OnReconnected()
            {
                var AdminId = Context.QueryString["AdminId"];
                var YewuIsSuper = Context.QueryString["YewuIsSuper"];
                WriteLog.Info("OnReconnected: ConnectionId=" + Context.ConnectionId + ",AdminId=" + AdminId + ",YewuIsSuper=" + YewuIsSuper);
                if (!_tjdata.ConnectIdAdminIdDic.ContainsKey(Context.ConnectionId))
                {
                    _tjdata.ConnectIdAdminIdDic.Add(Context.ConnectionId, AdminId);
                }
                if (!_tjdata.ConnectIdIsSuperDic.ContainsKey(Context.ConnectionId))
                {
                    _tjdata.ConnectIdIsSuperDic.Add(Context.ConnectionId, YewuIsSuper);
                }
                //
                int adminIdInt = 0;
                int.TryParse(AdminId, out adminIdInt);
                bool YewuIsSuperBool = YewuIsSuper.ToLower().Equals("true") ? true : false;
                var signalRConnectIdService = EngineContext.Current.Resolve<ISignalRConnectIdService>();
                signalRConnectIdService.Insert(new M_SignalRConnectId { ConnectId = Context.ConnectionId, AdminId = adminIdInt, YewuIsSuper = YewuIsSuperBool, IsDeleted = false, IsActive = false });
    
                return base.OnReconnected();
            }
    
            public IEnumerable<TJDataForTimer> GetAllStocks()
            {
                return null;
            }
    
            [HubMethodName("NewContosoChatMessage")]
            public void NewContosoChatMessage(string cid)
            {
                WriteLog.Info("TJDataQuery--IsAlive--cid=" + cid);
            }
    
        }
    }
    View Code

    创建一个单例来定时发布数据 给客户端:

    using Bll.Admin;
    using Data.Domain;
    using Data.Infrastructure;
    using Data.Log4Net;
    using Data.Services;
    using Microsoft.AspNet.SignalR;
    using Microsoft.AspNet.SignalR.Hubs;
    using OfficialAgentWeb.Controllers;
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using System.Web;
    using System.Web.SessionState;
    
    namespace OfficialAgentWeb.SignalRHub
    {
        public class TJDataQuery
        {
            // Singleton instance
            private readonly static Lazy<TJDataQuery> _instance = new Lazy<TJDataQuery>(() => new TJDataQuery(GlobalHost.ConnectionManager.GetHubContext<TJDataHub>().Clients));
    
            private readonly ConcurrentDictionary<string, TJDataForTimer> _stocks = new ConcurrentDictionary<string, TJDataForTimer>();
    
            private readonly object _updateStockPricesLock = new object();
    
            //stock can go up or down by a percentage of this factor on each change
            private readonly double _rangePercent = .002;
    
            private readonly TimeSpan _updateInterval = TimeSpan.FromSeconds(30); //TimeSpan.FromMilliseconds(250);
            private readonly Random _updateOrNotRandom = new Random();
    
            //private readonly Timer _timer;
            public Timer _timer { get; set; }
            private volatile bool _updatingStockPrices = false;
    
            private TJDataQuery(IHubConnectionContext<dynamic> clients)
            {
                Clients = clients;
                ConnectIdAdminIdDic = new Dictionary<string, string>();
                ConnectIdIsSuperDic = new Dictionary<string, string>();
    
                _stocks.Clear();
    
                _timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);
    
            }
    
            public static TJDataQuery Instance
            {
                get
                {
                    return _instance.Value;
                }
            }
    
            private IHubConnectionContext<dynamic> Clients
            {
                get;
                set;
            }
            //ConnectId 和 AdminId 的字典
            public Dictionary<string, string> ConnectIdAdminIdDic
            {
                get;
                set;
            }
            //ConnectId 和 IsSuperDic 的字典
            public Dictionary<string, string> ConnectIdIsSuperDic
            {
                get;
                set;
            }
    
            public IEnumerable<TJDataForTimer> GetAllStocks()
            {
                //return _stocks.Values;
                return null;
            }
    
            private void UpdateStockPrices(object state)
            {
                lock (_updateStockPricesLock)
                {
                    if (!_updatingStockPrices)
                    {
                        _updatingStockPrices = true;
    
    
                        //from database
                        var signalRConnectIdService = EngineContext.Current.Resolve<ISignalRConnectIdService>();
                        var cidQ = signalRConnectIdService.GetAllConnect();
                        if (cidQ != null)
                        {
                            var cidL = cidQ.ToList();
                            if (!(cidL == null || cidL.Count < 1))
                            {
                                //根据adminid查数据,然后分发推送到客户端。
                                Dictionary<int, List<string>> adminIdDic = new Dictionary<int, List<string>>();
                                foreach (var p in cidL)
                                {
                                    if (!adminIdDic.ContainsKey(p.AdminId))
                                        adminIdDic.Add(p.AdminId, new List<string> { p.ConnectId });
                                    else
                                        adminIdDic[p.AdminId].Add(p.ConnectId);
                                }
                                //
                                foreach (var a in adminIdDic)
                                {
                                    var ayewu = false;
                                    var cidL1 = cidL.Where(x => x.AdminId == a.Key).ToList();
                                    if (!(cidL1 == null || cidL1.Count < 1))
                                    {
                                        ayewu = cidL1[0].YewuIsSuper;
                                    }
                                    var m = GetTJDataQuery(a.Key, ayewu);
                                    //分发
                                    foreach (var connectid in a.Value)
                                    {
                                        WriteLog.Info("BroadcastStockPrice begin: AdminId=" + a.Key + ",connectid="+ connectid);
                                        BroadcastStockPrice(m, connectid);
                                    }
                                }
    
                            }
                        }
    
                        //
                        Clients.All.IsAlive();
    
                        //foreach (var a in ConnectIdAdminIdDic)
                        //{
                        //    var m = GetTJDataQuery(a.Value, a.Key);
                        //    WriteLog.Info("BroadcastStockPrice begin: AdminId=" + a.Value);
                        //    BroadcastStockPrice(m, a.Key);
                        //}
    
                        _updatingStockPrices = false;
                    }
                }
            }
    
            private void BroadcastStockPrice(TJDataForTimer stock, string connectId)
            {
                if (stock == null || (!stock.code.Equals("200")))
                {
                    WriteLog.Info("updateStockPrice begin: connectId=" + connectId + ", 推送空的内容");
                }
                else
                {
                    WriteLog.Info("updateStockPrice begin: connectId=" + connectId + ", 推送了:" + (stock.msg == null ? "" : stock.msg.Name));
                }
                Clients.Client(connectId).updateStockPrice(stock);
                //Clients.All.updateStockPrice(stock);
            }
    
            private TJDataForTimer GetTJDataQuery(int AdminId, bool YewuIsSuperBool)
            {
                TJDataForTimer value = new TJDataForTimer();
                B_TJInfo b = new B_TJInfo();
                //int intAdmin = 0;
                //int.TryParse(AdminId, out intAdmin);
                //审核数量,待审核:0      今天已审核:0      今天审核通过:0      今天审核不通过:0
                if (AdminId<1)
                {
                    WriteLog.Info("GetTJDataQuery AdminId <0: AdminId=" + AdminId);
                    return null;
                }
      
                count_sh count_Sh = b.GetCountSh(AdminId, YewuIsSuperBool);
                if (count_Sh == null)
                {
    
                }
                //
                M_TJInfo m = b.GetNewSingle(AdminId, YewuIsSuperBool);
                if (m == null)
                {
                    WriteLog.Info("GetTJDataQuery GetNewSingle is null");
                    //return null;
                    value.code = "401";
                    value.msgCountsh = count_Sh;
                    return value;
                }
                //TJInfoDetail
                List<M_TJInfoDetail> detailList = b.GetDetail(m.Id);
                if (detailList == null || detailList.Count < 1)
                {
    
                }
    
                //MachineCheck
                List<M_MachineCheck> machineList = b.GetMachine(m.OperateNo);
                if (machineList == null || machineList.Count < 1)
                {
    
                }
    
                value.code = "200";
                value.msg = m;
                value.msgDetail = detailList;
                value.msgMachine = machineList[0];
                value.msgCountsh = count_Sh;
                return value;
                //return Json(new { code = "200", msg = m, msgDetail = detailList, msgMachine = machineList[0], msgCountsh = count_Sh });
            }
    
        }
    }
    View Code

    4. 运行时碰到的问题:

    A. 客户端可以传递参数给服务器:

    $.connection.hub.qs = { 'AdminId': $("#AdminIdHid").val(), 'YewuIsSuper': $("#YewuIsSuperHid").val()};

    在服务端比如在OnConnected()里 使用 var AdminId = Context.QueryString["AdminId"]; 获取值。

    B. 每次连接都会调用OnConnected()方法,失去连接会调用OnDisconnected 方法,由于超时等原因暂时连不上,然后又连上的调用OnReconnected重连,但是重连后connectId会变。

    在OnConnected() 方法里把connectId 保存到数据库里。

    在OnDisconnected 方法里 更新该connectId为失去连接。

    但是在测试过程中,反复刷新该页面,也有可能出现某次OnDisconnected 没有调用的情况。

    所有就会出现数据库里发现有3个connectId 在连接, 但是其实可能只有1个客户端在连接。

    暂时想到的解决方法是:

    服务端启动一个定时器每30分钟调用一次,调用客户端的方法,而客户端的方法是传递connectId到服务端的另外一个方法里。在服务器的该方法里可以根据connectId设置该客户端为活动状态。

    服务器端创建一个任务Task,定时跑,查询连接表,如果发现该connectId 不是活动状态,则更新此数据。

    代码:

    Clients.All.IsAlive();

    服务端调用客户端的IsAlive 方法。

    然后客户端:

    var ticker = $.connection.TJDataHubMini;
    
    ticker.client.IsAlive = function () {
            var clinetConectId = $.connection.hub.id+'';
            console.log('clinetConectId=' + clinetConectId);
            ticker.server.NewContosoChatMessage(clinetConectId).done(function () {
                console.log('IsAliveService done');
            });
        }

    客户端得到 ConectId 后调用服务端的 NewContosoChatMessage 的方法。

    [HubMethodName("NewContosoChatMessage")]
            public void NewContosoChatMessage(string cid)
            {
                WriteLog.Info("TJDataQuery--IsAlive--cid=" + cid);
            }

    可以在 此 NewContosoChatMessage 方法里更新数据表。

    服务端创建任务参见另外一篇文章。

  • 相关阅读:
    data filter 去掉HTML文件中的所有标记
    data filter 去掉HTML文件中的所有标记
    C++ 上溢和下溢(overflow underflow)
    C++ 上溢和下溢(overflow underflow)
    按字节提取整形数值(按位与运算符“&”、右移位运算符“>>”)
    按字节提取整形数值(按位与运算符“&”、右移位运算符“>>”)
    二进制、十进制、N进制 ○| ̄|_
    二进制、十进制、N进制 ○| ̄|_
    JAVA合法标识符
    JAVA合法标识符
  • 原文地址:https://www.cnblogs.com/hpbkin/p/12909732.html
Copyright © 2020-2023  润新知