Ультрабыстрые приложения на Node.js и Socket.io

Библиотека Socket.io может реализовать синхронизированную коммуникацию внутри приложения. То есть, в режиме реального времени. Проще говоря, socket.io поможет создать живой чат на веб-сайте. Или, к примеру, браузерную игру, за развитием сюжета которой посетитель сможет следить без перезагрузки веб-страницы!

Что делает socket.io?

socket.io основывается на нескольких технологиях, обеспечивающих коммуникацию в режиме реального времени. Наиболее известная из них – это WebSocket.

Этот API JavaScript поддерживается всеми современными браузерами. Он обеспечивает синхронизированный двунаправленный обмен данными между покупателем и сервером.

Давайте ещё раз вернёмся к основам. Большая часть коммуникационных каналов интернета не синхронизированы. Он был таким: покупатель запрашивает, а сервер отвечает.

Ультрабыстрые приложения на Node.js и Socket.io

Коммуникации обычно не синхронизированы: покупатель запрашивает, а сервер отвечает

Это было приемлемо на заре всемирной сети, но теперь становится большим ограничением. Покупатель должен перезагрузить страницу или самостоятельно инициировать запрос к серверу. Так как сервер не может общаться с покупателем по воле.

WebSocket – это технология, которая создаёт некое подобие «канала» коммуникации между покупателем и сервером, который остаётся постоянно открытым. Браузер и сервер остаются соединёнными друг с ином и могут обмениваться сообщениями в любом направлении.

Ультрабыстрые приложения на Node.js и Socket.io

С WebSocket канал связи между покупателем и сервером остаётся открытым.

Не путайте WebSocket и AJAX!

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

Socket.io может просто использовать WebSocket. А так как не все браузеры могут создавать WebSocket, данная библиотека может использовать иные механизмы синхронизации, которые поддерживаются браузером.

Загляните в раздел поддерживаемых браузеров на официальном веб-сайте socket.io. Здесь можно легко увидеть, что библиотека определяет, какая технология подходит покупателю:

  • WebSocket;
  • Adobe Flash Socket;
  • Длинные опросы(long pooling) AJAX;
  • AJAX multipart streaming;
  • Iframe;
  • Опросы JSONP.

К примеру, если браузер не поддерживает WebSocket, но в нем установлен Flash, то socket.io будет использовать последнюю технологию для взаимосвязи в реальном времени. Если нет, библиотека применит иные техники, такие как длинные опросы AJAX.

Покупатель постоянно опрашивает сервер, есть ли обновления для него. Это не самый эффективный способ, но он работает. Также может применяться невидимый iframe, который постоянно перезагружается, чтобы приобрести обновления с сервера.

Благодаря всем данным технологиям библиотека socket.io поддерживает большое число браузеров, в том числе и устаревших:

  • Internet Explorer 5.5+;
  • Safari 3+;
  • Google Chrome 4+;
  • Firefox 3+;
  • Opera 10.61+;
  • Safari для iPhone и iPad;
  • Стандартный браузер Android.

Отправка и получение сообщений при помощи socket.io

Перейдём к делу: как использовать библиотеку socket.io?

Установка socket.io

При первом использовании библиотеки я потратил 15 минут, чтобы установить ее. Для этого необходимо выполнить эту команду:

npm install socket.io

Первый код: покупатель входит в систему

При работе с библиотекой socket.io используется одновременно два файла:

  • Серверный файл(к примеру, app.js): он управляет соединениями веб-сайта и клиентских приложений(браузеров).
  • Клиентский файл(например, index.html): он соединяет покупателя с сервером и выводит результаты в браузере.

Сервер(app.js)

Вначале мы загружаем на сервер содержимое страницы index.html и отображаем ее покупателю, После этого грузим библиотеку socket.io и управляем ее событиями.

var http = require('http');var fs = require('fs');// Загружаем файл index.html и отображаем его клиентуvar server = http.createServer(function(req, res) { fs.readFile('./index.html', 'utf-8', function(error, content) { res.writeHead(200, {"Content-Type": "text/html"}); res.end(content); });});// Загружаем socket.iovar io = require('socket.io').listen(server);// Когда покупатель соединяется, выводим сообщение в консольio.sockets.on('connection', function(socket) { console.log('A client is connected!');});server.listen(8080);

Что делает приведенный выше код:

  • Отправляет файл index.html, когда покупатель просит настроить страницу в браузере.
  • Готовится приобретать запросы через socket.io. Когда мы устанавливаем соединение при помощи библиотеки, то выводим сообщение об этом в консоль.

