websocket на node js


  • administrators

    Периодически всплывают вопросы на форуме касательно websocket и непонятной работы node js.
    Начну с websocket, ставим модуль ws для node js.
    В описании есть тестовый код для вебсокет эхо :

    const WebSocket = require('ws');
     
    const ws = new WebSocket('wss://echo.websocket.org/', {
     origin: 'https://websocket.org'
    });
     
    ws.on('open', function open() {
      console.log('connected');
      ws.send(Date.now());
    });
     
    ws.on('close', function close() {
      console.log('disconnected');
    });
     
    ws.on('message', function incoming(data) {
      console.log(`Roundtrip time: ${Date.now() - data} ms`);
     
    });
    

    Только он не сработает. Экшен выполнится, но лог BAS'a будет пустым.


    Здесь на помощь придут глобальные переменные в node js!
    Создаём экшен node js:

    WebSocket = require("ws");
    
    ws = new WebSocket("wss://echo.websocket.org/", {
    });
    
    ws.on("open", function open() {
      global.tost = "connected"
      ws.send("test");
    });
     
    ws.on("close", function close() {
      global.tost = "disconnected"
    });
     
    ws.on("message", function incoming(data) {
      global.tost = data
    });
    

    далее создаём ещё один экшен node js:

    console.log(global.tost);
    

    В итоге на выходе получаем данные из глобальной переменной global.tost.


    Если соединение не установлено, то экшен выведет в лог undefined. Нужно просто подождать и выполнить его секундной позже :D


    Далее можно создать ещё один экшен node js

    ws.send("New Test");
    

    и ответ будет уже New Test.


    !!!Очень важное замечаение!!!
    Экшен node js сбросит все функции и переменные если его просто открыть! Или создать новый экшен.

    То есть если после удачного конекта и получения ответа, открыть экшен node js в котором мы устанавливали соединение, то следующий экшен console.log(global.tost); вернёт undefined и больше не изменится.


    Вот тестовый скрипт.
    !!! Содержит модуль ws, который начнёт скачиваться при открытии скрипта. (Без паники! А то были инциденты...)
    0_1527193540216_на форум 15.png


  • administrators

    Глобальные переменные в node js мне уже ранее пригождались.. Возможно помогут и другим в подобных случаях. Когда обработчик события ни чего не возвращает.

    В обработчике объявить переменную с приставкой global., а в следующем экшене node js её использовать.



  • @Fox Вот наскоряк сделал бескостыльный код

    const WebSocket = require('ws');
     
    const ws = new WebSocket('wss://echo.websocket.org/', {
     origin: 'https://websocket.org'
    });
     
    function wsRun(){
      return new Promise((resolve, reject) => {
        ws.on('open', function open() {
            ws.send(Date.now());
            resolve('connected');  	
        });
    
        ws.on('close', function close() {
            reject('disconnected');  
        });
      });
    }
    
    
    await (new Promise((resolve, reject) => {
    	var promise=wsRun();
            promise
            .then(res=>{ 
                console.log(res);
                ws.on('message', function incoming(data) {
                    console.log(`Roundtrip time: ${Date.now() - data} ms`);
                    resolve();	   
                }),
            err=>{
                console.log(err);
                reject();
            };
        });	
    }));
    

    Не выводится в лог тк методы асинхронные и основной поток продолжает свое выполнение.не дождавшись ответа.

    Единственное меня смущает ( но ленился ковыряться в библиотеке), то, что должен быть еще по идее обработчик на случай ошибок. Например нет ответа от сервера.

    Еще наверное стоит вторую половину кода тоже завернуть в функцию, которая также получает промис и в случае ошибки через SetTimeout вызывать ее повторно.


  • administrators

    @Denis_krsk Да, твой код работает. Видно я мало асинхронный код знаю. Сколько не пытался в await запихнуть, ни чего не выходило. Чтож, буду знать.



  • @Fox await работает только с промисом. То есть если бы метод ws.on возвращал промис. С калбеками он не работает.


  • administrators

    Если код на node.js работает не так как вы хотите, то есть большая вероятность, что проблемы с асинхронностью.
    Вот способ все исправить, даже не понимая, что случилось:

    1. Нажимаем на кнопку синхронизировать на панели справа. Появляется такой код
    await (new Promise((resolve, reject) => {
    	/*Place your code here and call resolve to proceed*/
    	resolve()
    }));
    
    1. Вместо комментария /*Place your code here and call resolve to proceed*/ помещаем свой код.
    2. Вызов resolve() переносим туда, где действие должно закончиться. Например, в примере с вебсокетами, это место, где нам приходит ответ от сервера.

    alt text

    Почему так?

    Очень часто примеры на ноде только устанавливают обработчики события. Например, когда кнопка будет нажата, отобразить окно с сообщением. Или когда придет сообщение от сервера, отобразить его в лог.
    Но сама установка события происходит мгновенно. Здесь речь идет именно об установке события, а не ожидании самого события. То есть, установить обработчик клика по кнопке можно мгновенно, а вот ждать этого клика можно сколько угодно. Синхронизация позволяет именно дождаться самого события.

    Почему пример в теме не всегда работает, и что с глобальными переменными?

    БАС говорит ноде сделать запрос к серверу, говорит, что нужно сделать, когда соединение откроется или придет сообщение от сервера или соединение закроется(выставляет обработчики). После этого, действие node.js сразу завершается. Но сам код в node.js работает, делает запрос к серверу и устанавливает глобальную переменную(global.tost). Следующий вызов действия node.js читает эту глобальную переменную и выводит ее в лог(console.log(global.tost);). Но тут проблема. К тому времени, как мы читаем глобальную переменную, ответ от сервера не обязательно получен. Его может быть, а может и не быть. Именно поэтому, я все таки рекомендую использовать решение с синхронизацией.

    А нельзя ли сделать все автоматом?

    Нет, так не получится. Главная проблема тут, куда поместить вызов resolve? Напомню, resolve нужно вызывать тогда, когда действие должно быть завершено(в нашем случае получено сообщение от сервера). Код может быть более сложным и определить место, где будет resolve автоматически будет очень сложно. Например, если нода должна не просто получить одно сообщение от сервера, а обменяться несколькими, или опрашивать сервер, пока не будет получен нужный результат.
    Есть вариант, когда действие завершается после того, как нет активных вызовов(например запросов к серверу), но я предпочитаю такой вариант, с явным заданием места, где действие завершается.

    Все равно сложно!

    Да, это самая неприятная часть в ноде. Но у нее есть и множество достоинств. Например, выигрыш производительности за счет отсутствия необходимости создания потоков для параллельных вычислений. Это касается в основном работы с сетью и записи, чтения файлов, но так как именно работе с сетью в БАС уделяется огромное внимание, то это большой плюс. Нет необходимости обеспечивать целостность данных при работе с одними и теми же участками памяти из нескольких потоков.

    Для тех, кто хочет изучить, как использовать ноду в БАС более детально есть статья(на английском) https://wiki.bablosoft.com/doku.php?id=node.js и видео на русском https://www.youtube.com/watch?v=YojWxGcGGEg

    Также в БАС будут добавлены и другие языки, как я и обещал.



  • @support said in websocket на node js:

    Также в БАС будут добавлены и другие языки, как я и обещал.

    Немного не по теме. Сейчас занимаюсь добавлением питона по примеру ноды, хотелось бы знать ведется ли сейчас работа в этом направлении? Не хочу чтоб труд и время пропали зря


  • administrators

    @artihorror В данный момент нет.
    Вы делаете это по аналогии с нодой или как-то по своему?
    Можно ли будет использовать ваши наработки в БАС?



  • @support said in websocket на node js:

    Вы делаете это по аналогии с нодой или как-то по своему?

    @support По аналогии с нодой. К сожалению в с++ не особо сильна, очень много времени уходит на понимание кода:с.

    @support said in websocket на node js:

    Можно ли будет использовать ваши наработки в БАС?

    Конечно