WebSocket.onclose应该由用户导航或刷新触发吗?

38

第一部分:预期行为?

我发现Firefox和Chrome在处理onclose处理程序时存在不一致的浏览器行为。

似乎如果是由用户页面导航/刷新引起的,Chrome不会触发onclose。但是,Firefox会触发onclose

我认为Firefox在这里可能表现得正确:

当WebSocket连接关闭时,可能干净地关闭,用户代理必须创建一个使用CloseEvent接口的事件,事件名称为close,它不会冒泡,不能取消,没有默认操作,其wasClean属性设置为true(如果连接干净地关闭)并设置为false(否则),其代码属性设置为WebSocket连接关闭代码,其原因属性设置为WebSocket连接关闭原因; 并排队一个任务,首先将readyState属性的值更改为CLOSED(3),然后在WebSocket对象上分派事件。

来源:http://www.w3.org/TR/2011/WD-websockets-20110419/#closeWebSocket

尽管它可能会导致一些 狡猾的代码/意外行为

有人能确认预期的行为吗?

第二部分:如何实现自动重新连接?

如果您有一个为用户自动重新连接的库,那么您如何知道是否应该尝试重新连接?您检查 CloseEvent.wasClean 属性吗?我必须假设“干净”意味着关闭应该通过 WebSocket.close() 的 API 调用或服务器发送关闭帧来发生?如果网络错误导致关闭,我猜测 wasClean 将是 false

在 Pusher JavaScript 库中,我们假设(onclose -> waiting -> connecting),除非我们处于关闭状态 - 开发人员选择关闭连接,否则关闭将触发重新连接。看起来 socket.io 客户端库也做出了相同的假设。

基于此,由用户导航/刷新引起的Firefox onclose事件会触发不必要的重新连接,因为两个库都没有检查CloseEvent.wasClean属性。
示例和视频
下面是一个示例,您可以使用它来演示不一致性: http://jsbin.com/awonod/7 这是我演示问题的视频: http://www.screenr.com/vHn8 (很晚了,请忽略几个失误:))
需要注意的一点是,我的按Escape键也可能导致WebSocket连接关闭。但是,如果您仔细观察或自己尝试,您将看到在页面刷新之前记录了关闭事件。

Pusher的链接与socket.io的链接相同。另外,行号#不再适用。应该链接到一个特定的提交,例如https://github.com/socketio/socket.io-client/blob/fcb5c43ffeadc3b9398fbd087a6fc7a4e9276ba8/lib/socket.js。 - bennlich
2个回答

9
意外的行为是由于Firefox和Chrome处理Websocket关闭的方式不同造成的。当页面刷新时,两个浏览器都会关闭连接,但是Firefox会执行onclose代码,而Chrome则会关闭连接并直接跳转到重新加载新页面。所以,我确认这种奇怪的行为。
更奇怪的是,从我的观察来看,在Chrome中调用websocket.close()将立即关闭连接并调用onclose函数,而Firefox则等待服务器返回关闭消息。

如果您的库在不检查wasClean属性的情况下自动重新连接,则可能会导致问题,因为它在页面刷新时尝试重新建立连接。您应该考虑手动进行此操作,而不使用该库,这应该不难,只需在onclose函数中使用if语句调用connect,并确保onclean属性为true。或者更安全地,在onbeforeunload中设置一个变量,以防止任何新连接。
希望这可以帮助您!

1

令人惊讶的是,这在2022年仍然是当前行为。您可以通过在websocket的onclose处理程序中添加控制台日志,并在观察控制台的同时单击Firefox和Chrome中的链接来轻松演示此操作(确保在网页之间保留控制台):

ws.onclose = function(e) {
  printMsg('CLOSE')
  // ... my other code ...
}

Firefox会显示“CLOSE”而Chrome不会。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接