WebSockets в JavaScript: создаём real-time приложения с нуля

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

WebSockets в JavaScript: когда «живой» сайт — это не магия, а код

Вы когда-нибудь задумывались, как работают онлайн-чаты, биржевые котировки в реальном времени или многопользовательские браузерные игры? Секрет прост и элегантен — WebSockets в JavaScript. Эта технология превращает статичный веб-сайт в живой организм, который мгновенно реагирует на действия других пользователей.

В этой статье мы не просто разберём теорию. Мы напишем настоящий чат с нуля, научимся обрабатывать ошибки и поймём, почему real-time приложения на WebSockets работают быстрее, чем старый добрый AJAX с polling'ом. Пристегнитесь — будет интересно.

Почему HTTP не подходит для real-time? (Краткий ликбез)

Обычный HTTP работает по принципу «запрос-ответ». Клиент просит — сервер отвечает. Если сервер хочет что-то сказать сам (например, «пришло новое сообщение»), ему приходится ждать, пока клиент снова спросит. Это напоминает переписку почтовыми голубями — медленно и неэффективно.

Разработчики придумывали обходные пути:

  • Polling — клиент дёргает сервер каждые 5 секунд: «Ну что, есть новости?»
  • Long polling — сервер держит соединение открытым, пока не появится ответ (но это всё равно однонаправленный канал).

И тут на сцену выходят WebSockets. Представьте, что вы открыли телефонную линию между браузером и сервером. Оба могут говорить в любой момент, одновременно, без задержек на «поднятие трубки». Это и есть магия JavaScript WebSocket API.

Как работает WebSocket: протокол, рукопожатие, сообщения

Технически WebSocket — это отдельный протокол (ws:// или wss:// для шифрованного соединения). Всё начинается с HTTP-рукопожатия:

  1. Клиент отправляет обычный HTTP-запрос с заголовком Upgrade: websocket.
  2. Сервер отвечает: «101 Switching Protocols» — и соединение «апгрейдится» до WebSocket.
  3. Дальше — полный дуплекс: данные летят в обе стороны в виде фреймов (текст или бинарные данные).

В браузере для работы с WebSocket используется встроенный объект WebSocket. Никаких дополнительных библиотек не нужно — чистый JavaScript WebSocket работает из коробки во всех современных браузерах.

Пишем первый real-time чат на JavaScript

Давайте создадим простой чат. Серверную часть напишем на Node.js с библиотекой ws (самая популярная для WebSocket Node.js), клиент — на чистом JavaScript.

Шаг 1. Установка сервера

npm init -y
npm install ws

Шаг 2. Серверный код (server.js)

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws) {
    console.log('Новый пользователь подключился');

    // Отправляем приветствие новому клиенту
    ws.send(JSON.stringify({
        type: 'system',
        message: 'Добро пожаловать в чат!'
    }));

    // Получаем сообщения от клиента
    ws.on('message', function incoming(data) {
        const message = JSON.parse(data);
        console.log('Получено:', message);

        // Рассылаем сообщение всем подключенным клиентам
        wss.clients.forEach(function each(client) {
            if (client.readyState === WebSocket.OPEN) {
                client.send(JSON.stringify({
                    type: 'message',
                    user: message.user,
                    text: message.text,
                    time: new Date().toLocaleTimeString()
                }));
            }
        });
    });

    // Обрабатываем отключение
    ws.on('close', function() {
        console.log('Пользователь отключился');
    });
});

console.log('Сервер запущен на ws://localhost:8080');

Шаг 3. Клиентский код (index.html)

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Real-time Чат на WebSockets</title>
    <style>
        #chat { width: 500px; margin: 20px auto; font-family: Arial; }
        #messages { height: 300px; overflow-y: scroll; border: 1px solid #ccc; padding: 10px; }
        #input-area { display: flex; margin-top: 10px; }
        #message-input { flex: 1; padding: 10px; }
        #send-btn { padding: 10px 20px; background: #007bff; color: white; border: none; cursor: pointer; }
    </style>
</head>
<body>
<div id="chat">
    <h2>Real-time Чат</h2>
    <div id="messages"></div>
    <div id="input-area">
        <input type="text" id="message-input" placeholder="Введите сообщение..." />
        <button id="send-btn">Отправить</button>
    </div>
</div>

<script>
    const ws = new WebSocket('ws://localhost:8080');
    const messagesDiv = document.getElementById('messages');
    const input = document.getElementById('message-input');
    const sendBtn = document.getElementById('send-btn');

    // При подключении
    ws.onopen = function() {
        console.log('Соединение установлено');
        addMessage('system', 'Вы подключились к чату');
    };

    // При получении сообщения
    ws.onmessage = function(event) {
        const data = JSON.parse(event.data);
        addMessage(data.type, data.message || data.text, data.user, data.time);
    };

    // При ошибке
    ws.onerror = function(error) {
        console.error('Ошибка WebSocket:', error);
        addMessage('system', '⚠️ Ошибка соединения');
    };

    // При закрытии
    ws.onclose = function() {
        console.log('Соединение закрыто');
        addMessage('system', '❌ Соединение потеряно');
    };

    // Отправка сообщения
    sendBtn.addEventListener('click', function() {
        const text = input.value.trim();
        if (text) {
            ws.send(JSON.stringify({
                user: 'Пользователь',
                text: text
            }));
            input.value = '';
        }
    });

    // Отправка по Enter
    input.addEventListener('keypress', function(e) {
        if (e.key === 'Enter') sendBtn.click();
    });

    function addMessage(type, content, user, time) {
        const div = document.createElement('div');
        if (type === 'system') {
            div.style.color = '#888';
            div.style.fontStyle = 'italic';
            div.textContent = content;
        } else {
            div.innerHTML = `<strong>${user}</strong> [${time}]: ${content}`;
        }
        messagesDiv.appendChild(div);
        messagesDiv.scrollTop = messagesDiv.scrollHeight;
    }
</script>
</body>
</html>

Продвинутые техники: бинарные данные и reconnect

Отправка бинарных данных через WebSocket

WebSocket умеет передавать не только текст, но и бинарные данные

Блоги

Рекомендаций по книгам