我配置了 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 中,Hub 类是瞬态的。每次客户端连接或调用 Hub 方法时,都会在请求完成后创建并处置一个新的 Hub 实例。因此,您不应在 Hub 构造函数中启动长时间运行的任务,因为 Hub 实例可能会在任务完成之前被释放,从而导致“已释放”错误。
为了方便大家测试,我再次提供完整的测试代码,如下,一切正常。直接运行即可。
添加新的后台服务,这是项目结构。
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;
}
}