Допустим, что посетитель открывает в браузере веб-страницу, на которой находится приложение(в данном случае http://localhost:8080). Мы отправляем ему файл index.html, страница загружается. Код JavaScript этого файла соединяется с сервером. Но на этот раз не через http, а через socket.io(WebSocket). Покупатель поддерживает два типа соединений:

  • «Классическое» HTTP-соединение с сервером, которое используется для загрузки веб-страницы index.html.
  • Соединение, происходящее «в реальном времени» для открытия канала через WebSocket при помощи socket.io.

Покупатель(index.html)

Файл index.html отправляется сервером Node.js. Это классический HTML-файл, который включает JavaScript-код для соединения с сервером в режиме реального времени.

<!DOCTYPE html><html>  <head>        <meta charset="utf-8" />        <title>Socket.io</title>    </head>     <body>        <h1>Communication with socket.io!</h1>        <script src="https://www.internet-technologies.ru/wp-content/uploads/socket.io/socket.io.js"></script>        <script>            var socket = io.connect('http://localhost:8080');        </script>    </body></html>

Я специально расположил JavaScript- программный код в конце HTML-кода, чтобы избежать задержки загрузки страницы из-за JavaScript.

В первом блоке кода мы получаем файл socket.io.js для покупателя. Он автоматически предоставляется сервером Node.js через модуль socket.io(так что путь к файлу выбран не случайно):

<script src="https://www.internet-technologies.ru/wp-content/uploads/socket.io/socket.io.js"></script>

Данный скрипт может управлять соединением с сервером на стороне покупателя, через WebSocket. А также при помощи одного из способов, если WebSocket не поддерживается браузером.

Далее выполним действие на стороне покупателя для установки соединения с сервером. Пока я сделал самое простое: соединился с сервером. Он располагается на моём компьютере, что объясняет адрес http://localhost:8080.

var socket = io.connect('http://localhost:8080');

Тестируем программный код!

Запустите приложение:

node app.js

Далее в браузере откройте страницу с Node.js: http://localhost:8080

После этого загрузится основная страница. Далее компьютер откроет соединение с socket.io, а сервер выведет отладочную информацию в консоли:

$ node app.js info  - socket.io started debug - client authorized info  - handshake authorized Z2E7aqIvOPPqv_XBn421 debug - setting request GET /socket.io/1/websocket/Z2E7aqIvOPPqv_XBn421 debug - set heartbeat interval for client Z2E7aqIvOPPqv_XBn421 debug - client authorized for debug - websocket writing 1::A client is connected!

Хорошо! Значит, код работает.

Отправка и получение сообщений

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

  • Сервер хочет отправить сообщение покупателю.
  • Покупатель хочет отправить сообщение серверу.

Сервер хочет отправить сообщение покупателю.

Я предлагаю рассмотреть вариант, когда сервер отправляет сообщение покупателю о том, что соединение установлено. Добавьте этот программный код в файл app.js:

io.sockets.on('connection', function(socket) {        socket.emit('message', 'You are connected!');});

Когда соединение установлено, покупателю отправляется сообщение при помощи socket.emit(). Эта функцию принимает два параметры:

  • Тип сообщения, которое необходимо передать. Это сможет различать типы сообщений. К примеру, в игре можно без проблем отправлять типы сообщений «move_player» или «attack_player».
  • Содержимое сообщения. Здесь можно легко указать всё, что планируете.

Если необходимо передать разные типы данных в сообщении, группируйте их в объекты, как показано в следующем примере:

socket.emit('message', { content: 'You are connected!', importance: '1' });

В файле index.html(на стороне покупателя) мы будем отслеживать входящие сообщения:

<script>    var socket = io.connect('http://localhost:8080');    socket.on('message', function(message) {        alert('The server has a message for you: ' + message);    })</script>

При помощи socket.on() перехватываются сообщения типа message. Когда они приходят, вызывается функцию, которая выводит простое диалоговое окно.

Попробуйте. Вы увидите, что при загрузке index.html выводится диалоговое окно, сообщающее, что соединение успешно установлено.

Ультрабыстрые приложения на Node.js и Socket.io

Покупатель выводит сообщение от сервера в диалоговом окне

Покупатель хочет отправить сообщение серверу.

На стороне покупателя(в файле index.html) я добавлю кнопку «Poke the server». Когда мы нажмём на неё, серверу будет отправлено сообщение. Вот полный программный код:

<!DOCTYPE html><html>    <head>        <meta charset="utf-8" />        <title>Socket.io</title>    </head>     <body>        <h1>Communicating with socket.io!</h1>        <p><input type="button" value="Poke the server" id="poke" /></p>        <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>        <script src="https://www.internet-technologies.ru/wp-content/uploads/socket.io/socket.io.js"></script>        <script>            var socket = io.connect('http://localhost:8080');            socket.on('message', function(message) {                alert('The server has a message for you: ' + message);            })            $('#poke').click(function() {                socket.emit('message', 'Hi server, how are you?');            })        </script>    </body></html>

Чтобы перехватить событие нажатия на кнопку, я использую jQuery. Вы можете легко использовать JavaScript.

Добавьте в программный код следующий блок:

$('#poke').click(function() {    socket.emit('message', 'Hi server, how are you?');})

Это простой код. При нажатии на кнопку на сервер отправляет сообщение типа message(вместе с контентом). Если необходимо приобрести его на стороне сервера, придётся реализовать отслеживание сообщений типа message:

io.sockets.on('connection', function(socket) {    socket.emit('message', 'You are connected!');    // Когда сервер получает сообщение типа “message” от покупателя socket.on('message', function(message) {        console.log('A client is speaking to me! They’re saying: ' + message);    }); });

Запустите программный код. Нажмите на кнопку, размещенную на веб-странице, и понаблюдайте за консолью сервера. Вы должны увидеть следующий текст.

A client is talking to me! They’re saying: Hi server, how are you?
Ультрабыстрые приложения на Node.js и Socket.io

Когда покупатель нажимает на кнопку, сервер мгновенно реагирует в консоли

Коммуникация с несколькими покупателями

В предыдущих примерах мы работали с одним сервером и одним покупателем. На практике у вас будет сразу же пару покупателей, соединённых с приложением Node.js. Чтобы воссоздать эту ситуацию локально, откройте две вкладки в браузере. На каждой из них перейдите по адресу http://localhost:8080. В результате сервер увидит соединения двух разных покупателей.

При работе с несколькими клиентскими соединениями необходимо иметь функция:

  • Отправлять сообщения всем покупателям одновременно. Мы называем это рассылкой.
  • Запоминать информацию о каждом покупателе(например, имя посетителя). Для этого нам потребуются переменные сессии.

Это как раз то, что о чем я расскажу дальше.

Отправка сообщения всем покупателям(рассылка)

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

Рассмотрим следующий сценарий:

  1. Покупатель A посылает сообщение серверу.
  2. Сервер анализирует его.
  3. Сервер решает сделать рассылку этого сообщения и отправляет его иным покупателям B и C.
Ультрабыстрые приложения на Node.js и Socket.io

В рассылке сервер отправляет сообщение всем иным соединённым покупателям

К примеру, покупатель A пишет сообщение в чате и отправляет его на сервер. А чтобы его увидели иные покупатели, оно должно быть разослано.

socket.broadcast.emit('message', 'Message to all units. I repeat, message to all units.');

Для этого необходимо вызвать socket.broadcast.emit(), и сообщение будет отправлено иным посетителям. Добавьте приведенный ниже код рассылки в файл app.js:

io.sockets.on('connection', function(socket) {    socket.emit('message', 'You are connected!');    socket.broadcast.emit('message', 'Another client has just connected!');    socket.on('message', function(message) {        console.log('A client is speaking to me! They're saying: ' + message);    }); });

Сейчас попробуйте открыть в браузере пару веб-страниц по адресу http://localhost:8080. Вы увидите, что, когда к серверу подключается новый покупатель, иные страницы реагируют на это сообщением: «Another client has just connected!»

Переменные сессии

При многопользовательском подключении трудно идентифицировать каждого покупателя. Идеальным вариантом была бы использовать переменные сессии. Но они недоступны в socket.io.

Хотя переменными сессии можно управлять из другой библиотеки через промежуточный слой session.socket.io.

Чтобы показать вам, как использовать промежуточный слой управления сессиями, пришлось бы написать целую главу книги. Но я предложу упрощённую технику.

Нам необходимо, чтобы сервер запоминал информацию о каждом соединённом покупателе. Благодаря данному покупателю не придётся при каждой отправке сообщения напоминать нам кто он!

Чтобы добавить переменную сессии на стороне сервера, необходим следующий код:

socket.myvariable = myvariable;

В этом примере мы храним данные в переменной объекта socket. Чтобы приобрести эту информацию, необходимо просмотреть содержимое socket.myvariable:

console.log(socket.myvariable);

Сейчас попытаемся представить реальный случай использования. Когда покупатель соединяется с сервером, веб-страница запрашивает его имя. Сервер сохраняет логин посетителя в переменной сессии, чтобы использовать его, когда покупатель нажмёт на кнопку «Poke the server».

Давайте посмотрим, какие изменения нам необходимо внести.

Веб-страница(index.html) отправляет сообщение, содержащее имя посетителя

Когда веб-страница загружается, запрашивается логин посетителя. Мы отправляем его на сервер при помощи сообщения типа «little_newbie». Это сообщение включает имя пользователя:

var username = prompt('What's your username?');socket.emit('little_newbie', username);

Сервер(app.js) хранит имя посетителя

Сервер должен приобрести это сообщение. Мы отслеживаем сообщения типа «little_newbie» и, когда его получаем, то сохраняем имя посетителя в переменной сессии:

socket.on('little_newbie', function(username) {    socket.username = username;});

Сервер(app.js) помнит имя посетителя, когда мы отправляем ему сообщение

Чтобы сервер помнил посетителя, когда он нажимает на кнопку, мы дополним возможность обратного вызова. Она вызывается при получении сервером сообщения типа «message»:

socket.on('message', function(message) {    console.log(socket.username + ' is speaking to me! They're saying: ' + message);});

Как только сервер получает сообщение, мы запрашиваем переменную сессии с именем посетителя из клиентского объекта socket.

Тест кода

Попробуйте открыть два окна браузера и ввести различные имена посетителей. Далее нажмите на кнопку “Poke the server”. В консоли сервера вы увидите имя посетителя, который кликнул по кнопке.

Ультрабыстрые приложения на Node.js и Socket.io

Пару посетителей подключены: сервер помнит их имена!

Полный программный код

Полный программный код файла index.html:

<!DOCTYPE html><html>    <head>        <meta charset="utf-8" />        <title>Socket.io</title>    </head>     <body>        <h1>Communicating with socket.io!</h1>        <p><input type="button" value="Poke the server" id="poke" /></p>        <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>        <script src="https://www.internet-technologies.ru/wp-content/uploads/socket.io/socket.io.js"></script>        <script>            var socket = io.connect('http://localhost:8080');            // Пользователя просят ввести имя посетителя...            var username = prompt('What's your username?');                        // Оно отправляется в сообщении типа "little_newbie" (чтобы отличать его от сообщений типа "message")            socket.emit('little_newbie', username);            // Диалоговое окно выводится, когда сервер отправляет нам сообщение типа "message"            socket.on('message', function(message) {                alert('The server has a message for you: ' + message);            })            // Когда нажимается кнопка, сообщение типа "message" отправляется на сервер            $('#poke').click(function () {                socket.emit('message', 'Hi server, how are you?');            })        </script>    </body></html>

… а вот код серверного файла app.js:

var http = require('http');var fs = require('fs');// Загружаем файл index.html для выведения клиентуvar server = http.createServer(function(req, res) {    fs.readFile('./index.html', 'utf-8', function(error, content) {        res.writeHead(200, {"Content-Type": "text/html"});        res.end(content);    });});// Загрузка socket.iovar io = require('socket.io').listen(server);io.sockets.on('connection', function (socket, username) {    // Когда покупатель подключается, им отправляется сообщение socket.emit('message', 'You are connected!');    // Иным покупателям сообщается, что пришёл кто-то новый socket.broadcast.emit('message', 'Another client has just connected!');    // Как только получено имя посетителя, оно сохраняется в переменной socket.on('little_newbie', function(username) {        socket.username = username;    });    // Когда сообщение типа "message" получено (нажатие на кнопку), оно записывается в консоль socket.on('message', function (message) {        // Имя посетителя пользователя, который нажал на кнопку, извлекается из переменной сессии console.log(socket.username + ' is speaking to me! They're saying: ' + message);    }); });server.listen(8080);

Помните, что это простое приложение, которое может протестировать функционал библиотеки socket.io.

Резюме

  • io – это модуль Node.js, который может пользователям постоянно (в режиме реального времени) общаться с сервером.
  • Библиотека Socket.io основана на технологии WebSocket.
  • Разработанное нами приложение может отправлять запросы на сервер в любое время без перезагрузки страницы. При этом пользователи смогут приобретать сообщения отправляемые сервером в любой момент!
  • Сервер и покупатель отправляют друг другу сообщения при помощи emit() и отслеживают их при помощи socket.on().

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *