SignalrR Hub 常量处置

问题描述 投票:0回答:1

我配置了 MS .NET SignalR。它是一个内部集线器,只能通过桌面应用程序连接。集线器在创建时不断被释放,当我尝试从集线器与客户端通信时,我收到已释放的错误。我可以从客户端到集线器进行通信,因此不清楚为什么从集线器到客户端的通信失败。有谁知道为什么会发生这种情况的详细信息吗?

我的调查没有查明原因。我注意到的一件事是集线器 Dispose 操作被不断地调用[在建立连接之后]。仅发出 1 个连接请求,并且在客户端集线器不会断开任何连接 - 因此它在客户端上看起来仍处于连接状态。我检查了集线器析构函数,似乎从未被调用过。

以下是集线器的准备方式(在服务器上):

 public void ConfigureServices(IServiceCollection services)
 {
     services.AddGrpc();
     
     services.AddCors(o =>
     {
         o.AddDefaultPolicy(b =>
         {
             b.WithOrigins(TargetURL)
                 .AllowCredentials()
                 .AllowAnyHeader();
         });
     });

     services.AddHostedService<Worker>();

     services.AddSignalR(hubOptions =>
     {
         hubOptions.EnableDetailedErrors = true;
         hubOptions.ClientTimeoutInterval = TimeSpan.FromSeconds(100);
         hubOptions.KeepAliveInterval = TimeSpan.FromMinutes(100);
     });
 }

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseSwagger();
        app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "oiStockPusherSite v1"));
    }

    app.UseRouting();
    app.UseCors(PolicyName);

    app.UseEndpoints(endpoints =>
    {
        //endpoints.MapControllers(); 
        endpoints.MapHub<TradingServiceHub>("/orderITSignalR", options =>
        {
            options.Transports = HttpTransportType.LongPolling; // you may also need this
        });
    });            
}

以下是HUB的激活方式:

protected virtual bool PrepareTradeActivityHub(string TargetHub)
{            
    string TradeActivitySignalRURL = "";
    bool Res = false;

    lock (PreparingHub)
    {
        TradeActivitySignalRURL = Connection;

        if (!PrepareNewHub)
        {                   
            if (HubConnectionOld == null)
            {                        
                EventLog.WriteEntry("Support", string.Format("Prepare Trade Service Hub for {0}", TradeActivitySignalRURL), EventLogEntryType.Information);

                HubConnectionOld = new Microsoft.AspNet.SignalR.Client.HubConnection(TradeActivitySignalRURL);
                HubConnectionOld.Closed += TradingClientHubConnection_Closed;

                ClientHubProxy = HubConnectionOld.CreateHubProxy(TargetHub);

                Task<bool> Result = TradingClientHubConnection_Connect();
                Result.Wait();

                if (!Result.Result)
                {
                    EventLog.WriteEntry("Support", "Failed to start Trade Service Hub", EventLogEntryType.Error);

                    StopHubConnection();
                }
                else
                {
                    EventLog.WriteEntry("Support", "Connected to Trade Service Hub", EventLogEntryType.Error);
                    
                    Result = ClientHubProxy?.Invoke<bool>("Active");
                    if (Result != null)
                    {
                        Result.Wait();
                        Res = Result.Result;
                        CallCheckActivated();                                
                        HubConnected = true;
                    }
                    else
                        Res = false;
                }
            }
            else
            {
                EventLog.WriteEntry("Support", "Check if Hub still connectedHub", EventLogEntryType.Information);

                Res = true;
                if (ClientHubProxy != null)
                {
                    Task.Run(async () =>
                    {
                        if (ClientHubProxy != null)
                        {
                            EventLog.WriteEntry("Support", "Test calling Hub Active operation", EventLogEntryType.Information);

                            Res = await ClientHubProxy?.Invoke<bool>("Active");

                            if (Res)
                            {                                        
                                CallCheckActivated();
                                EventLog.WriteEntry("Support", "Successfully called Hub Active operation", EventLogEntryType.Information);
                            }
                            else
                            {
                                EventLog.WriteEntry("Support", "Problem calling Hub Active operation", EventLogEntryType.Error);
                            }
                        }
                    }
                    ).Wait();
                }
                else
                {
                    EventLog.WriteEntry("Support", "Hub not avaible - should be", EventLogEntryType.Error);
                }
            }
        }
        else
        {                    
            if (HubConnectionNew == null)
            {
                EventLog.WriteEntry("Support", string.Format("Prepare Trade Service Hub for {0}", TradeActivitySignalRURL), EventLogEntryType.Information);

                HubConnectionNew = new HubConnectionBuilder().WithUrl(TradeActivitySignalRURL).Build();
                HubConnectionNew.Closed += StockPusherHubDisconnect;
               var Connect = StockPusherHubConnection_Connect();
                Connect.Wait();

                EventLog.WriteEntry("Support", "Test calling Hub Active operation", EventLogEntryType.Information);

                var Response = HubConnectionNew.InvokeAsync<bool>("Active").Result;
                if (!Response)
                {
                    HubConnectionNew = null;
                    EventLog.WriteEntry("Support", "Problem calling Hub Active operation", EventLogEntryType.Error);
                    HubConnected = false;
                }
                else
                {
                    EventLog.WriteEntry("Support", "Successfully called Hub Active operation", EventLogEntryType.Information);
                    Res = true;
                    HubConnected = true;
                    HubConnectionNew.ServerTimeout = TimeSpan.FromSeconds(100000);
                    
                }                        
            }
            else
            {
                EventLog.WriteEntry("Support", "Test calling Hub Active operation", EventLogEntryType.Information);

                var Response = HubConnectionNew.InvokeAsync<bool>("Active").Result;
                if (!Response)
                {
                    HubConnectionNew = null;
                    EventLog.WriteEntry("Support", "Problem calling Hub Active operation", EventLogEntryType.Error);
                    HubConnected = false;
                }
                else
                {
                    EventLog.WriteEntry("Support", "Successfully called Hub Active operation", EventLogEntryType.Information);
                    Res = true;
                    HubConnected = true;
                }
            }
        }
    }
    return Res;
}

