我用SignalR做了一个简单的应用程序进行测试。当页面加载它调用服务器上的函数时,该函数然后调用在屏幕上打印消息的客户端函数。我这样做是为了检查客户端和服务器功能是否正常以及SignalR通信是否正常。
我的问题是,如果我在两个不同的标签上打开相同的页面(在Chrome中执行),第一页加载正常,但第二页不会调用服务器的功能 - 仅在我关闭第一页时。
据我了解,他们可能是一个连接限制,与浏览器有关,不允许SignalR多次连接(实际上是两个,一个用于接收,一个用于发送)
更新:我发现其他标签处于打开状态,但现在我已经检查了它,它允许只有4个标签 /页面处于活动状态。如果我尝试在新选项卡上放置相同的页面而不发送任何数据,当我关闭其中一个选项卡时,新选项卡会立即发送数据。
我想知道是否有任何解决方案,因为如果用户决定在两个或更多选项卡上打开同一页面,我希望此连接可用。
我不相信它与IIS有任何关系,因为据我所知,它可以接受数以千计的连接。
答案 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用来决定使用哪种传输的步骤。
如果浏览器是Internet Explorer 8或更早版本,则使用Long Polling。
如果配置了JSONP(即,启动连接时jsonp参数设置为true),则使用长轮询。
如果建立了跨域连接(即,SignalR端点与托管页面不在同一域中),则在满足以下条件时将使用WebSocket:
客户端支持CORS(跨域资源共享)。有关哪些客户端支持CORS的详细信息,请参见caniuse.com上的CORS。
客户端支持WebSocket
服务器支持WebSocket
如果不满足任何这些条件,将使用长轮询。有关跨域连接的更多信息,请参见如何建立跨域连接。
如果未配置JSONP并且连接不是跨域的,则客户端和服务器均支持WebSocket。
如果客户端或服务器不支持WebSocket,则使用服务器发送事件(如果可用)。
如果“服务器发送事件”不可用,则尝试永久帧。
如果“永久帧”失败,则使用“长轮询”。
使用开发者工具,检查您页面上的网络调用。您应该能够看到类似这样的内容:
.../connect?transport=webSockets&clientProtocol=1.5&connectionToken=...
如果transport
不是webSockets
(例如serverSentEvents
或longPolling
),则可能要对客户端和服务器进行故障排除,以了解为什么握手不成功使用WebSockets。 (就我而言,我错过了为IIS安装 WebSocket协议 Windows功能。