【问题标题】:Isolating specific browser instance with SignalR使用 SignalR 隔离特定浏览器实例
【发布时间】:2013-04-11 15:41:50
【问题描述】:

我们正在构建一个应用程序,它将使用 SignalR 向浏览器发送消息。用户可能打开了多个浏览器实例,我们希望将每条消息发送到适当的浏览器。我们的理解是 ClientId ConnectionId 将允许我们这样做。我们遇到的问题是在代码库中的适当时间访问 ClientId ConnectionId 或 SessionId。这是我们的场景:

执行 MVC 操作,作为该处理的一部分,调用 Biztalk 端点。 Biztalk 执行不在进程中(从 MVC 操作的角度来看),完成后不会返回。这是设计使然。为了通知 MVC 应用程序它已完成,Biztalk 通过调用 /myapp/signalr 端点向 MVC 应用程序的 SignalR 集线器发送一条消息。消息由 SignalR 接收,然后应路由到适当的浏览器实例。

由于发送给 SignalR 的消息是由 Biztalk 而不是 MVC 应用程序发送的,因此与 SignalR 的连接的 ClientId 不是标识应该接收消息的浏览器实例的那个。所以我们试图实现的是类似于返回地址模式的东西,方法是在发送给 Biztalk 的消息中包含启动 Biztalk 调用的浏览器实例的 ClientId ConnectionId。当 Biztalk 将其消息发送到 SignalR 时,其中一个内容是原始 ClientId ConnectionId 值。当 SignalR 处理来自 Biztalk 的消息时,它可以使用消息中包含的 ClientId ConnectionId 将该消息路由到适当的浏览器实例。 (是的,我们知道,如果浏览器已关闭并重新打开,这将不起作用,我们对此没问题。)

我们面临的问题是,当最初从我们的 MVC Action 向 Biztalk 发送消息时,我们无法访问 ClientId ConnectionId,因为它仅在 Hub 的上下文中可用。这是可以理解的,因为 MVC Action 不知道要查找哪个 Hub 上下文。

我们尝试的是通过 Biztalk 消息传递 SessionId 并将其返回给 SignalR。这解决了在 Biztalk 消息中包含浏览器实例标识符并将其返回给 SignalR 的问题。它引入的事实是,当客户端连接到 Hub 时,我们无法访问 Hub 的 OnConnect 方法中的 Session(因此也无法访问 SessionId)。

David Fowler 发布了一个要点,据报道显示了如何使只读 SessionState 在 Hub 中可访问,但它不起作用。 (https://gist.github.com/davidfowl/4692934) 只要我们将此代码添加到发送到 SignalR 的应用程序消息中,就会导致 HTTP 500 错误,这是由 SignalR 引发以下异常引起的。

[ArgumentNullException: Value cannot be null.Parameter name: s]
System.IO.StringReader..ctor(String s) +10688601
Microsoft.AspNet.SignalR.Json.JsonNetSerializer.Parse(String json, Type targetType) +77
Microsoft.AspNet.SignalR.Json.JsonSerializerExtensions.Parse(IJsonSerializer serializer, String json) +184
Microsoft.AspNet.SignalR.Hubs.HubRequestParser.Parse(String data) +101
Microsoft.AspNet.SignalR.Hubs.HubDispatcher.OnReceived(IRequest request, String connectionId, String data) +143
Microsoft.AspNet.SignalR.<>c__DisplayClassc.<ProcessRequest>b__7() +96
Microsoft.AspNet.SignalR.<>c__DisplayClass3c.<FromMethod>b__3b() +41
Microsoft.AspNet.SignalR.TaskAsyncHelper.FromMethod(Func`1 func) +67

无论我们设置 SessionStateBehavior 的模式(如 David Fowler 的要点所示),我们要么在向 Hub 发送消息时收到此异常,要么在 Hub 的 OnConnect 中时 SessionState 为 null。

那么,在所有这些序言之后,我们要问的是人们在 SignalR 中使用这种类型的断开连接的消息传递时如何更新适当的客户端?

【问题讨论】:

  • 当你说 ClientId 是指连接 ID 吗?
  • 是的。更新到正确的措辞
  • 所以似乎没有技术解决方案。我认为正确的解决方案是拥有一个更好的架构,它不依赖于向用户返回消息。我们现在正在改变方向。

标签: signalr signalr-hub


【解决方案1】:

如果您希望在向集线器发出正常请求之外向客户端发送数据,那么我建议您在集线器上使用静态并发字典来管理您的用户并将它们映射到相应的连接 ID。

通过这种方法,您可以在任何时候根据他们映射的连接 ID 向任何用户发送信息。因此,在将数据发送到 Biztalk 时,您需要做的就是发送您的用户 ID(由您创建),然后当数据流回 SignalR 时,您可以查找该给定用户 ID 的 ConnectionId(如果存在)。

最后,您可以通过在 OnConnected 中将用户添加到并发字典中来管理用户映射,仅当用户在 OnReconnected 中不存在时才添加,并在 OnDisconnected 中删除。

【讨论】:

  • 问题是当用户打开多个应用程序/页面实例时,仅将用户 ID 发送到 BizTalk 不足以完全识别返回消息应该发送到的浏览器实例。例如,Joe 使用 ClientId 123456ABC 和 DEF987654 打开了两个浏览器实例。如果我们发送给 BizTalk(它返回给我们)的所有内容都是“Joe”,那么我们不知道要将消息推送到哪个浏览器实例。
  • 我们需要的是能够获取一个唯一的浏览器标识符(我认为 ClientId 是答案)并将其发送到 Biztalk,以便它可以在 Biztalk 消息中返回。随着消息被发送到集线器和上下文之外的 Biztalk,我们没有任何访问 ClientId 的权限......所以有什么替代方案成为问题。
  • 您仍然可以通过将用户映射到集线器中的连接 ID 来完成此操作。例如,它可能不是用户 -> 连接 ID 映射,而是用户 -> 浏览器 [] 映射,其中浏览器具有映射到连接 ID 的唯一 ID。此时您只需将 browserID 发送到 biztalk,然后您可以在它返回 SignalR 端点时进行查找。一直以来,浏览器 ID 仍由 OnConnected、OnReconnected、OnDisconnected 等管理。
  • 对。我们尝试了这种方法,我们能想到的唯一合乎逻辑的项目是 SessionId,因为我们需要能够支持多个选项卡以及多个浏览器实例。不幸的是,正如我在问题中概述的那样,在 OnConnect 中访问 SessionState 和 SessionId 的一种技术(我们需要在其中进行映射)不断失败。如果您知道 Hub 中可以访问的不同值,我们可以使用它来映射浏览器/选项卡实例,我们很乐意听到。
猜你喜欢
  • 2022-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多