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

时间:2018-05-04 04:22:18

标签: javascript node.js google-chrome express websocket

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

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

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

虽然我的应用程序太复杂而无法在此处发布,但我在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

最后一个似乎是预取的。

1 个答案:

答案 0 :(得分:0)

我尝试使用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 );
    });
});