【问题标题】:SignalR and Browser Connection limitSignalR 和浏览器连接限制
【发布时间】:2012-09-25 07:23:58
【问题描述】:

我使用 SignalR 制作了一个简单的应用程序进行测试。当页面加载时,它调用服务器上的一个函数,然后该函数调用一个在屏幕上打印消息的客户端函数。我这样做是为了检查客户端和服务器功能是否正常工作以及 SignalR 通信是否正常。

我的问题是,如果我在两个不同的选项卡上打开同一个页面(在 Chrome 中完成),第一页加载正常,但第二页不调用服务器的功能 - 仅当我关闭第一页时。

据我了解,它们可能是与浏览器有关的连接限制,它不允许 SignalR 连接一次以上(实际上是两个,一个用于接收,一个用于发送)

更新:我发现我们的其他标签页都打开了,但现在我已经检查过了,它只允许 只有 4 个标签页/页面通过连接处于活动状态。如果我尝试将同一页面放在新选项卡上,则不会发送任何数据,当我关闭其他选项卡之一时,新选项卡会立即发送数据。

我想知道是否有任何解决方案,因为如果用户决定在两个或更多选项卡上打开同一页面,我希望此连接可用。

我不相信它与 IIS 有任何关系,因为据我所知它可以接受数千个连接。

【问题讨论】:

  • 您使用的是 IIS 还是 IIS express?
  • 通过visual studio在本地测试

标签: asp.net iis push-notification signalr


【解决方案1】:

这个问题最好由未来的Channel Messaging 规范解决,该规范迄今为止尚未被任何浏览器实现,但我设法通过 limiting the number of connections 解决了这个问题,正如 Alex Ford 所描述的那样,并使用 localStorage 作为选项卡之间的消息总线。

storage 事件允许您在选项卡之间传播数据,同时保持单个 SignalR 连接打开(从而防止连接饱和)。调用 localStorage.setItem('sharedKey', sharedData) 将在所有其他选项卡(不是调用者)中引发 storage 事件:

$(window).bind('storage', function (e) {
    var sharedData = localStorage.getItem('sharedKey');
    if (sharedData !== null)
        console.log(
            'A tab called localStorage.setItem("sharedData",'+sharedData+')'
        );
});

您可以测试 if ($.connection.hub.state === 1) 以确定给定选项卡是否应通过 localStorage(由 Alex 提供)通知其他选项卡,以防止重复的 localStorage.setItem 调用。

Facebook 通过在多个子域上提供持久连接来克服此浏览器限制,但这会使部署和测试变得复杂。

注意事项

旧连接:在 Alex 的解决方案中,您需要注意 Disconnect() 未被调用(例如异常),并且您使用旧集线器填充您的 HubConnections 存储桶(或存储库)连接。如果会话 ID 没有更改(可能发生),这可能会阻止新客户端建立 SignalR 连接,即使没有处于活动状态。或者,为新连接添加时间戳并设置一个滑动过期时间以最大程度地减少潜在影响。

锁定: localStorage 可能会受到竞争条件的影响,因为它没有像 described here 那样实现任何锁定。

为了支持不同类型的事件,您应该在 JSON 消息中编码一个 eventType 并在 storage 事件上对其进行测试。

后备

如果无法建立 SignalR 连接,我会每 45 秒轮询一次服务器以检索通知计数。

如果你不想使用localStorage,你可以使用cookies,但不是那么干净。

【讨论】:

  • 这是一个有趣的解决方案,感谢分享!我想知道 SignalR 是否可以在未来也将其抽象化,这样无论您打开多少个标签,它都只需要一个并发连接?
  • 很棒的解决方案,虽然不确定是否将其构建到信号器中,但似乎很复杂。 IIS express 也没有连接限制。
  • 我开始实现这种 localStorage 行为,然后意识到我仍然需要一种方法来将连接限制为一个。我知道我是如何使用数据库的,但我回到这个答案,看看它是如何在这里完成的。直到那时我才意识到他链接到我自己的博客文章。哈哈
  • @dfowler,你是对的。我已经删除了关于 IIS Express 有连接限制的声明。我一定是达到了 Chrome 的同时连接数限制(8 个还是 10 个?)。
  • @AlexFord 指向您博客的链接已损坏。此外,您的博客文章中要求人们报告损坏的链接的“让我知道”链接已损坏:)
【解决方案2】:

我创建了IWC-SignalR 实用程序,它允许为同一应用程序的所有窗口(选项卡)建立单个 SignalR 连接。

工作原理

其中一个窗口成为连接所有者(随机选择)并持有真正的 SignalR 连接。如果连接所有者关闭或崩溃,另一个窗口将成为连接所有者 - 这会自动发生。窗口间通信通过inter-window communication library(基于localStorage)完成。该库提供了在窗口之间以及在并行进程(锁、共享数据、事件总线......)之间进行通信的功能。希望它对某人有用。

