Рубрики
Без рубрики

Примеры Node.js Websocket с помощью Socket.io

За последние несколько лет в Интернете и в мобильных приложениях начал появляться новый тип коммуникации, называемый websockets. Этот новый протокол открывает гораздо более быструю и эффективную линию связи с клиентом.

Что такое вебсокеты?

За последние несколько лет в Интернете и в мобильных приложениях начал появляться новый тип коммуникации, называемый websockets. Этот протокол был долгожданным и, наконец, был стандартизирован IETF в 2011 году, открыв путь к широкому использованию.

Этот новый протокол открывает гораздо более быструю и эффективную линию связи с клиентом. Как и HTTP, веб-сокеты работают поверх TCP-соединения, но они намного быстрее, потому что нам не нужно открывать новое соединение каждый раз, когда мы хотим отправить сообщение, поскольку соединение поддерживается столько, сколько хочет сервер или клиент.

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

Как работают Websockets?

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

Чтобы установить это соединение, протокол фактически инициирует рукопожатие как обычный HTTP-запрос, но затем “модернизируется” с помощью HTTP-заголовка upgrade request, как показано ниже:

GET /ws/chat HTTP/1.1
Host: chat.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: q1PZLMeDL4EwLkw4GGhADm==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 15
Origin: http://example.com

Затем сервер отправляет ответ HTTP 101 “Switching Protocols”, подтверждая, что соединение будет обновлено. Как только соединение установлено, он переключается на двунаправленный двоичный протокол, и тогда можно отправлять данные приложения.

Все, что нужно сделать протоколу, чтобы сохранить соединение открытым, это послать несколько пакетов ping/pong, которые сообщают другой стороне, что они все еще там. Чтобы закрыть соединение, посылается простой пакет “закрыть соединение”.

Некоторые примеры Websocket

Из множества различных библиотек websocket для Node.js, доступных нам, я решил использовать socket.io в этой статье, потому что она кажется самой популярной и, на мой взгляд, самой простой в использовании. Хотя каждая библиотека имеет свой собственный уникальный API, у них также много общего, поскольку все они построены на основе одного и того же протокола, поэтому, надеюсь, вы сможете перевести приведенный ниже код на любую библиотеку, которую захотите использовать.

Для HTTP-сервера я буду использовать Express, который является самым популярным сервером Node. Имейте в виду, что вы также можете просто использовать обычный модуль http, если вам не нужны все возможности Express. Хотя, поскольку большинство приложений будут использовать Express, мы также будем использовать именно его.

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

Установление соединения

Для того чтобы установить соединение между клиентом и сервером, сервер должен сделать две вещи:

  1. Подключиться к HTTP-серверу для обработки websocket-соединений.
  2. Предоставить клиентскую библиотеку socket.io.js в качестве статического ресурса.


В приведенном ниже коде пункт (1) выполняется на 3-й строке. Пункт (2) выполняется за вас (по умолчанию) библиотекой socket.io и передается по пути /socket.io/socket.io.js. По умолчанию все вебсокетные соединения и ресурсы обслуживаются по пути /socket.io.

Сервер

var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);

app.get('/', function(req, res) {
    res.sendFile(__dirname + '/index.html');
});

server.listen(8080);

Клиенту также необходимо сделать две вещи:

  1. Загрузить библиотеку с сервера
  2. Вызвать .connect() для адреса сервера и пути к websocket.

Клиент

<script src="/socket.io/socket.io.js"></script>
<script>
    var socket = io.connect('/');
</script>

Если вы перейдете в браузере по адресу http://localhost:8080 и просмотрите HTTP-запросы за кулисами с помощью инструментов разработчика браузера, вы сможете увидеть, как выполняется рукопожатие, включая GET-запросы и результирующий ответ HTTP 101 Switching Protocols.

Отправка данных с сервера на клиент

Итак, теперь перейдем к более интересным частям. В этом примере мы покажем вам наиболее распространенный способ отправки данных с сервера на клиент. В этом случае мы отправим сообщение в канал, на который может подписаться и получать его клиент. Так, например, клиентское приложение может прослушивать канал ‘announcements’, который будет содержать уведомления об общесистемных событиях, например, когда пользователь присоединяется к чату.

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

