ASP.. NET Core SignalR使用多个Hub实例



我使用。net 5的Blazor WebAssembly,我有一个MultipleCameraLive组件,它嵌入了4个SingleCameraLive组件。每个组件代表来自给定摄像机的帧流。通过点击属于给定相机的帧,用户将跳转到一个页面(让我们称之为SingleCameraFocus),其中只显示来自该相机的流。

从摄像机获取图像后,服务器通过SignalR发送图像(服务器端发起的通信)。

现在,出于模块化的原因,我认为最好的方法是有一个单一的Hub类和"多个实例"。它。换句话说,在服务器中,我有这样的东西

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
[...]
app.UseEndpoints(endpoints => {
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapFallbackToFile("index.html");
endpoints.MapHub<CameraLiveModeHub>("/cameraLiveHub_0");
endpoints.MapHub<CameraLiveModeHub>("/cameraLiveHub_1");
endpoints.MapHub<CameraLiveModeHub>("/cameraLiveHub_2");
endpoints.MapHub<CameraLiveModeHub>("/cameraLiveHub_3");
});
CameraLiveModeHub.Context = app.ApplicationServices.GetService<IHubContext<CameraLiveModeHub>>();
[...]

客户端没有问题,因为我可以使用上面的Uri连接到给定的Hub。但是,当我通过Hub上下文从服务器发送消息时,我没有这种可能性。事实上,据我所知,它不像有4个Hub:只有一个,有四个Uri"指向"。去那个中心。实际上这里可以读到

hub是瞬态的:不要将状态存储在hub类的属性中。每个hub方法调用都在一个新的hub实例上执行。

这似乎表明没有办法完成我想要的。如果我能够完成上面提到的模块化设计,我将有如下优势:为了设计SingleCameraFocus组件,我只需要重用SingleCameraLive组件,传入正确的参数(如Hub Uri)。

因此,另一种方法是为MultipleCameraLive组件提供一个单独的Hub,并且每次它从服务器接收到一个帧时,它都需要充当"多路复用",将该帧传递给正确的子组件。对于SingleCameraFocus组件,我需要一个单独的Hub。此外,因为有4个摄像头,我可能需要4个集线器,4个不同的类几乎完全相同的代码。这是因为客户端应该只从单个摄像机接收帧,即从单个集线器接收帧,因为集线器无法选择将数据发送到哪个客户端(如果通信是服务器发起的,如本例)。

你认为解决这个问题的最好方法是什么?

示例:

public class MyHub : Hub
{
private new static readonly ConcurrentDictionary<string, IList<string>> Groups = new ConcurrentDictionary<string, IList<string>>(
new Dictionary<string, IList<string>>()
{
{ "group_1", new List<string>() },
{ "group_2", new List<string>() },
{ "group_3", new List<string>() },
{ "group_4", new List<string>() }
}
);
public bool Join(string groupName)
{
var connId = Context.ConnectionId;
// Check if I already belong to this group
if(Groups.TryGetValue(groupName, out var groupClients))
{
if (groupClients.Contains(connId) == false)
groupClients.Add(connId);
return true;
}
return false;
}
public async Task Send(string groupName, string message)
{
var connId = Context.ConnectionId;
// Check if I belong to this group
if(Groups.TryGetValue(groupName, out var groupClients))
{
if(groupClients.Contains(Context.ConnectionId))
{
await Clients.Users((IReadOnlyList<string>)groupClients.Where(c => c != connId)).SendAsync(message);
}
}
}
public void Disconnect()
{
var myGroups = GetMyGroups();
foreach(var groupName in myGroups)
if (Groups.TryGetValue(groupName, out var groupClients))
groupClients.Remove(Context.ConnectionId);
}
// In case you can belong to many groups at the same time
private IList<string> GetMyGroups()
{
var connId = Context.ConnectionId;
var output = new List<string>();
foreach(var item in Groups)
{
if (item.Value.Contains(connId))
output.Add(item.Key);
}
return output;
}
}

最新更新