Пишем свой бинарный протокол передачи данных на NodeJS
Пишем свой бинарный протокол передачи данных на NodeJS

Введение

Разработка эффективных и безопасных приложений требует не только хорошо продуманного API, но и правильного выбора протокола передачи данных. Веб-приложения обычно используют текстовые форматы, такие как JSON или XML, но для высокопроизводительных систем, требующих минимальной задержки и небольшого объема передачи данных, может быть выгодно использовать бинарные протоколы.

В этой статье мы рассмотрим, как разработать собственный бинарный протокол для приложений на основе Node.js и WebSockets, добавить авторизацию с помощью JWT и изучим преимущества бинарного протокола по сравнению с другими форматами данных.

Бинарный протокол сообщений
Бинарный протокол сообщений

Почему именно бинарный протокол?

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

  • Эффективность: Бинарные протоколы более компактны, чем текстовые форматы (например, JSON). Они позволяют передавать данные в более сжатом виде, что уменьшает объем передаваемого трафика.

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

  • Безопасность: Двоичные данные сложнее анализировать в режиме реального времени по сравнению с текстовыми данными, что делает двоичные протоколы менее уязвимыми для атак.

  • Гибкость: В бинарных протоколах форматы данных можно более точно контролировать для работы с конкретными типами данных (например, числами с плавающей запятой, строками, массивами байтов и т. д.).

Архитектура системы:

Мы разработаем систему, состоящую из следующих компонентов:

  • Сервер на Node.js, использующий WebSockets для связи с клиентами.

  • JavaScript -клиент, который подключается к серверу и использует бинарный протокол для передачи данных.

  • Авторизация с использованием JWT (JSON Web Token) для безопасного подключения клиентов к серверу.

Серверная реализация на Node.js

Установка зависимостей

Для начала установим необходимые зависимости:

npm init -y
npm install ws jsonwebtoken

Примечание: JWT можно также заменить на авторизацию внутри бинарного протокола, однако здесь будем использовать его для простоты понимания

ws — это библиотека для работы с WebSocket на стороне сервера, а jsonwebtoken — для работы с JWT.

Простой серверный код:

const WebSocket = require('ws');
const jwt = require('jsonwebtoken');

// Наш секретный ключ для JWT
const SECRET_KEY = 'your_secret_key';

// Создаем сервер
const wss = new WebSocket.Server({ port: 8080 });

// Верификация токена
function verifyJWT(token) {
    try {
        return jwt.verify(token, SECRET_KEY);
    } catch (e) {
        return null;
    }
}

// Подключение к сокету
wss.on('connection', (ws, req) => {
    // Получение токена из Header
    const token = req.url.split('token=')[1];
    const user = verifyJWT(token);

    // Пользователь не авторизован
    if (!user) {
        ws.close(4001, 'Unauthorized');
        return;
    }

    console.log(`User ${user.username} connected`);

    ws.on('message', (message) => {
        if (message instanceof Buffer) {
            // Рабоа с бинарными сообщениями
            const messageType = message.readUInt8(0); // Первый байт - тип сообщения

            if (messageType === 1) {
                const textLength = message.readUInt16BE(1);
                const text = message.toString('utf-8', 3, 3 + textLength);
                console.log(`Received message from ${user.username}: ${text}`);
            } else if(messageType === 2) {
                // Другие типы сообщений
            }
        }
    });

    ws.on('close', () => {
        console.log(`User ${user.username} disconnected`);
    });
});

console.log('WebSocket server started on ws://localhost:8080');

Пояснение к коду:

  • Авторизация с помощью JWT: Сервер проверяет токен JWT, переданный клиентом при подключении. Если токен недействителен, сервер закрывает соединение с ошибкой авторизации.

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

  • Сервер WebSocket: библиотека ws используется для управления соединениями и сообщениями.

Внедрение клиентских решений

Клиентский код

Для реализации клиентской части мы используем чистый JavaScript.

// Создаем подключение к серверу
const socket = new WebSocket('ws://localhost:8080?token=your_jwt_token');

// Подключение установлено
socket.addEventListener('open', () => {
    console.log('Connected to server');

    // Пример бинарного сообщения
    const message = "Hello, Binary World!";
    const buffer = new ArrayBuffer(3 + message.length);
    const view = new DataView(buffer);

    view.setUint8(0, 1); // Устанавливаем тип сообщения
    view.setUint16(1, message.length); // Устанавливаем длинну
    for (let i = 0; i < message.length; i++) {
        view.setUint8(3 + i, message.charCodeAt(i));
    }

    socket.send(buffer);
});

// Получание сообщение от сервера
socket.addEventListener('message', (event) => {
    if (event.data instanceof Blob) {
        event.data.arrayBuffer().then(buffer => {
            const view = new DataView(buffer);
            const messageType = view.getUint8(0);

            if (messageType === 1) { // Тип сообщения
                const textLength = view.getUint16(1);
                const text = String.fromCharCode(...new Uint8Array(buffer.slice(3, 3 + textLength)));
                console.log(`Received message: ${text}`);
            }
        });
    }
});

// Закрытие соединения
socket.addEventListener('close', () => {
    console.log('Disconnected from server');
});

Пояснение к коду:

  • Подключение к серверу: Клиент подключается к WebSocket серверу, передавая JWT-токен через строку запроса.

  • Отправка двоичных данных: Для отправки двоичных данных создается объект ArrayBuffer, в который записываются тип сообщения и текстовые данные.

  • Получение сообщений: Клиент ожидает от сервера двоичные данные и анализирует их, используя DataView для чтения байтов.

Создание и проверка JWT-токенов

Пример создания JWT-токена на стороне сервера:

const jwt = require('jsonwebtoken');
const SECRET_KEY = 'your_secret_key';
const token = jwt.sign({ username: 'user1' }, SECRET_KEY, { expiresIn: '1h' });

console.log(token);

Этот токен можно использовать для подключения клиента.

Заключение

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

Примеры готовых библиотек:

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

И, конечно же, спасибо за прочтение.

Комментарии (4)


  1. savostin
    13.12.2025 20:35

    Да где ж он, этот бинарный протокол?


    1. poznohub Автор
      13.12.2025 20:35

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


  1. goldexer
    13.12.2025 20:35

    Итак, заголовок статьи начинается с «бинарные протоколы...», а в итоге в статье ни их названий, ни разновидностей, ни хитростей, тонкостей и секретов, ни даже приложения, которое бы реализовало пару видов реальных бинарных протоколов. Зато АИшный выхлоп с кодом уровня «пример для детей из учебника по JS» вполне себе присутствует. Такое надо минусовать, но наверно сейчас набегут «вау спасибо как удивительно и познавательно теперь мы всё знаем о бинарных протоколах» и люто заплюсуют. Хоть одной строчкой бы пару реальных названий - увы...