Signalr 使用signar隔离特定浏览器实例

Signalr 使用signar隔离特定浏览器实例,signalr,signalr-hub,Signalr,Signalr Hub,我们正在开发一个应用程序,它将使用SignalR向浏览器发送消息。用户可能打开了多个浏览器实例,我们希望将每条消息发送到相应的浏览器。我们的理解是ClientId ConnectionId允许我们这样做。我们遇到的问题是在代码库中的适当时间访问ClientId ConnectionId或SessionId。下面是我们的场景: 执行MVC操作,并且作为该处理的一部分,调用Biztalk端点。Biztalk执行处于进程外(从MVC操作的角度来看),完成后不会返回。这是故意的。为了通知MVC应用程序它

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

执行MVC操作,并且作为该处理的一部分,调用Biztalk端点。Biztalk执行处于进程外(从MVC操作的角度来看),完成后不会返回。这是故意的。为了通知MVC应用程序它已完成,Biztalk通过调用/myapp/signer端点向MVC应用程序的信号器中心发送一条消息。该消息由信号器接收,然后应路由到相应的浏览器实例

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

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

我们在这里尝试的是通过Biztalk消息传递SessionId并将其返回给SignalR。这解决了在Biztalk消息中包含浏览器实例标识符并将其返回给Signal的问题。它引入的事实是,当客户机连接到集线器时,我们无法在集线器的OnConnect方法中访问会话(从而访问SessionId)

David Fowler发布了一个要点,据报道该要点展示了如何在中心中访问readonly SessionState,但它不起作用。()一旦我们将此代码添加到发送给Signal的应用程序消息中,就会导致HTTP 500错误,这是由Signal引发以下异常引起的

[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
[ArgumentNullException:值不能为null。参数名称:s]
System.IO.StringReader..ctor(字符串s)+10688601
Microsoft.AspNet.signal.Json.JsonNetSerializer.Parse(字符串Json,类型targetType)+77
Microsoft.AspNet.signal.Json.JsonSerializerExtensions.Parse(IJsonSerializer序列化程序,字符串Json)+184
Microsoft.AspNet.signal.Hubs.HubRequestParser.Parse(字符串数据)+101
Microsoft.AspNet.signal.Hubs.HubDispatcher.OnReceived(IRequest请求,字符串连接ID,字符串数据)+143
Microsoft.AspNet.signal.c__DisplayClassc.b__7()+96
Microsoft.AspNet.signal.c__DisplayClass3c.b__3b()+41
Microsoft.AspNet.signal.TaskAsyncHelper.FromMethod(Func`1 Func)+67
无论我们设置SessionStateBehavior的模式如何(如David Fowler的gist所示),我们要么在向集线器发送消息时得到此异常,要么在集线器的OnConnect中得到SessionState为null


所以,在经历了所有的预演之后,我们要问的是,在SignalR中处理此类断开连接的消息时,人们如何更新相应的客户端?

如果您希望将数据发送到集线器的正常请求之外的客户端,那么我建议在集线器上设置一个静态并发字典,用于管理您的用户并将其映射到相应的用户连接Id的

使用这种方法,您可以根据映射的连接Id随时向任何用户发送数据。因此,在将数据发送到Biztalk时,您只需发送您的用户Id(由您创建),然后当数据返回到SignalR时,您就可以查找给定用户Id的ConnectionId(如果存在)


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

当您说ClientId时,您指的是连接ID吗?是的。已更新到正确的措辞,因此似乎没有解决此问题的技术解决方案。我认为正确的解决方案是拥有一个更好的体系结构,它不依赖于向用户返回消息。我们现在正在改变方向,朝那个方向前进。问题是,当用户打开了应用程序/页面的多个实例时,仅将用户id发送到BizTalk不足以完全识别返回消息应发送到的浏览器实例。例如,Joe打开了两个浏览器实例,其中clientid123456abc和DEF987654。如果我们发送给BizTalk(并且它返回给我们)的都是“Joe”,那么我们不知道将消息推送到哪个浏览器实例。我们需要的是能够获取唯一的浏览器标识符(我认为ClientId就是答案)并将其发送给BizTalk,以便它可以在BizTalk消息中返回。由于消息被发送到中心和上下文之外的Biztalk,我们无法访问ClientId…因此,问题是替代方法是什么。您仍然可以通过将用户映射到中心中的连接ID来实现这一点。例如,它不是一个用户->连接ID映射,而是一个用户->浏览器[]映射,其中浏览器具有映射到连接ID的唯一ID。此时,您只需将browserID发送到biztalk,然后在它返回到Si时可以查找它