Как отключить веб-сокет перед загрузкой следующей страницы в Chrome - PullRequest
0 голосов
/ 04 мая 2018

При реализации системы уведомлений на основе Socket.io в приложении Express JS я обнаружил, что когда пользователь перемещается по сайту, следующая загрузка страницы через контроллеры происходит до события отключения веб-сокета на предыдущей странице.

Это разочаровывает, так как я сопоставляю пользователей с сокетами, и во время цикла загрузки страницы все новые сообщения отправляются на старый сокет.

У меня есть проверка, которая подделывает сообщения, если нет активного соединения, а затем сбрасывает их клиенту при подключении или по требованию позже. Во время обработки контроллера второй / третьей страницы (конечно, даже до того, как страница была отправлена) этот процесс обманчив, заставляя думать, что соединение предыдущей страницы все еще активно. В идеале я хочу, чтобы сокет был разрушен, поэтому я могу удалить запись карты до запуска контроллера.

Хотя мое приложение слишком сложно для размещения здесь, я легко воспроизвел проблему в образце приложения 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 голосов
/ 04 мая 2018

Я пытался использовать событие 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 );
    });
});
...