【讨论】:

    【解决方案3】:

    为了扩展@FreshCode 的答案,这就是我实现他的想法的方式。

    我需要在选项卡之间传递两个不同的操作。我可以设置通知或删除通知。这些通知由浏览器接收并存储为将在指定时间触发的超时。第一个选项卡有 SignalR 连接,而其他所有选项卡都没有。我必须克服的一个问题是,无论我打算执行哪个操作(设置/删除),“存储”事件都会触发。

    我最终做的是传递一个自定义 JSON 对象,其中包含我想要执行的操作的属性:

    $(window).bind('storage', function () {
        var updateInfo = JSON.parse(localStorage.getItem('updateInfo'));
        if (updateInfo.action == 'removeNotification')
            removeNotification(updateInfo.notificationId);
        else if (updateInfo.action == 'setNotification')
            setNotification(updateInfo.notification);
    });
    

    这样每次我在本地存储中设置一个项目时,我只需指定需要发生的操作,并且只有该操作会在其他选项卡上发生。例如,如果我更新一个通知,它是客户端收到通知时这两种操作的组合。它删除通知并使用更新的值设置通知。因此,正在对localStorage.setItem 进行两次调用。

    function removeNotification(id) {
        // Check if signalR is connected. If so, I am the tab that will update
        // the other tabs.
        if ($.connection.hub.state === 1) {
            var updateInfo = {
                action: 'removeNotification',
                notificationId: id
            };
            localStorage.setItem('updateInfo', JSON.stringify(updateInfo));
        }
        // brevity brevity
    }
    

    同样,setNotification 函数。

    function setNotification(notification) {
        if ($.connection.hub.state === 1) {
            var updateInfo = {
                action: 'setNotification',
                notification: notification
            };
            localStorage.setItem('updateInfo', JSON.stringify(updateInfo));
        }
        // brevity brevity
    }
    

    【讨论】:

    • 用你的$.connection.hub.state === 1 测试和一些注意事项更新了我的答案。
    • 我其实想知道如何解决断开连接不触发的问题。我认为某种时间戳解决方案会起作用,但我无法在脑海中确切地弄清楚应该如何实现它。例如:如果选项卡一连接,并且为了争论,使用相同的 sessionId 保持连接 3 天。如果在第 3 天打开第二个选项卡,则原始连接的 HubConnection 将具有 3 天前但仍然有效的时间戳。
    • 您能否提供一个示例项目,以便我进行测试。请。很难实现这一点
    • @xec 我不再在我建立该链接时所在的公司工作,因此我无法再访问带注释的文件。如果我有时间,我会摸索文件并重新注释它。不过,就目前而言,here's a link 是我拥有的工作脚本的唯一副本,该脚本有效地将单个 websocket 连接多路复用到多个选项卡。已经好几年了,所以它可能是粗俗的 JavaScript。我们只会说我从那以后长大了;)
    【解决方案4】:

    确保 SignalR 可以使用并且正在使用 WebSocket,并且它不会使用您的应用程序允许使用的有限数量的连接之一。

    SignalR 可以使用不同的传输协议以及 SignalR 如何决定使用哪个is described here

    HTML 5 传输

    这些传输依赖于对 HTML 5 的支持。如果客户端浏览器不支持 HTML 5 标准,则将使用旧的传输。

    • WebSocket(如果服务器和浏览器都表明它们可以支持 Websocket)。 WebSocket 是唯一一种在客户端和服务器之间建立真正持久的双向连接的传输方式。但是,WebSocket 也有最严格的要求;它仅在最新版本的 Microsoft Internet Explorer、Google Chrome 和 Mozilla Firefox 中完全受支持,并且仅在 Opera 和 Safari 等其他浏览器中部分实现。
    • Server Sent Events,也称为EventSource(如果浏览器支持Server Sent Events,基本上是除了Internet Explorer之外的所有浏览器。)

    运输选择过程 以下列表显示了 SignalR 用于决定使用哪种传输方式的步骤。

    1. 如果浏览器是 Internet Explorer 8 或更早版本,则使用长轮询。

    2. 如果配置了JSONP(即启动连接时jsonp参数设置为true),则使用Long Polling。

    3. 如果正在建立跨域连接(即,如果 SignalR 端点与托管页面不在同一个域中),则如果满足以下条件,则将使用 WebSocket:

      • 客户端支持 CORS(跨域资源共享)。有关哪些客户端支持 CORS 的详细信息,请参阅 caniuse.com 上的 CORS。

      • 客户端支持WebSocket

      • 服务器支持WebSocket

        如果不满足这些条件中的任何一个,将使用长轮询。有关跨域连接的更多信息,请参阅如何建立跨域连接。

    4. 如果没有配置JSONP且连接不是跨域的,如果客户端和服务端都支持,则使用WebSocket。

    5. 如果客户端或服务器不支持 WebSocket,则使用服务器发送事件(如果可用)。

    6. 如果服务器发送的事件不可用,则尝试永久帧。

    7. 如果 Forever Frame 失败,则使用长轮询。

    使用开发者工具,检查在您的页面上进行的网络调用。您应该能够看到类似的内容:

    .../connect?transport=webSockets&clientProtocol=1.5&connectionToken=...

    如果transport 不是webSockets(例如serverSentEventslongPolling),您可能需要对客户端和服务器进行故障排除,以了解为什么握手不会导致使用WebSockets。 (就我而言,我错过了为 IIS 安装 WebSocket 协议 windows 功能。

    【讨论】:

    • 这个答案对我帮助很大。谢谢
    猜你喜欢
    • 2011-02-27
    • 2014-01-28
    • 2015-10-19
    • 2015-02-26
    • 2014-03-12
    • 1970-01-01
    • 2020-12-20
    • 1970-01-01
    • 2019-02-21
    相关资源
    最近更新 更多