Сервер

io.on('connection', function(socket) {
    socket.emit('announcements', { message: 'A new user has joined!' });
});

Клиент

<script src="/socket.io/socket.io.js"></script>
<script>
    var socket = io.connect('/');
    socket.on('announcements', function(data) {
        console.log('Got announcement:', data.message);
    });
</script>

Отправка данных с клиента на сервер

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

Сервер

io.on('connection', function(socket) {
    socket.on('event', function(data) {
        console.log('A client sent us this dumb message:', data.message);
    });
});

Клиент

<script src="/socket.io/socket.io.js"></script>
<script>
    var socket = io.connect('/');
    socket.emit('event', { message: 'Hey, I have an important message!' });
</script>

Подсчет подключенных пользователей

Это хороший пример для изучения, поскольку он демонстрирует еще несколько возможностей socket.io (например, событие разъединения), его легко реализовать, и он применим ко многим веб-приложениям. Мы будем использовать события соединения и разъединения для подсчета количества активных пользователей на нашем сайте и будем обновлять текущее количество пользователей.

Сервер

var numClients = 0;

io.on('connection', function(socket) {
    numClients++;
    io.emit('stats', { numClients: numClients });

    console.log('Connected clients:', numClients);

    socket.on('disconnect', function() {
        numClients--;
        io.emit('stats', { numClients: numClients });

        console.log('Connected clients:', numClients);
    });
});

Клиент

<script src="/socket.io/socket.io.js"></script>
<script>
    var socket = io.connect('/');
    socket.on('stats', function(data) {
        console.log('Connected clients:', data.numClients);
    });
</script>

Гораздо более простым способом отслеживания количества пользователей на сервере было бы просто использовать это:

var numClients = io.sockets.clients().length;

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

Комнаты и пространства имен

Вероятно, по мере усложнения приложения вам потребуется более широкая настройка websockets, например, отправка сообщений определенному пользователю или набору пользователей. Или, возможно, вам потребуется строгое разделение логики между различными частями вашего приложения. Здесь на помощь приходят комнаты и пространства имен.

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

По умолчанию socket.io использует корневое пространство имен (/) для отправки и получения данных. Программно вы можете получить доступ к этому пространству имен через io.sockets, хотя многие из его методов имеют ярлыки на io. Таким образом, эти два вызова эквивалентны:

io.sockets.emit('stats', { data: 'some data' });
io.emit('stats', { data: 'some data' });

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

var iosa = io.of('/stackabuse');
iosa.on('connection', function(socket){
    console.log('Connected to Stack Abuse namespace'):
});
iosa.emit('stats', { data: 'some data' });

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

<script src="/socket.io/socket.io.js"></script>
<script>
    var socket = io('/stackabuse');
</script>

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

Если пойти еще дальше, то внутри каждого пространства имен можно присоединяться к “комнатам” и покидать их. Эти комнаты обеспечивают еще один уровень разделения поверх пространств имен, а поскольку клиент может быть добавлен в комнату только на стороне сервера, они также обеспечивают дополнительную безопасность. Поэтому если вы хотите убедиться, что пользователи не подглядывают за определенными данными, вы можете использовать комнату, чтобы скрыть их.

Чтобы быть добавленным в комнату, вы должны вызвать для нее метод .join():

io.on('connection', function(socket){
    socket.join('private-message-room');
});

Затем оттуда можно отправлять сообщения всем, кто принадлежит к данной комнате:

io.to('private-message-room').emit('some event');

И, наконец, вызовите .leave(), чтобы прекратить получать сообщения о событиях из комнаты:

socket.leave('private-message-room');

Заключение

Это всего лишь одна библиотека, реализующая протокол websockets, существует множество других, каждая из которых имеет свои уникальные особенности и достоинства. Я бы посоветовал попробовать некоторые другие (например, node-websockets), чтобы получить представление о том, что есть на свете.

Всего в нескольких строках можно создать довольно мощные приложения, поэтому мне интересно посмотреть, что вы сможете придумать!

Оригинал: “https://stackabuse.com/node-js-websocket-examples-with-socket-io/”