SignalR和浏览器连接限制

时间:2012-10-04 09:26:16

标签: asp.net iis push-notification signalr

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

我的问题是,如果我在两个不同的标签上打开相同的页面(在Chrome中执行),第一页加载正常,但第二页不会调用服务器的功能 - 仅在我关闭第一页时。

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

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

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

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

4 个答案:

答案 0 :(得分:18)

这个问题最好通过未来的Channel Messaging规范来解决,该规范迄今尚未被任何浏览器实施,但我设法按照Alex Ford所描述的limiting the number of connections解决它并使用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,但它不是那么干净。

答案 1 :(得分: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
}

答案 2 :(得分:3)

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

工作原理

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

答案 3 :(得分:0)

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

SignalR kan使用不同的传输协议,以及SignalR如何决定使用is described here

  

HTML 5传输

     

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

     
      
  • WebSocket (如果服务器和浏览器均指示它们可以支持Websocket)。 WebSocket是唯一在客户端和服务器之间建立真正的持久双向连接的传输方式。但是,WebSocket也有最严格的要求。只有最新版本的Microsoft Internet Explorer,Google Chrome和Mozilla Firefox才完全支持它,而其他浏览器(例如Opera和Safari)仅部分实现。
  •   
  • 服务器发送事件,也称为EventSource(如果浏览器支持服务器发送事件,则基本上是除Internet Explorer之外的所有浏览器。)
  •   

  

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

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

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

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

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

    •   
    • 客户端支持WebSocket

    •   
    • 服务器支持WebSocket

           

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

    •   
  6.   
  7. 如果未配置JSONP并且连接不是跨域的,则客户端和服务器均支持WebSocket。

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

  10.   
  11. 如果“服务器发送事件”不可用,则尝试永久帧。

  12.   
  13. 如果“永久帧”失败,则使用“长轮询”。

  14.   

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

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

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