WebSockets в Chrome и Firefox отключается после одной минуты бездействия

Я обнаружил, что WebSockets в Chrome и Firefox отключается через ровно одну минуту бездействия. Основываясь на материалах, которые я видел в Интернете, я был настроен обвинять прокси или некоторые настройки сервера или что-то в этом роде, но этого не происходит в IE или Edge. Кажется, если сокеты отключены сервером после одной минуты бездействия, которые будут применяться к IE и Edge так же, как Chrome и Firefox.

Кто-нибудь знает, почему это? Документировано ли это где угодно? Я знаю возможный способ остановить это, пинговая, но меня больше интересует, почему это происходит. Код причины, указанный при отключении, равен 1006, что указывает на то, что браузер закрыл соединение. Ошибок не возникает, и событие onerror для сокета не запускается.

Этот проект был построен по адресу https://glitch.com/edit/#!/noiseless-helmet, где вы можете видеть и запускать все. Страница клиента обслуживается здесь: https://noiseless-helmet.glitch.me/

Вот моя страница клиента:

<div id="div">
</div>
<script>
  let socket = new WebSocket("wss://noiseless-helmet.glitch.me/");
  socket.onmessage = function(event) {
    div.innerHTML += "<br>message " + new Date().toLocaleString() + " " + event.data;
  };
  socket.onopen = function (event) {
    div.innerHTML += "<br>opened " + new Date().toLocaleString();
    socket.send("Hey socket! " + new Date().toLocaleString());
  };
  socket.onclose = function(event) {
    div.innerHTML += "<br>socket closed " + new Date().toLocaleString();
    div.innerHTML += "<br>code: " + event.code;
    div.innerHTML += "<br>reason: " + event.reason;
    div.innerHTML += "<br>clean: " + event.wasClean;
  };
  socket.onerror = function(event) {
    div.innerHTML += "<br>error: " + event.error;
  };
</script>

И вот мой код сервера Node.js:

var express = require('express');
var app = express();
app.use(express.static('public'));

let server = require('http').createServer(),
  WebSocketServer = require('ws').Server,
  wss = new WebSocketServer({ server: server });

app.get("/", function (request, response) {
  response.sendFile(__dirname + '/views/index.html');
});

let webSockets = [];
wss.on('connection', function connection(socket) {
  webSockets.push(socket);
  webSockets.forEach((w) => { w.send("A new socket connected"); });
  socket.on('close', (code, reason) => {
    console.log('closing socket');
    console.log(code);
    console.log(reason);
    let i = webSockets.indexOf(socket);
    webSockets.splice(i, 1);
  });
});

server.on('request', app);
server.listen(process.env.PORT, function () {
  console.log('Your app is listening on port ' + server.address().port);
});

Ответы

Ответ 1

Кажется, если сокеты отключены сервером после одной минуты бездействия, которые будут применяться к IE и Edge так же, как Chrome и Firefox.

Хм, нет, нет. IE и Edge могут реализовывать пакет ping как часть протокола WebSocket.

Протокол WebSocket включает в себя поддержку протокола ping уровне протокола, который JavaScript API не предоставляет. Это немного ниже уровня, чем пинг-код пользовательского уровня, который часто реализуется.

Этот трафик ping pong сбрасывает таймеры в любых сетевых посредниках (прокси, балансировщики нагрузки и т.д.) - и все соединения времени для отметки устаревших подключений для закрытия (например, установки времени установки Heroku на 55 секунд).

Большинство браузеров доверяют серверу для реализации ping, который вежлив (поскольку серверам необходимо управлять своей нагрузкой и таймаутом для pinging...

... однако это также немного расстраивает, поскольку браузеры не имеют представления о том, что соединение было анонимно потеряно. Именно поэтому многие клиенты JavaScript реализуют пинг на уровне пользователя (например, JSON {event: "ping", data: {...}} или другое "пустое" сообщение о событии).

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

Для некоторых особенностей, касающихся тайм-аутов по умолчанию nginx (при проксировании соединений WebSocket), вы можете прочитать ответ @Hendry.

Ответ 2

Насколько я понял из исследования этого, это вызвано тайм-аутом websocket в течение периода времени, когда данные не отправляются. Вероятно, это зависит от браузера.

Вы можете использовать pings, чтобы разрешить это или просто повторно подключиться, когда вам нужно снова использовать сокет.

Имеет смысл не сохранять открытые сокеты, когда они не используются со стороны сервера, как со стороны браузера. Например, Chrome имеет ограничение на то, сколько подключений может быть открыто, если ограничение будет 64 подключения, и у вас есть открытые 64 вкладки (что очень вероятно для меня, поскольку у меня всегда есть множество вкладок), и каждая вкладка подключена к сервер, больше никаких подключений не было (на самом деле подобное случилось со мной однажды, когда у меня закончились доступные сокеты в Chrome, смешно).

Существует proxy_read_timeout (http://nginx.org/r/proxy_read_timeout), который также применим к соединениям WebSocket. Вы должны столкнуться с этим, если ваш сервер не отправит ничего в течение длительного времени. В качестве альтернативы вы можете настроить свой сервер для периодической отправки ping-кадров websocket, чтобы сбросить таймаут (и проверить, все ли соединение еще живое).

https://forum.nginx.org/read.php?2,236382,236383#msg-236383

У веб-сокетов есть тайм-аут в режиме ожидания 60 секунд: если вы не используете пульс или подобное с помощью кадров ping и pong, тогда сокет предполагает, что пользователь закрыл страницу и закрыл сокет, чтобы сохранить ресурсы.

https://www.codeproject.com/questions/1205863/Websocket-is-closed-after-min

https://github.com/tornadoweb/tornado/issues/1070

Ответ 3

Спецификация протокола WebSocket определяет рамки Ping и Pong, которые могут использоваться для поддержания активности, сердечных ударов, проверки состояния сети. Ping означает, что клиент/сервер отправляет iq, чтобы сообщить серверу/клиенту другой стороны, что он поддерживает соединение, а другая сторона отправит подтверждение с понгами с одинаковыми данными полезной нагрузки.

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

подробнее: http://vunse.blogspot.in/2014/04/websocket-ping-pong.html

Ответ 4

Возможно, это не чистое решение, но именно так я реализовал websocket в JS для автоматического переподключения при отключении

var socket_main
const mainSocketMessageListener = (event) => {
  //retreive the data here
  console.log(event.data)
}

const mainSocketOpenListener = (event) => {
  console.log("Websocket opened")
  //Example of sending message to websocket here
  socket_main.send(JSON.stringify({
    event: "subscribe",
    data: ["all"]
  }))
}

const mainSocketCloseListener = (event) => {
  if (socket_main) {
    console.error('Websocket disconnected.')
  }
  socket_main = new WebSocket('SOCKET_URL')
  socket_main.addEventListener('open', mainSocketOpenListener)
  socket_main.addEventListener('message', mainSocketMessageListener)
  socket_main.addEventListener('close', mainSocketCloseListener)
}

//connect the first time
mainSocketCloseListener()