protected virtual async Task StockPusherHubConnection_Connect()
{
    Exception exception = null;
    Exception except = null;

    try
    {
        if (HubConnectionNew != null)
        {
            lock (PreparingHub)
            {
                EventLog.WriteEntry("Support", string.Format("Attempt to connect to Hub for {0}", Connection), EventLogEntryType.Information);

                var HubConnect = HubConnectionNew?.StartAsync();
                HubConnect?.Wait();
                HubConnect?.ContinueWith(
                    task =>
                    {
                        try
                        {
                            if (task != null && task.IsFaulted)
                            {
                                EventLog.WriteEntry("Support", "Problem connecting to Hub for", EventLogEntryType.Error);
                                EventLog.WriteEntry("Support", task.Exception.Message, EventLogEntryType.Error);

                                exception = task?.Exception;
                                except = exception?.InnerException;
                                while (except != null)
                                {
                                    except = except?.InnerException;
                                    if (except != null)
                                        EventLog.WriteEntry("Support", except.Message, EventLogEntryType.Error);
                                }
                            }
                        }
                        catch { }
                    }
                ).Wait();
            }
        }

    }
    catch (Exception ex)
    {
    }
}

作为补充。我制作了一个测试应用程序来演示这一观察结果。它可用于 https://github.com/FutureInfinite/SignalRTest

应用中有一个回调客户端的操作,叫做 留言

无法调用此操作,因为 HUB 似乎总是被释放。如果您可以尝试一下,看看是否可以确定集线器长时间不活动的原因,那就太好了。 WPF 应用程序有一个按钮,需要选择该按钮才能激活 Hub。

谢谢 彼得

signalr asp.net-core-signalr reconnect
1个回答
0
投票

在 SignalR 中,Hub 类是瞬态的。每次客户端连接或调用 Hub 方法时,都会在请求完成后创建并处置一个新的 Hub 实例。因此,您不应在 Hub 构造函数中启动长时间运行的任务,因为 Hub 实例可能会在任务完成之前被释放,从而导致“已释放”错误。

