В одном из моих скриптов есть такой код:
var webSocket = window.WebSocket || window.MozWebSocket;
window.ws = new webSocket('ws://64.121.210.140:2585/consoleappsample', 'my-protocol');
Который работает нормально. Однако, когда пользователь меняет страницу, мне приходится заново устанавливать соединение. Я полагаю, что это вызывает проблемы в моем коде, потому что если клиент отправляет данные на сервер, а затем меняет страницы, данные могут быть не получены, и возникают условия гонки.
Я пытался поместить window.ws
в глобальную область видимости, но это, похоже, не решило проблему. Есть ли способ сохранить соединение WebSockets между страницами, чтобы соединение не нужно было постоянно восстанавливать?
Упомянутая вами глобальная область видимости всегда связана с JavaScript Context, а Context создается для каждого окна (и уничтожается при выгрузке документа из памяти). Поэтому ваши усилия бесполезны: вы не сможете сохранить соединение открытым, если пользователь сменит страницу. Конечно, вы можете сделать ваш webapp "одностраничным" приложением, где все данные загружаются с помощью XMLHttpRequest / ajax / WebSocket. Таким образом, выход со страницы означает выход/закрытие приложения, и имеет смысл закрыть сокет.
Другим старым подходом может быть размещение страниц во фрейме, где пользователь перемещается только по фрейму (даже если он занимает весь размер окна). Таким образом, вы можете создать свой WebSocket в самом верхнем окне, которое никогда не изменяется (это также означает, что URL, отображаемый в строке местоположения, всегда будет одним и тем же).
При этом я согласен с @dystroy: ваше приложение должно быть всегда способно справиться с этим сценарием - у пользователя могут возникнуть проблемы с сетью, и он на мгновение потеряет соединение, даже если он не покинет страницу.
Вы можете попробовать создать WebSocket-соединение в Shared WebWorker, который позволяет нескольким страницам из одного домена совместно использовать контекст выполнения. Однако неясно, сохраняются ли Shared Workers при перезагрузке или замене страницы: https://stackoverflow.com/questions/9336774/do-shared-web-workers-persist-across-a-single-page-reload-link-navigation/9337749#comment11795892_9337749.
Кроме того, Shared WebWorkers имеют ограниченную поддержку браузеров (webkit и Opera) в настоящее время.
Обновление:
Поскольку один общий веб-работник может обслуживать несколько страниц, его реализация немного сложнее, чем у обычных веб-работников.
Вот пример разделяемого веб-работника, который использует WebSockets и может обмениваться между
Сначала HTML:
<!DOCTYPE html>
<html>
<body>
<script>
var worker = new SharedWorker("shared.js");
worker.port.addEventListener("message", function(e) {
console.log("Got message: " + e.data);
}, false);
worker.port.start();
worker.port.postMessage("start");
</script>
</body>
</html>
Javascript, реализующий совместную работу в shared.js
:
var ws = null
var url = "ws://" + location.hostname + ":6080"
self.addEventListener("connect", function(e) {
var port = e.ports[0]
port.addEventListener("message", function(e) {
if (e.data === "start") {
if (ws === null) {
ws = new WebSocket(url);
port.postMessage("started connection to " + url);
} else {
port.postMessage("reusing connection to " + url);
}
}
}, false);
port.start();
}, false);
Я проверил, что это работает в Chrome 52.
К сожалению, самое чистое решение без изменения вашего сайта в SPA-приложении получается при использовании только одного ServiceWorker, потому что он выполняет работу в фоновом режиме (также при закрытой вкладке) и решает проблемы нескольких вкладок. Я сказал "к сожалению", потому что они все еще не совместимы с большинством браузеров. Единственное решение, которое я нашел самостоятельно, заключается в группировке каналов сокетов на стороне сервера, и создании очереди для хранения сообщений, потерянных при смене страницы. В этом случае у вас есть своего рода виртуальные каналы.
Вы можете войти в беседу с сервером через WebSocket, localStorage С, который сохраняется во время загрузки страницы.