如何在 Chrome 中加载下一页之前断开 websocket

Posted

技术标签:

【中文标题】如何在 Chrome 中加载下一页之前断开 websocket【英文标题】:How to disconnect a websocket before the next page is loaded in Chrome 【发布时间】:2018-05-04 04:22:18 【问题描述】:

在 Express JS 应用程序中实现基于 Socket.io 的通知系统时,我发现当用户在站点中导航时,通过控制器加载的下一页在前一页的 websocket 断开事件之前进入。

这令人沮丧,因为我正在将用户映射到套接字,并且在页面加载周期期间,任何新消息都会发送到旧套接字。

我有一个检查,如果没有活动的连接,它会缓冲消息,然后在连接时将它们刷新到客户端,或者稍后按需刷新。在第二个/第三个页面控制器处理期间(当然,在页面甚至还没有发送之前)这个过程被愚弄,认为前一个页面的连接仍然是活动的。理想情况下,我希望拆掉套接字,这样我就可以在控制器运行之前删除映射条目。

虽然我的应用过于复杂,无法在此处发布,但我已在 Socket.io 示例应用中轻松重现了该问题。

当在两个页面之间单击并再次返回时,这会产生以下跟踪:

Page 1 controller
Page 1 done
connection  VlPV3nIQHFuzGUXKAAAU


Page 2 controller
Page 2 done
disconnecting VlPV3nIQHFuzGUXKAAAU transport close
disconnect VlPV3nIQHFuzGUXKAAAU transport close
connection  Xr7l-6TSv4VpWibXAAAV


Page 1 controller
Page 1 done
disconnecting Xr7l-6TSv4VpWibXAAAV transport close
disconnect Xr7l-6TSv4VpWibXAAAV transport close
connection  WcmYeaOnpmzYG3_qAAAW

如您所见,前一个套接字的断开连接是在页面完成渲染后完成的。

服务器是此https://github.com/socketio/chat-example 的略微修改版本。我刚刚添加了事件日志记录和第二个页面以供交替使用。

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var port = process.env.PORT || 3000;

app.get('/', function(req, res)
    console.log('Page 1 controller');
    res.sendFile(__dirname + '/index.html');
    console.log('Page 1 done');
);
app.get('/2', function(req, res)
    console.log('Page 2 controller');
    res.sendFile(__dirname + '/index.html');
    console.log('Page 2 done');
);

io.on('connection', function(socket)
    console.log('connection ', socket.id);
    socket.on('chat message', function(msg)
        console.log('received ', msg);
        io.emit('chat message', msg);
    );
    socket.on('disconnecting', function(reason)
        console.log('disconnecting', socket.id, reason);
    );
    socket.on('disconnect', function(reason)
        console.log('disconnect', socket.id, reason);
    );
);

http.listen(port, function()
    console.log('listening on *:' + port);
);

我尝试将控制器包装在 setImmediate() 中,但这并没有延迟足够的时间。

我猜 Chrome 在拆除旧页面之前会进行一些“聪明”的预取,或者套接字会稍微徘徊。

如何在控制器被击中之前断开连接?


编辑:在 Safari 中尝试会得到相同的结果,但由于它会在地址栏中预取网站,因此您会得到重复的连接。

Page 1 controller
Page 1 done
connection  77rWBC3fx_VrVGSsAAAj
disconnecting 77rWBC3fx_VrVGSsAAAj transport close
disconnect 77rWBC3fx_VrVGSsAAAj transport close

Page 2 controller
Page 2 done
connection  ExZeKTiG1wRix7pxAAAk
disconnecting ExZeKTiG1wRix7pxAAAk transport close
disconnect ExZeKTiG1wRix7pxAAAk transport close

Page 1 controller
Page 1 done
connection  0Xtkd5lYCyHDumHTAAAl
disconnecting 0Xtkd5lYCyHDumHTAAAl transport error
disconnect 0Xtkd5lYCyHDumHTAAAl transport error

Page 2 controller
Page 2 done
connection  0x7aV1ttnG6NT9_pAAAm

Page 2 controller
Page 2 done
disconnecting bxzFSDWGNDfEpw5MAAAi transport close
disconnect bxzFSDWGNDfEpw5MAAAi transport close
connection  l09oRFBX0-XZp658AAAn
disconnecting 0x7aV1ttnG6NT9_pAAAm transport close
disconnect 0x7aV1ttnG6NT9_pAAAm transport close

最后一个似乎是预取的。

【问题讨论】:

你应该只使用不会改变的标识符而不是物理套接字 是的,这就是我正在做的事情,但我必须在连接期间将标识符映射到套接字 ID,连接参数令牌已烘焙到每个用户的视图中。否则如何通过另一个标识符向特定客户端发送消息? 我想这是一个不同的问题,也许我会单独发布。我现在已经解决了这个具体问题。但是我有兴趣甚至不必解决它! 【参考方案1】:

我尝试使用window.onunload 事件来查看是否更早,但没有。然后我注意到了window.onbeforeload 事件,并且确实发生在下一页加载之前:

Page 1 controller
Page 1 done
connection  RZJ0RpDdIRM9JwcHAAAI

window_beforeunload RZJ0RpDdIRM9JwcHAAAI
Page 2 controller
Page 2 done
window_unload RZJ0RpDdIRM9JwcHAAAI
disconnecting RZJ0RpDdIRM9JwcHAAAI transport close
disconnect RZJ0RpDdIRM9JwcHAAAI transport close
connection  5wU3WklUptDVm7HHAAAJ

window_beforeunload 5wU3WklUptDVm7HHAAAJ
Page 1 controller
Page 1 done
window_unload 5wU3WklUptDVm7HHAAAJ
disconnecting 5wU3WklUptDVm7HHAAAJ transport close
disconnect 5wU3WklUptDVm7HHAAAJ transport close
connection  VOywtaf_4nAipPpvAAAK

有了这个修改后的客户端:

      window.onbeforeunload = function()
          socket.emit('window_beforeunload');
      ;
      window.onunload = function()
          socket.emit('window_unload');
      ;

这个修改后的服务器:

io.on('connection', function(socket)
    ...

    socket.on('window_beforeunload', function()
        console.log('window_beforeunload', socket.id);
    );
    socket.on('window_unload', function()
        console.log('window_unload', socket.id );
    );
);

【讨论】:

以上是关于如何在 Chrome 中加载下一页之前断开 websocket的主要内容,如果未能解决你的问题,请参考以下文章

在 UIScrollView 中加载下一个图像视图

WEB 打印如何去掉 页头页尾

带有IFrames Chrome 63的Android Cordova应用程序无请求标题Cookie

导航到下一页时,Chrome (iOS) 中的奇怪自动滚动/跳转行为

在进入下一页之前使用数据库验证表单

在链接到下一页之前播放音频的 HTML 链接