为了方便大家测试,我再次提供完整的测试代码,如下,一切正常。直接运行即可。

enter image description here

添加新的后台服务,这是项目结构。

enter image description here

1.OrderITHub.cs

using Microsoft.AspNetCore.SignalR.Client;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace TestClient
{
    public class OrderITHubProxy : BindableBase
    {
        #region "Properties & Attributes"                
        protected Microsoft.AspNetCore.SignalR.Client.HubConnection ClientHubConnectionNew;
        protected static Microsoft.AspNetCore.SignalR.Client.HubConnection ClientHubConnectionNewSinlge;
        protected bool UseSingle { get; set; } = false;

        protected Microsoft.AspNetCore.SignalR.Client.HubConnection HubConnectionNew
        {
            get
            {
                if (UseSingle == null)
                    return ClientHubConnectionNew;
                else
                    return ClientHubConnectionNewSinlge;
            }
            set
            {
                //the use of a single hub connection requires
                //that the hub connecion is to the same HUB
                if (UseSingle == null)
                    ClientHubConnectionNew = value;
                else
                    ClientHubConnectionNewSinlge = value;
            }
        }

        //protected static Microsoft.AspNet.SignalR.Client.IHubProxy ClientHubProxy { get; set; } = null;
        protected static object PreparingHub = new object();
        protected bool Continue { get; set; } = true;
        protected bool ActivatingTradeInterface = false;
        protected static bool HubConnected = false;
        protected string Connection;

        public delegate void ClientHubActive();
        public event ClientHubActive ClientHubActiveated;

        private static System.Timers.Timer HubConnectionTimer;
        private static object WaitToActivateHub = new object();
        #endregion //"Properties & Attributes"

        #region "Lifetime"
        public OrderITHubProxy(
            string Connection,
            bool UseSingle = false
            )
        {
            System.Diagnostics.EventLog.WriteEntry("Support", string.Format("Start Hub connection for {0}", Connection), System.Diagnostics.EventLogEntryType.Information);

            HubConnectionTimer = new System.Timers.Timer();

            this.Connection = Connection;
            this.UseSingle = UseSingle;

            ///simple thread logic to start up 
            ///and maintain the connection
            ///to the trading interface
            //HubConnectionTimer.Elapsed += PrepareTradeActivityClient;            
            //HubConnectionTimer.Start();
            PrepareTradeActivityClient(null, null);
        }

        private void TestApp()
        {

        }

        ~OrderITHubProxy()
        {
            StopTradeClientInterface();
        }
        #endregion "Lifetime"

        #region "Operations"

        virtual protected void PrepareTradeActivityClient(object sender, System.Timers.ElapsedEventArgs e)
        {
            Task.Factory.StartNew(
                   async () =>
                   {
                       while (Continue)
                       {
                           try
                           {
                               ActivateTradeClientInterface();
                           }
                           finally
                           {
                               ///5sec interval for trading interface refresh
                               Thread.Sleep(5000);
                           }
                       }
                   }
                   , TaskCreationOptions.LongRunning
               );

            //HubConnectionTimer.Interval = 5000;            
        }

        protected virtual bool PrepareTradeActivityHub()
        {
            string TradeActivitySignalRURL = "";
            bool Res = false;

            lock (PreparingHub)
            {
                TradeActivitySignalRURL = Connection;

                if (HubConnectionNew == null)
                {
                    System.Diagnostics.EventLog.WriteEntry("Support", string.Format("Prepare Trade Service Hub for {0}", TradeActivitySignalRURL), System.Diagnostics.EventLogEntryType.Information);


                    HubConnectionNew = new Microsoft.AspNetCore.SignalR.Client.HubConnectionBuilder().WithUrl(TradeActivitySignalRURL).Build();
                    HubConnectionNew.Closed += StockPusherHubDisconnect;
                    var Connect = StockPusherHubConnection_Connect();
                    Connect.Wait();

                    System.Diagnostics.EventLog.WriteEntry("Support", "Test calling Hub Active operation", System.Diagnostics.EventLogEntryType.Information);

                    var Response = HubConnectionNew.InvokeAsync<bool>("Active").Result;
                    if (!Response)
                    {
                        HubConnectionNew = null;
                        System.Diagnostics.EventLog.WriteEntry("Support", "Problem calling Hub Active operation", System.Diagnostics.EventLogEntryType.Error);
                        HubConnected = false;
                    }
                    else
                    {
                        System.Diagnostics.EventLog.WriteEntry("Support", "Successfully called Hub Active operation", System.Diagnostics.EventLogEntryType.Information);
                        Res = true;
                        HubConnected = true;
                        HubConnectionNew.ServerTimeout = TimeSpan.FromSeconds(100000);

                    }
                }
                else
                {
                    System.Diagnostics.EventLog.WriteEntry("Support", "Test calling Hub Active operation", System.Diagnostics.EventLogEntryType.Information);

                    var Response = HubConnectionNew.InvokeAsync<bool>("Active").Result;
                    if (!Response)
                    {
                        HubConnectionNew = null;
                        System.Diagnostics.EventLog.WriteEntry("Support", "Problem calling Hub Active operation", System.Diagnostics.EventLogEntryType.Error);
                        HubConnected = false;
                    }
                    else
                    {
                        System.Diagnostics.EventLog.WriteEntry("Support", "Successfully called Hub Active operation", System.Diagnostics.EventLogEntryType.Information);
                        Res = true;
                        HubConnected = true;
                    }
                }
            }
            return Res;
        }

        public virtual void TradingClientHubConnection_Closed()
        {
            StopHubConnection();
        }

        protected virtual void StopHubConnection()
        {
            try
            {
                if (HubConnectionNew != null && HubConnectionNew.State == HubConnectionState.Connected)
                {
                    //HubConnectionNew.StopAsync().Wait();
                    lock (PreparingHub)
                    {
                        HubConnectionNew = null;
                    }
                }
            }
            catch { }
            finally
            {
                HubConnected = false;
            }
        }

        private void StopTradeClientInterface()
        {
            try
            {
                StopHubConnection();
            }
            finally { }

        }

        /// <summary>
        /// activate trading services        
        /// </summary>
        protected virtual bool ActivateTradeClientInterface()
        {
            bool bRes = false;

            if (!ActivatingTradeInterface)
            {
                try
                {
                    ActivatingTradeInterface = true;

                    System.Diagnostics.EventLog.WriteEntry("oiTrader Client", "Attempt to connect to Trader Hub", System.Diagnostics.EventLogEntryType.Information);

                    if (PrepareTradeActivityHub())
                    {
                        bRes = true;
                        Action TestRequestResult = TestApp;
                        HubConnectionNew.On("TestOp", TestRequestResult);
                        HubConnectionNew.On("TestOp", () =>
                        {
                            MessageBox.Show("Receive a TestOp message from the server");
                        });

                    }
                    else
                        StopHubConnection();
                }
                catch (Exception ex)
                {
                    StopHubConnection();
                }
                finally { ActivatingTradeInterface = false; }
            }
            else
                bRes = HubConnected;

            return bRes;
        }

        protected virtual Task StockPusherHubDisconnect(Exception arg)
        {
            StockPusherHubConnection_Connect().Wait();

            return null;
        }

        protected virtual async Task StockPusherHubConnection_Connect()
        {
            Exception exception = null;
            Exception except = null;

            try
            {
                if (HubConnectionNew != null)
                {
                    lock (PreparingHub)
                    {
                        System.Diagnostics.EventLog.WriteEntry("Support", string.Format("Attempt to connect to Hub for {0}", Connection), System.Diagnostics.EventLogEntryType.Information);

                        var HubConnect = HubConnectionNew?.StartAsync();
                        HubConnect?.Wait();
                        HubConnect?.ContinueWith(
                            task =>
                            {
                                try
                                {
                                    if (task != null && task.IsFaulted)
                                    {
                                        System.Diagnostics.EventLog.WriteEntry("Support", "Problem connecting to Hub for", System.Diagnostics.EventLogEntryType.Error);
                                        System.Diagnostics.EventLog.WriteEntry("Support", task.Exception.Message, System.Diagnostics.EventLogEntryType.Error);

                                        exception = task?.Exception;
                                        except = exception?.InnerException;
                                        while (except != null)
                                        {
                                            except = except?.InnerException;
                                            if (except != null)
                                                System.Diagnostics.EventLog.WriteEntry("Support", except.Message, System.Diagnostics.EventLogEntryType.Error);
                                        }
                                    }
                                }
                                catch { }
                            }
                        ).Wait();
                    }
                }

            }
            catch (Exception ex)
            {
            }
        }

        protected virtual void StopStockPusher()
        {
            try
            {
                if (HubConnectionNew != null && HubConnectionNew.State == HubConnectionState.Connected)
                    HubConnectionNew.StopAsync();
            }
            catch { }

            HubConnectionNew = null;
        }

        protected void CallCheckActivated()
        {
            if (ClientHubActiveated != null) ClientHubActiveated();
        }
        #endregion //"Operations"
    }
}

