Правильное закрытие WebSocket (HTML5, Javascript)

Я играю с HTML5 WebSockets. Мне было интересно, как я могу закрыть соединение изящно? Мол, что произойдет, если пользователь обновит страницу или просто закроет браузер?

Есть странное поведение, когда пользователь просто обновляет страницу без вызова websocket.close() - когда они вернутся после обновления, он поразит websocket.onclose событие.

7 ответов

Согласно спецификации протокола v76 (это версия, которую реализует браузер с текущей поддержкой):

Чтобы завершить соединение корректно, от одного узла отправляется кадр, состоящий только из байта 0xFF, за которым следует байт 0x00, чтобы запросить, чтобы другой узел закрыл соединение.

Если вы пишете сервер, вы должны обязательно отправить закрытый фрейм, когда сервер закрывает клиентское соединение. Обычный метод закрытия сокета TCP иногда может быть медленным и заставлять приложения думать, что соединение все еще открыто, даже если это не так.

Браузер должен действительно сделать это для вас, когда вы закрываете или перезагружаете страницу. Тем не менее, вы можете убедиться, что закрытый фрейм отправлен, выполнив захват события beforeunload:

window.onbeforeunload = function() {
    websocket.onclose = function () {}; // disable onclose handler first
    websocket.close();
};

Я не уверен, как вы можете получить событие onclose после обновления страницы. Объект websocket (с обработчиком onclose) больше не будет существовать после перезагрузки страницы. Если вы сразу же пытаетесь установить соединение WebSocket на своей странице при загрузке страницы, возможно, вы столкнулись с проблемой, когда сервер отказывает в новом соединении вскоре после того, как старое отключилось (или браузер не готов установить соединения в точке, где вы пытаетесь подключиться), и вы получаете событие onclose для нового объекта websocket.

Дело в том, что сегодня используются две основные версии протоколов WebSockets. Старая версия, которая использует [0x00][message][0xFF] протокол, а затем есть новая версия с использованием форматированных пакетов Hybi.

Старая версия протокола используется Opera и iPod/iPad/iPhone, поэтому очень важно, чтобы на серверах WebSockets была реализована обратная совместимость. С этими браузерами, использующими старый протокол, я обнаружил, что обновление страницы, переход от страницы или закрытие браузера приводят к тому, что браузер автоматически закрывает соединение. Большой!!

Однако в браузерах, использующих новую версию протокола (например, Firefox, Chrome и, в конечном итоге, IE10), только закрытие браузера приведет к тому, что браузер автоматически закроет соединение. То есть, если вы обновите страницу или уйдете со страницы, браузер НЕ автоматически закроет соединение. Однако, что делает браузер, так это отправляет пакет hybi на сервер с первым байтом (прототип) 0x88 (более известный как закрытый фрейм данных). Как только сервер получает этот пакет, он может принудительно закрыть само соединение, если вы того пожелаете.

Очень просто, вы закрываете его:)

var myWebSocket = new WebSocket("ws://example.org"); 
myWebSocket.send("Hello Web Sockets!"); 
myWebSocket.close();

Вы проверили также следующий сайт и проверить вводную статью Opera

Как упоминается theoobe, некоторые браузеры не закрывают веб-сокеты автоматически. Не пытайтесь обрабатывать любые события "закрыть окно браузера" на стороне клиента. В настоящее время нет надежного способа сделать это, если вы рассматриваете поддержку основных настольных И мобильных браузеров (например, onbeforeunload не будет работать в мобильном сафари). У меня был хороший опыт решения этой проблемы на стороне сервера. Например, если вы используете Java EE, взгляните на javax.websocket.Endpoint, в зависимости от браузера OnClose метод или OnError метод будет вызван, если вы закроете / перезагрузите окно браузера.

пожалуйста, используйте это

      var uri = "ws://localhost:5000/ws";
var socket = new WebSocket(uri);
socket.onclose = function (e){
    console.log(connection closed);
};
window.addEventListener("unload", function () {
    if(socket.readyState == WebSocket.OPEN)
        socket.close();
});

Закрытие браузера не вызывает событие закрытия веб-сокета. Вы должны вызвать socket.close() вручную.

Используя метод close веб-сокета, где вы можете написать любую функцию в соответствии с требованиями.

var connection = new WebSocket('ws://127.0.0.1:1337');
    connection.onclose = () => {
            console.log('Web Socket Connection Closed');
        };

Мероприятия следует избегать. Из МДН:

Особенно на мобильных устройствах событие beforeunload не срабатывает надежно.

Кроме того, его следует использовать только для сохранения пользовательских данных, а не для закрытия соединения через веб-сокет.

Вместо закрытия соединения наbeforeunload, я бы использовалpagehideили (запасной вариант)unloadсобытие, как предлагает документация разработчика Chrome .

      const terminationEvent = 'onpagehide' in self ? 'pagehide' : 'unload';
window.addEventListener(terminationEvent, (event) => {
    if (event.persisted === false) {
        // client is gone
        socket.onclose = function () { };
        socket.close();
    }
});

Преимущество этого подхода не только в том, что он рекомендован спецификацией, но и в том, что вы все еще можете получить запрос, подтверждающий, что пользователь хочет уйти (window.addEventListener("beforeunload", () => return "Are you sure?");) без отключения пользователя, если он решит остаться на странице. Как говорится в документах Chrome (выделено мной):

Документ по-прежнему виден, и событие по-прежнему можно отменить.

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

Другие вопросы по тегам