【问题标题】:SignalR Join Group new Context.ConnectionId on browser refreshSignalR 加入组新的 Context.ConnectionId 浏览器刷新
【发布时间】:2013-08-17 07:43:58
【问题描述】:

我的应用有一堆用户组。

有一个管理员页面可以向其中一个或多个用户组发送通知。

我有一个集线器,管理员可以通过对 WebAPI 方法的 AJAX 调用将通知保存到数据库后将其发布到该集线器。当这在 Ajax 成功部分返回时,我然后调用服务器集线器上的一个方法,该方法将通知实时发布到 1 个或多个组。这些组由一个 int 数组指示,该数组仅通过使用 .ToString() 转换 int 来创建组名。

客户的主页上有一个显示通知的小部件。一些用户可以是多个用户组的一部分,但他们在页面顶部有一个下拉列表,指定他们当前正在查看的用户组。因此,它们可能是许多用户组的一部分,但一次只能查看 1 个。

因此,我猜我的做法类似于 Jabbr Chatrooms,但我不想设置太多,因为该应用程序有不同的意图。因此,当客户端登录时,他们会根据下拉列表中 Selected 项的值加入一个组。我确实注意到,每当我以客户端身份刷新页面时,都会使用新的 connectionId 再次调用“加入”服务器方法。这是正确的吗?我不想拥有比实际更多的联系。当他们更改下拉列表时,我还需要断开客户端与群组的连接并重新加入新群组。

我正在寻找演示,但找到非常简单的演示或非常复杂的演示,我最初的目标是介于两者之间,因为我打算在网站的其他区域添加 SignalR 功能,所以不想锁定我的对象模型太低了。

所以在我的集线器上我得到了:

public void Join(string groupName) {
  Groups.Add(Context.ConnectionId, groupName);
}

public void SendNotificationsToGroups(string title, string description, int[] groups) {
        foreach (int i in groups) {
            Clients.OthersInGroup(i.ToString()).addMessage(Title, description);
        }
}

管理页面调用 SendNotificationsToGroups 并且这工作正常。如您所见,它传递了一个整数数组,这些整数是要向其广播通知的组。我只是 .ToString 的 int 来创建 GroupNames。

客户端执行以下操作:

var Hub = $.connection.myHub;

Hub.client.addMessage = function (title, description) {
  $('#Notification').find('tr:first').before('<tr><td>'+title+description'</td></tr>');
}

$.connection.hub.start().done(function () {
    var groupId = $('#userGroupID').val();
    newsHub.server.join(groupId);
}

我主要关心的是加入方法,因为 addMessage 部分有效!管理员发送通知,客户看到它们。问题是如果客户端刷新他们的浏览器 Join 在服务器上再次被调用(我在它上面设置了一个断点)并且它有一个新的 Context.ConnectionId。这很糟糕吗?这是否会导致不必要的连接。如果关闭客户端的窗口离开页面,我应该进行 javascript/jQuery 调用以确保连接如果被破坏,那么浏览器刷新问题呢?

感谢您的任何想法。

【问题讨论】:

    标签: signalr


    【解决方案1】:

    SignalR hooks the window's unload event and calls .stop() on the connection,但它使用异步消息执行此操作,因此它可能不会立即发生。此外,服务器端将自动检测并清除“死”连接,作为其日常工作的一部分。

    也就是说,如果可以的话,最好保持 SignalR 连接处于活动状态,而不是在每个页面导航时断开/重新连接。不断断开和重新连接有点违背目的,您显然会错过页面导航之间的一些消息。

    【讨论】:

    • 想详细说明您将如何做到这一点(防止刷新之间的 connectionId 回收)?谢谢
    • 我真的不知道您的应用程序的确切上下文,但一种方法是迁移到现代单页应用程序 (SPA) 模型,其中在 SPA 启动时连接 SignalR 连接然后您可以根据需要动态加载应用的各个部分。
    【解决方案2】:

    我解决它的方法是将用户的会话和 connectionId 存储在与用户的一对多表中。例如,您可以创建一个 PersonSession 表来存储上述信息以及主机、连接时间、断开连接时间和用户代理等附加信息。

    您可以使用以下代码在OnConnected()方法上获取会话:

    var session = Context.RequestCookies.ContainsKey("ASP.NET_SessionId") ? Context.RequestCookies["ASP.NET_SessionId"].Value : null;
    

    并将其与 connectionId 一起存储在数据库中。在OnDisconnected() 方法中,您只需使用 DisconnectTiemstamp 更新此记录。

    然后当它们刷新时,您可以检查以前的记录(我随时为用户保留最多 3 条记录),查看它们是否具有相同的会话,如果有,则不更新,否则,您假设这是一个新会话并进行更新。

    if (session != null)
    {
         var MyInActiveSessions = p.PersonSessions.Where(x=>x.DisconnectTimestamp != null);
                                bUpdateStatus = !MyInActiveSessions.Any(x => x.SessionID != null &&          x.SessionID == session);
    }
    

    另外,请记住,您必须在登录时强制重新生成会话 cookie,因为即使在您注销时 ASP.net 也会保留旧的会话 cookie。为此,您需要做几件事:

    1. 在您的登录代码中,将任意随机值放入 Session 变量中以强制 ASP.NET 生成 cookie(例如 Session['0'] = 0;
    2. 在您的注销代码中,放弃会话并将 cookie 添加到响应中:

      Session.Abandon(); Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", ""));

    3. 最后,在 Web.config 中,确保您的会话自动重新生成:

    &lt;sessionState mode="InProc" cookieless="false" regenerateExpiredSessionId="true"&gt;&lt;/sessionState&gt;

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 2015-02-18
      • 2014-08-10
      • 1970-01-01
      • 1970-01-01
      • 2018-03-14
      • 1970-01-01
      • 2010-09-08
      • 2011-09-30
      • 1970-01-01
      相关资源
      最近更新 更多