我有一个 Web 应用程序,它有一个从 BackgroundService 派生的托管服务,该服务应该定期轮询 Azure 以获取某些数据,并在找到时通过 SignalR 将数据发送到客户端。
我有 2 个客户端:一个 Web 客户端(Razor Pages + javascript),用于获取消息并显示数据。
我正在开发的第二个客户端是桌面客户端。
在网络端,我的监控服务中的轮询功能进行 signalR 调用来传递数据:
var connectionId = _connectionMappingService.GetConnectionId(userName);await
IndexStats? currentStats = await _indexStatsService.GetIndexStatsAsync(searchIndexName);
_hubContext.Clients.Client(connectionId).SendAsync("IndexStats", currentStats);
我确认已拨打电话,并且数据正确。 “currentStats”是一个复杂的对象。为了简洁起见,我在这里简化了代码......
这里的_hubContext是在服务构造函数中通过DependencyInjection注入的:
public IndexStatsPolling(ILogger<IndexStatsPolling> logger,
IHubContext<NotificationHub> hubContext,
IConnectionMappingService connectionMappingService,
IAzureSettings azureSettings,
IIndexStatsService indexStatsService)
{
_logger = logger;
_hubContext = hubContext;
_connectionMappingService = connectionMappingService;
_azureSettings = azureSettings;
_indexStatsService = indexStatsService;
}
在桌面端我有 SignalRClient 类:
public class SignalRClient : ISignalRCleint
{
private HubConnection hubConnection;
public event Action<IndexStats> OnIndexStatsReceived;
public SignalRClient(string url)
{
Init(url);
}
private void Init(string url)
{
using (Log.VerboseCall())
{
string err = "Failed to connect to hub or set event handler";
try
{
hubConnection = new HubConnectionBuilder().WithUrl(url).Build();
if (String.IsNullOrWhiteSpace(url))
{
throw new Exception(err);
}
Log.VerboseFormat($"Created hubConnection for URL: {url}");
hubConnection.KeepAliveInterval = new TimeSpan(0,0,5);
hubConnection.HandshakeTimeout = new TimeSpan(0,2,0);
// "IndexStats" is just an endpoint alias
hubConnection.On<IndexStats>("IndexStats", (indexStats) =>
{
// Handle the received IndexStats object
// For example, updating the UI or processing the data
OnIndexStatsReceived?.Invoke(indexStats);
});
}
catch (Exception ex)
{
Log.Verbose(ex);
throw new Exception(err);
}
}
}
public async Task ConnectAsync()
{
using(Log.VerboseCall())
{
try
{
await hubConnection.StartAsync();
Log.VerboseFormat($"Connection state: {hubConnection.State}");
}
catch(Exception ex)
{
Log.Verbose(ex);
}
}
}
public async Task DisconnectAsync()
{
if (hubConnection != null && hubConnection.State == HubConnectionState.Connected)
{
await hubConnection.StopAsync();
}
}
public event Action<string> OnMessageReceived;
public async Task SendMessageAsync(string message)
{
// Implementation to send message...
}
}
在桌面上,SignalR 已在 Program.cs 中注册:
// Add SignalRCleint
services.AddScoped<SignalRClient, SignalRClient>(provider =>
{
var azConfig = provider.GetRequiredService<IAzConfig>();
var signalRHubUrl = azConfig.AzureADDesktop.SignalRHubUrl; // Now this should have the correct value
return new SignalRClient(signalRHubUrl);
});
然后在MainForm.cs中:
private async void MainForm_Load(object sender, EventArgs e)
{
_signalRClient.OnIndexStatsReceived += SignalRClient_OnIndexStatsReceived;
await _signalRClient.ConnectAsync();
}
private void SignalRClient_OnIndexStatsReceived(IndexStats indexStats)
{
using (Log.VerboseCall())
{
string indexName = indexStats.IndexName;
UpdateGlobalStats(UpdateType.IngestData, indexName, indexStats);
}
}
底线是 SignalRClient_OnIndexStatsReceived 永远不会被调用...
在客户端,我使用以下 NuGet 包: Microsoft.AspNetCore.SignalR.Client 8.0.0 Microsoft.AspNetCore.SignalR.Client.Core 8.0.0
在网络应用程序方面: Microsoft.AspNetCore.SignalR.Core 1.0.15(最新稳定版本)
如果有人对此有任何线索,我会很高兴提供任何解决方案、提示、指针 - 我花了很多时间试图解决它但徒劳......
最终是库不兼容的问题。我的桌面客户端根本没有通过 signalR 服务器进行身份验证,因此在内部,尽管客户端看到了初始连接状态,但服务器中的连接立即断开。