2.MessageBackgroundService.cs

using Microsoft.AspNetCore.SignalR;

namespace TestClientServer
{
    public class MessageBackgroundService : BackgroundService
    {
        private readonly IHubContext<TheHub> _hubContext;

        public MessageBackgroundService(IHubContext<TheHub> hubContext)
        {
            _hubContext = hubContext;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                await Task.Delay(1000, stoppingToken);

                await _hubContext.Clients.All.SendAsync("TestOp", cancellationToken: stoppingToken);
            }
        }
    }
}

3.Startup.cs

using Microsoft.AspNetCore.Http.Connections;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestClientServer
{                  
    internal class Startup
    {
        #region Properties&Attributes
        public static string ListenPort { get; private set; }
        public static string TargetURL { get; private set; }
        private static string PolicyName { get { return "Trading Client Policy"; } }
        #endregion Properties&Attributes

        #region Lifetime
        static Startup()
        {
#if DEBUG
            ListenPort = "20";
            Console.WriteLine(string.Format("HUB URL {0}", ListenPort));
#else
            ListenPort = System.Configuration.ConfigurationManager.AppSettings["TradeClientSignalRURL"];
#endif

            TargetURL = string.Format("http://localhost:{0}", ListenPort);
            Console.WriteLine(string.Format("orderIT HUB listenining on {0}", TargetURL));
        }

        #endregion Lifetime

        #region Operations   
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddGrpc();

            services.AddCors(o =>
            {
                o.AddDefaultPolicy(b =>
                {
                    b.WithOrigins(TargetURL)
                        .AllowCredentials()
                        .AllowAnyHeader();
                });
            });

            //services.AddHostedService<Worker>();

            services.AddSignalR(hubOptions =>
            {
                //hubOptions.EnableDetailedErrors = true;
                hubOptions.ClientTimeoutInterval = TimeSpan.FromSeconds(100);
                //hubOptions.KeepAliveInterval = TimeSpan.FromMinutes(1);
            });
            services.AddHostedService<MessageBackgroundService>();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                //app.UseSwagger();
                //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "oiStockPusherSite v1"));
            }

            app.UseRouting();
            app.UseCors(PolicyName);

            app.UseEndpoints(endpoints =>
            {
                //endpoints.MapControllers(); 
                endpoints.MapHub<TheHub>("/TestHub", options =>
                {
                    options.Transports = HttpTransportType.LongPolling; // you may also need this
                });
            });
        }

        #endregion Operations
    }
}

4.TheHub

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

namespace TestClientServer
{
    public class TheHub : Microsoft.AspNetCore.SignalR.Hub
    {
        bool IsDisposed = true;
        private Task CallTask;
        public TheHub()
        {         
        }

        protected override void Dispose(bool disposing)
        {
            IsDisposed = true;
            base.Dispose(disposing);
        }

        public override Task OnConnectedAsync()
        {
            IsDisposed = false;
            return base.OnConnectedAsync();
        }

        public void PostMessage(object sender, ElapsedEventArgs e)
        {
            if (!IsDisposed)
                Clients?.All.SendAsync("TestOp");                                                                        
        }

        public bool Active() => true;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.