websocket на стороне клиента
Коллеги, доброго времени суток,
суть проблемы такова. Пишу приложение и вэбсокетами. Встал на клиенте. Точнее встал над recconect Вот код: 'use strict'; window.SocketModule = ((window) => { class SocketModule { constructor(options) { this.path = options.path; this.reconnectNumber = 0; this.connection(); } /* * connection - установить соединение * connectionStatus, * * disconnection - остановить соединение * reconnection - пересоединение * * send - отправить сообщение * accept - принять сообщение * */ connection() { this.socket = new WebSocket(this.path); this.socket.addEventListener('open', (e) => { console.log('connection...); this.send('checkConnection', { connect: true }); }); this.socket.addEventListener('close', (e) => { console.log('reconnection... please wait...'); this.connection(this.path); }); } send(event_name, data) { // event_name (string), data (string, array, object) if (event_name.constructor.name === 'String') { let _this = this; this.wait(function() { _this.socket.send(JSON.stringify({event_name, data})); }, 0); } else return; } wait(callback, time) { let timer; clearTimeout(timer); if (this.socket.readyState === 1) { callback(); } else { let _this = this; timer = setTimeout(function() { _this.wait(callback, time); }, time); } } }; return SocketModule; })(window); Создается экземпляр класса websocket и на него навешивается события open и close. По событию close происходит рекурсивный вызов этой же функции. В консоль падает сообщение : WebSocket connection to 'ws://server.localhost:5555/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED - так и должно быть. После включения сервера связь появляется, все ок. Но у меня вопрос, может кто делал или словом подскажет. Как избежать данного сообщения??? Или может, как сделать reconnect??? |
Проблема, даже не в сообщении, а в том, что увеличивается время каждого следующего переподключения
|
Цитата:
Сохраняйте ID таймаута в this. wait(callback, time) { clearTimeout(this._timer); if (this.socket.readyState === 1) { callback(); } else { this._timer = setTimeout(() => this.wait(callback, time), time); } } |
ruslan_mart, ничего не поменялось, задержки те же самые.
|
взял тут https://socket.io/docs/client-api/#n...er-url-options
<table> <thead> <tr> <th>Option</th> <th>Default value</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>path</code></td> <td><code>/socket.io</code></td> <td>name of the path that is captured on the server side</td> </tr> <tr> <td><code>reconnection</code></td> <td><code>true</code></td> <td>whether to reconnect automatically</td> </tr> <tr> <td><code>reconnectionAttempts</code></td> <td><code>Infinity</code></td> <td>number of reconnection attempts before giving up</td> </tr> <tr> <td><code>reconnectionDelay</code></td> <td><code>1000</code></td> <td>how long to initially wait before attempting a new reconnection (<code>1000</code>). Affected by +/- <code>randomizationFactor</code>, for example the default initial delay will be between 500 to 1500ms.</td> </tr> <tr> <td><code>reconnectionDelayMax</code></td> <td><code>5000</code></td> <td>maximum amount of time to wait between reconnections (<code>5000</code>). Each attempt increases the reconnection delay by 2x along with a randomization as above</td> </tr> <tr> <td><code>randomizationFactor</code></td> <td><code>0.5</code></td> <td>0 <= randomizationFactor <= 1</td> </tr> <tr> <td><code>timeout</code></td> <td><code>20000</code></td> <td>connection timeout before a <code>connect_error</code> and <code>connect_timeout</code> events are emitted</td> </tr> <tr> <td><code>autoConnect</code></td> <td><code>true</code></td> <td>by setting this false, you have to call <code>manager.open</code> whenever you decide it’s appropriate</td> </tr> <tr> <td><code>query</code></td> <td><code>{}</code></td> <td>additional query parameters that are sent when connecting a namespace (then found in <code>socket.handshake.query</code> object on the server-side)</td> </tr> </tbody> </table> Each attempt increases the reconnection delay by 2x along with a randomization as above |
SuperZen, спасибо, но socket io в данном случае не подходит
|
а, что-то не увидел ) что не socket.io -
да, vanilla websocket не имеет таких настроек по дефолту ) как socket.io https://github.com/websockets/ws/wik...auto-reconnect https://stackoverflow.com/questions/...s-in-websocket https://github.com/joewalnes/reconnecting-websocket - древнее какое-то оно, но как идея ) а почему не сделать на socket.io, ? или бэк на другом языке? |
SuperZen, да подходит, только у меня подразумевается cluster, а там еще в настройках не разбирался, по всей видимости буду использовать именно socket io. Но с другой стороны, в моем случае проблема в сообщении, которое прилетает в консоль WebSocket connection to 'ws://server.localhost:5555/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED. Подключение восстанавливается но с задержкой, и чем дольше выключен сервер, тем больше эта задержка, фотку не могу вставить в сообщение, почему с компа не дает???????
В socket io есть лишнее. Мне в проекте нужны только websockets, а там все транспорты, ws + ajax и тд, он сам проверяет на доступность. Мне нужно по идее, только подключение по ws. От сюда и решил сам написать модуль. Можно конечно воткнуть ajax, для проверки доступности сервера... |
Цитата:
Если да, то это оптимизации браузера - вроде сафари так делает, тогда как файрфокс с хромом просто повышают задержку до 1 секунды. В таком случае надо перенести setTimeout в веб-воркер: https://github.com/mohayonao/worker-timer |
Белый шум, все верно, браузер увеличивает задержку примерно на 1 секунду (в хроме сижу), и через 50 попыток соединиться, я включаю сервер и жду пока клиент подключится.
если не вешать слушалку событий то ничего не вылетает (я про сообщения, но и не могу в браузере дать пользователю понять что связи нет))))))))) с аяком проще, там 502 и все норм, а тут есть только статус, и слушалка (addeventlistener) но при close вылетает сразу ошибка |
Еще вопрос, может кто знает, как закрыть соединение????? принудительно.
Вопрос, к тому, что по событию close делаю this.spcket = null, а потом вызываю опять connection то все равно ничего выходит |
|
коллеги, всем спасибо, решил задачу.
Все работает, отлично и быстро |
что сделал то?)
|
SuperZen, радость была преждевременной)))))))))))))))
Дабы исключить ошибку соединения сокета, я перед созданием экземпляра, пульнул ajax на сервер, и если он мне вернул код 200 то все ок, создаем и вешаем слушатели open и close. по событию close просто снова вызываем connection. Вот код: 'use strict'; window.SocketModule = (function(window) { class SocketModule { constructor(options) { this.socket; this.path = options.path; this.connection(); this.reconnectCount = 60; } connection() { const _t = this; this.checkServer(function(server) { if (!server) return; _t.socket = new WebSocket(_t.path); _t.socket.addEventListener('open', function() { _t.indicatorControl(true); }); _t.socket.addEventListener('close', function() { _t.indicatorControl(false); _t.connection(); }); }); return this; } checkServer(callback) { let timer; const _t = this; let count = 0; timer = setInterval(function() { const req = new XMLHttpRequest(); req.open('GET', '/api/connect', true); req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); req.send(null); req.addEventListener('load', function() { if (req.status === 200) { clearInterval(timer); callback(true); } }); count++; if (count === _t.reconnectCount) { clearInterval(timer); console.log('Превышен лимит ожидания!'); } }, 200); return this; } sendMessage(event_name, data) { if (event_name.constructor.name === 'String') { const _t = this; // this.readyStateConnection(function() { _t.socket.send(JSON.stringify({event_name, data})); // }); } else return; return this; } readyStateConnection(callback) { let timer; clearTimeout(timer); if (this.socket.readyState === 1) { callback(); } else { const _t = this; timer = setTimeout(function() { _t.readyStateConnection(callback); }, 0); } } }; return SocketModule; })(window); НО!!!!!!!!!!!!!!!!!!!!!!!!!))))))))))))))))))))) ) Когда я вызываю метод sendMessage в другом классе: 'use strict'; window.Chat = (function(window) { const options = window.options; const socketModule = window.SocketModule; const ServiceModule = window.ServiceModule; const MessageModule = window.MessageModule; class Chat { constructor() { this.transport = new socketModule({ path: options.socketPath }); } init() { this.transport.sendMessage('chat_message', { type: 'text', text: 'Lorem ipsum dolor sit amet, megatron7000' }); } } return Chat; })(window); Эта шайтан машина))) выдает мне - annot read property 'send' of undefined! В методе readyStateConnection this.socket = undefined!!!! если вывести в консоль this, то он есть, а если this.socket его нет. |
Теперь встал вопрос в том, видимо где я потерял контекст???????!!!!!!!!!! или просто туплю сижу.
|
server.js
var path = require('path') var http = require('http') var express = require('express') var WebSocket = require('ws') const app = express() const httpServer = http.createServer(app) const wss = new WebSocket.Server({ server: httpServer }) app.use(express.static(path.join(__dirname, 'public'))) app.get('/', function (req, res) { res.sendfile(path.join(__dirname, 'public', 'index.html')) }) wss.on('connection', (client) => { client.on('message', (message) => { console.log('message: ', message) client.send('ok') }) client.send('connected') }) httpServer.listen(2999, () => { console.log('started at http://localhost:2999') }) public/index.html <html> <head> </head> <body> <div id="connection">not connected</div> <button id="button">Send</div> <script> var connection = document.getElementById('connection') document.getElementById('button').addEventListener('click', function () { ws.send('message') }) var ws function connect() { console.log('connecting') connection.innerHTML = 'connecting...' ws = new WebSocket('ws://localhost:2999') ws.addEventListener('open', wsOpenEvent) ws.addEventListener('close', wsCloseEvent) ws.addEventListener('message', wsMessageEvent) ws.addEventListener('error', wsErrorEvent) } function wsOpenEvent(e) { console.log('connected') connection.innerHTML = 'connected' } function wsCloseEvent(e) { console.log('close') connection.innerHTML = 'disconnected' setTimeout(() => connect(), 5000) } function wsMessageEvent(e) { console.log('message', e) } function wsErrorEvent(e) { console.log('error', e) } connect() </script> </body> </html> package.json { "name": "web_socket", "version": "1.0.0", "main": "index.js", "license": "MIT", "dependencies": { "express": "^4.16.3", "ws": "^6.0.0" }, "scripts": { "start": "node server.js" } } я так сделал reconnect ) и модули бы делал через webpack или parcel ) |
SuperZen, по сути то же самое, но только Реконнект делает через 5 секунд. Я так тоже сделал сначала)))) но теперь вопрос не в Реконнекте, а в том что контекст потерялся и при вызове из другого места ошибка
|
SuperZen, можно по идее сделать и без проверки доступности сервера, но тогда при создании нового экземпляра будет ошибка в консоли и все. Оборвать попытки, например, через 30 секунд и выдать клиенту сообщение какое нибудь, что серверу кранты. Вот.
У меня задача стоит, сделать коннект по сокет соединению, если обрыв то реконнект. Обязательно что бы модульная система была. Дабы коллеги вникали сразу, что бы использовать можно было в разных модулях. |
Реконнект сделал, работает отлично. А вызывать не могу в других местах))))))))))))))))))))))
|
Коллеги, прошу опять помощи, как и писал выше после создания соединения, не могу вызывать sendMessage!!! как исправить????
|
ну выложи куда-нибудь проект, а то, из 4 "классов" показал только 2, как происходит инициализация и т.д. неизвестно
|
SuperZen, остальные не важны:
main.js - тут просто подключаются модули, будут проверки браузера и в конце запускается приложение Chat: 'use strict'; (function(window) { document.addEventListener('DOMContentLoaded', function() { const Chat = window.Chat; const chat = new Chat; chat.init(); }); })(window); chat.js - это само приложение, в нем будет вся логика (пока что логика ws)))) вернее организовать подключение и дальнейшее использование в приложении) : 'use strict'; window.Chat = (function(window) { const socketModule = window.SocketModule; class Chat { constructor() { this.transport = new socketModule({ path: window.options.socketPath }); } init() { this.transport.sendMessage('chat_message', { type: 'text', text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.' }); } } return Chat; })(window); и собственно socketModule.js, логика его как и выше писал. : 'use strict'; window.SocketModule = (function(window) { class SocketModule { constructor() { this.socket; this.connection(); } connection() { const _t = this; this.checkServer(function(server) { if (!server) return; _t.socket = new WebSocket('ws://server.localhost:5555'); _t.socket.addEventListener('open', function() { _t.sendMessage('connection', { client: true }); _t.indicatorControl(true); }); _t.socket.addEventListener('close', function() { _t.indicatorControl(false); _t.connection(); }); }); return this; } checkServer(callback) { let timer; const _t = this; let count = 0; timer = setInterval(function() { const req = new XMLHttpRequest(); req.open('GET', '/api/connect', true); req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); req.send(null); req.addEventListener('load', function() { if (req.status === 200) { clearInterval(timer); callback(true); } }); count++; if (count === _t.reconnectCount) { clearInterval(timer); console.log('Превышен лимит ожидания!'); } }, 200); return this; } sendMessage(event_name, data) { // event_name (string), data (string, array, object) if (event_name.constructor.name === 'String') { const _t = this; this.readyStateConnection(function() { _t.socket.send(JSON.stringify({event_name, data})); }); } else return; return this; } readyStateConnection(callback) { let timer; clearTimeout(timer); if (this.socket.readyState === 1) { callback(); } else { const _t = this; timer = setTimeout(function() { _t.readyStateConnection(callback); }, 0); } } }; return SocketModule; })(window); Ну а результат res.render вот, но я думаю он не нужен особо: <!DOCTYPE html><html lang=ru><head><title>Тестим WebSockets</title><meta charset=UTF-8><meta name=viewport content="width=device-width,initial-scale=1"><link rel="shortcut icon" href="/images/favicon.png?v=2qx-wa3g2x" type=image/png><link rel=stylesheet href="/stylesheets/style.css?v=2tg-1rjnwfd"></head><body><noscript>Ваш браузер не поддерживает JavaScript. Включите или используйте другой браузер</noscript><section id=chat><div class=overlay><div id=chat_header><h2>Привет, anton</h2><a href=/logout class=logout>Выйти</a></div><div class=overflow><div id=messages><p>Сообщения в чате:</p><p id=no_messages>Сообщений нет</p></div><div id=users_list><p>Пользователи online</p><ul><li><p>marey</p></li></ul></div></div></div><div id=control_panel><form method=post id=send_message autocomplete=off><textarea name=message_text placeholder="ваше сообщение..."></textarea><button type=submit>Отправить</button></form></div></section><div id=server_message data-action=false></div></body><script src="/javascripts/options.js?v=5a-n5whjv"></script><script src="/javascripts/modules/SecureModule.js?v=8v-18pg8f1"></script><script src="/javascripts/modules/ServiceModule.js?v=1as-zlyrbi"></script><script src="/javascripts/modules/UserModule.js?v=uj-rhyhqe"></script><script src="/javascripts/modules/MessageModule.js?v=r3-m0w1dz"></script><script src="/javascripts/modules/socketModule.js?v=6jh-bhcxxf"></script><script src="/javascripts/Chat.js?v=q0-dpx5ol"></script><script src="/javascripts/main.js?v=dt-14wlng5"></script></html> |
this теряется когда вызываю checkServer в socketModule. Вернее я его сохраняю в _t а в this.socket самого модуля он не сохраняется.
Но тогда почему если сделать console.log(this); и console.log(this.socket); в методе sendMessage первое выведет socketModule в котором будет this.socket = экземпляру websocket (все правильно), а второе выведет undefined |
const chat = new Chat; не? https://developer.mozilla.org/ru/doc.../Operators/new const chat = new Chat(); |
Та и не нашёл, где там модули! То, что вы запихнули файлы в папку `modules` и, о ужас, дописали к имени переменной "Module", конечно же не сделает из них ни каким автомагическим способом модули. (также они все глобальны у вас, но странности вида `const Chat = window.Chat;` тоже зачем-то встретились)
Чтобы указать входную точку для вашей программы, используйте атрибут type со значением module <script src="/javascripts/main.js" type="module"></script>И это единственное, что вам нужно указать в вашем html. const chat = new Chat; chat.init();Так и представил, что было если было бы так: const array = new Array; array.init();Так что почему бы не просто `const chat = new Chat;`? Используйте стрелочные функции, тогда не придётся писать такое `const _t = this;` Можно просто `event_name.constructor === String` `this.socket.readyState === 1` WTF??? Почему не `this.socket.readyState === this.socket.OPEN` мне пришлось посмотреть в документации, что такое 1 У вас в SocketModule.prototype.readyStateConnection переменная timer всегда новая, так что старый таймер невожможно остановить У вас в chat.js передается `path: options.socketPath`, но в socketModule.js не используется... Вместо класса XMLHttpRequest можно использовать функцию fetch. Ваш /api/connect может возвратить не только 200 но и 304 (может быть, у меня именно так и произошло), почему бы не проверять так `response.status >= 200 && response.status < 400`, или даже `response.ok` (если используете fetch) `this.socket;` почему бы не (однако это ужасно, кто знает как лучше?) this.socket = { readyState: WebSocket.prototype.CLOSED };тогда не будет неожиданных ошибок связанных тем, что this.socket равен null Это и была главная причина! Вот исправленный код, исправленный с учётом сказанного выше, и модули тоже используются (а не переменные с именем, содержащим "Module") В index.html теперь только нужно, то что касается скриптов <script src="/javascripts/main.js" type="module"></script> main.js import Chat from "./Chat.js"; document.addEventListener("DOMContentLoaded", () => { const chat = new Chat; }); options.js Я использовал этот сервер для теста, у вас там должно быть что-то другое export default { socketPath: "wss://echo.websocket.org" }; Chat.js import Socket from "./modules/socketModule.js"; import options from "./options.js"; export default class Chat { constructor() { this.transport = new Socket({ path: options.socketPath }); // FIXME в отдельный модуль это я проверил как оно работает если текст в текстовое поле ввести и отправить document.addEventListener("click", event => { if(event.target.matches("#send_message [type='submit']")) { event.preventDefault(); this.transport.sendMessage('chat_message', { type: 'text', text: document.querySelector("[name='message_text']").value }); } }) this.transport.sendMessage('chat_message', { type: 'text', text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.' }); } } modules/socketModule.js export default class SocketModule { constructor({ path }) { this.socket = { readyState: WebSocket.prototype.CLOSED }; this.connection(); this.path = path; } connection() { this.checkServer(server => { if (!server) return; this.socket = new WebSocket(this.path); this.socket.addEventListener('open', () => { this.sendMessage('connection', { client: true }); this.indicatorControl = true; }); this.socket.addEventListener("message", event => { // FIXME логику разбора в отдельный модуль const { event_name, data } = JSON.parse(event.data); if(event_name === "chat_message") { var p = document.createElement("p"); p.textContent = data.text; document.getElementById("messages").append(p); } }); this.socket.addEventListener('close', () => { this.indicatorControl = false; this.connection(); }); }); return this; } set indicatorControl(value) { // что-то делает } checkServer(callback) { let timer; let count = 0; timer = setInterval(function() { fetch("/api/connect", { headers: { "Content-Type": "application/x-www-form-urlencoded" } }).then(response => { if(response.ok) { clearInterval(timer); callback(true); } }); count++; if (count === this.reconnectCount) { clearInterval(timer); console.log('Превышен лимит ожидания!'); } }, 200); return this; } sendMessage(event_name, data) { // event_name (string), data (string, array, object) if (event_name.constructor === String) { this.readyStateConnection(() => { console.log(event_name, data); this.socket.send(JSON.stringify({ event_name, data })); }); } else return; return this; } readyStateConnection(callback) { if (this.socket.readyState === this.socket.OPEN) { callback(); } else { setTimeout(() => { this.readyStateConnection(() => callback()); }, 0); } } }; Но очень там не сердитесь, что я всё исправил на модули, как описано в стандарте JavaScript. UPD В readyStateConnection всё-таки не нужно стирать таймер, а то сообщения отправленные до подключения не отправятся. Исправил код выше(modules/socketModule.js) |
Malleys, +100500 к вашей карме))))))))) Щас посмотрим!!!!!
|
Malleys, про автомагию это потому что толковых учебных пособий нету, где все и вся будет рассказано)))))))))))))))))))) поэтому приходится учиться как получается к сожалению
|
А авторы учебников откуда узнают?
Я не знаю как такое для обучения, но я уже прочитал спецификацию языка. Девятое издание. Большая часть конечно же взята из предыдущего издания. https://www.ecma-international.org/e...9.0/index.html Я не знаю как такое для обучения, но что есть в JavaScript`е и как работает, всё описано. Я не говорю, что вам именно так надо изучать, просто я так узнаю. |
Malleys, теперь по коду, который вы написали. Большое спасибо за помощь. Но!
в chat.js в конструкторе, после слушателя просто вызываем метод sendMessage и он не срабатывает! |
те если просто вызвать в любом месте этот метод работать не будет если с задержкой то все нормально.
|
Цитата:
|
Malleys, большое спасибо, разобрался))))) Я сам запутался. Еще раз спасибо
|
Malleys, все работает, теперь
|
Malleys, и кстати, а почему мои классы глобальные??? Насколько я понимаю, я добавляю свойства в объект window
|
Часовой пояс GMT +3, время: 16:20. |