Услуги Портфолио Контакты БлогНовости +7 (800) 511‑32‑27 Обсудить проект
Новости

WebSocket: особенности и использование на React

27 октября 2022·5 мин чтения
Логотип YuSMP Group
Автор материалаРедакция компании YuSMP GroupБлог YuSMP Group
Профиль редакции
WebSocket: особенности и использование на React

Веб-сокеты — современная технология, которая используется  для создания современных веб-приложений реального времени. В блоге студии web-разработки YuSMP Group рассказали подробнее о том, что такое WebSockets и как их используют на React.

Коротко. WebSocket — протокол постоянного двустороннего соединения поверх TCP: сервер и клиент обмениваются сообщениями без повторных HTTP-запросов и задержек polling. В статье — чем он отличается от HTTP и long polling, когда действительно нужен (чаты, трейдинг, многопользовательские игры, совместное редактирование), готовый хук useWebSocket на функциональных компонентах React с переподключением, разбор частых ошибок и когда стоит взять Socket.IO вместо нативного API. Если нужна frontend-разработка real-time-функциональности под ключ — обращайтесь к команде YuSMP Group.

Что представляет из себя веб-сокет

REST был бесспорным чемпионом веб-API. Он сверг старый SOAP и его многословный XML в пользу JSON поверх HTTP. REST также имел довольно сложную концептуальную модель, основанную на ресурсах и глаголах.

Но REST не идеально подходил для решения всех проблем, связанных с веб-коммуникациями. Разработчики из кожи вон лезли и пытались вписать все требования к связи и обмену данными в шаблон RESTful.

В настоящее время современные группы разработчиков программного обеспечения, как правило, развивают взаимодействие своих веб-служб с помощью WebSockets, GraphQL, gRPC и HTTP/2 по традиционному шаблону RESTful.

WebSockets protocol — это протоколы связи на основе соединений, которые поддерживают полнодуплексную связь в реальном времени.

Каждый современный веб-браузер поддерживает websockets java, и каждый популярный язык бэкенда имеет серверные библиотеки websocket wss либо в виде сторонних модулей, либо в виде стандартных библиотечных модулей. 

Давайте посмотрим на следующую схему и поймем, как формируется полнодуплексный канал связи websocket client-server:

Как показано на приведенной выше диаграмме, клиент сначала пытается инициализировать соединение WebSocket с сервером, отправляя HTTP-запрос.

Затем сервер завершает рукопожатие WebSocket, отвечая HTTP-ответом для инициализации соединения WebSocket. Как только соединение установлено, и клиент, и сервер могут отправлять друг другу сообщения асинхронно с сообщениями протокола WebSocket.

Разработка мобильных приложений на заказ
Узнайте цену

Чем WebSocket отличается от HTTP и long polling

До появления веб-сокетов вы могли выполнять операции HTTP-запрос-ответ только в веб-браузере.

Веб-сокеты основаны на сообщениях. WebSockets реализованы поверх TCP, но необработанный TCP основан на потоках. Вы отправляете множество байтов, а другая сторона должна выяснить, как их накопить и разбить на связные сообщения. Websocket swift делает это за вас, что очень помогает разработчикам.

HTTP/HTTPS - в чем разница между этими протоколами

Во-первых, адрес сайта будет выглядеть иначе: для HTTP — http://, а для HTTPS, соответственно, — https://. Еще HTTPS - это защищенное соединение: для него нужен подписанный SSL-сертификат, соединение требует проверки домена, а также протокол шифрует данные. Для HTTP ничего из этого не нужно, он передает данные через порт 80, в то время как HTTPS использует порт 443.

КритерийHTTP-pollingLong pollingWebSocket
Тип соединенияНовое TCP/HTTP-соединение на каждый запросСоединение держится до ответа сервераОдно постоянное соединение на всю сессию
Задержка обновленийЗависит от интервала опроса (секунды)Ниже, но с накладными расходами на переподключениеМиллисекунды, сервер сам инициирует push
Нагрузка на серверРастёт линейно с числом клиентов × частотой опросаДержит открытые запросы, расходует потоки/сокетыОдин сокет на клиента, минимум служебного трафика
Направление данныхТолько клиент → сервер → клиент (запрос-ответ)То же, с более долгим ожиданием ответаПолный дуплекс: обе стороны шлют сообщения независимо
Когда достаточноДанные обновляются редко (раз в минуты)Нужен псевдо-real-time без смены инфраструктурыНужны обновления в реальном времени (чат, котировки, игры)
Сравнение HTTP-polling и WebSocket: повторные запросы против одного постоянного соединения

Когда и зачем использовать WebSocket

Websocket js действительно полезны, когда сервер хочет передать много данных и/или часто обновлять браузер (например, многопользовательские игры или чаты). Традиционный шаблон RESTful представляет собой синхронный механизм, основанный на запросе и ответе, и не подходит для полнодуплексных сценариев реального времени. С WebSockets все эти проблемы устранены — сервер находится под контролем и точно знает, сколько клиентов подключено в каждый момент времени.

Он может немедленно обновлять все подключенные клиенты, когда происходит что-то стоящее, и нет задержки для механизмов опроса HTTP. Серверу не нужно сохранять состояние, когда он уведомляет всех клиентов, и, кроме того, если клиент отключается, соединение разрывается, и сервер немедленно уведомляется.

  • Чаты и мессенджеры — мгновенная доставка сообщений и статусов «печатает...» без опроса сервера.
  • Биржевые котировки и трейдинг — обновление цен и стакана заявок за миллисекунды.
  • Многопользовательские игры — синхронизация позиций и действий игроков в реальном времени.
  • Совместное редактирование документов — как в Google Docs или Figma, изменения видны всем участникам сразу.
  • IoT и мониторинг — потоковая передача показаний датчиков и алертов на дашборд.
  • Live-уведомления в интерфейсе — счётчики, статусы заказов, прогресс длительных операций без перезагрузки страницы.

Плюсы и минусы WebSocket

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

Плюсы:

  • Минимальная задержка — сервер сам инициирует push, не дожидаясь запроса клиента.
  • Меньше служебного трафика на сообщение по сравнению с повторными HTTP-запросами — нет заголовков на каждый пакет.
  • Полный дуплекс — обе стороны отправляют данные независимо друг от друга.
  • Поддерживается всеми современными браузерами и большинством бэкенд-стеков без дополнительных зависимостей.

Минусы:

  • Соединение нужно явно поддерживать: обрывы сети, засыпание вкладки и балансировщики без sticky-сессий рвут сокет.
  • Сложнее горизонтально масштабировать — состояние соединения привязано к конкретному серверному процессу, нужен pub/sub (Redis, NATS) между инстансами.
  • Прокси, корпоративные файрволы и часть CDN по умолчанию режут или буферизуют WebSocket-трафик.
  • Требует ручной реализации переподключения, heartbeat и очереди сообщений на случай разрыва.

Как подключить WebSocket в React: хук на функциональном компоненте

Практика студии YuSMP Group: в новых проектах на React WebSocket подключают не через классовые компоненты и componentDidMount, а через кастомный хук — так проще тестировать логику и переиспользовать её между компонентами. Ниже — минимальный рабочий пример с переподключением при разрыве связи.

  1. Заведите useRef для инстанса WebSocket и useState для статуса соединения и входящих сообщений.
  2. В useEffect откройте соединение и подпишитесь на onopen, onmessage, onerror и onclose.
  3. Отправляйте сообщения через send(), предварительно проверив readyState === WebSocket.OPEN.
  4. В cleanup-функции useEffect закройте соединение, а в обработчике onclose запланируйте переподключение с задержкой (экспоненциальный backoff).
function useWebSocket(url) {
  const ws = useRef(null);
  const [isConnected, setConnected] = useState(false);
  const [lastMessage, setLastMessage] = useState(null);

  useEffect(() => {
    let reconnectTimer;
    let attempt = 0;

    function connect() {
      ws.current = new WebSocket(url);

      ws.current.onopen = () => {
        setConnected(true);
        attempt = 0;
      };

      ws.current.onmessage = (event) => {
        setLastMessage(JSON.parse(event.data));
      };

      ws.current.onerror = () => {
        ws.current?.close();
      };

      ws.current.onclose = () => {
        setConnected(false);
        const delay = Math.min(1000 * 2 ** attempt, 15000);
        attempt += 1;
        reconnectTimer = setTimeout(connect, delay);
      };
    }

    connect();

    return () => {
      clearTimeout(reconnectTimer);
      ws.current?.close();
    };
  }, [url]);

  const sendMessage = (data) => {
    if (ws.current?.readyState === WebSocket.OPEN) {
      ws.current.send(JSON.stringify(data));
    }
  };

  return { isConnected, lastMessage, sendMessage };
}

Хук возвращает статус соединения, последнее сообщение и функцию отправки — компоненту достаточно вызвать const { isConnected, lastMessage, sendMessage } = useWebSocket('wss://api.example.ru/ws'); и не думать о жизненном цикле сокета.

Архитектура real-time-обновлений: React-компонент через хук получает данные от сервера по WebSocket-соединению

Классовый подход: WebSocket в componentDidMount (legacy)

Исторический контекст. До хуков (React 16.8 и раньше) WebSocket подключали в классовых компонентах через componentDidMount. Такой код ещё встречается в legacy-проектах — вот как он устроен.

Пример с проекта: было необходимо отправлять пинг на сервер каждые 30 секунд, чтобы сообщить серверу, что приложение все еще находится в сети, а также отслеживать пользователя, вошедшего в приложение, и продолжительность его пребывания в сети. Требовалось постоянно общаться с сервером, а использование REST API для этого было бы неэффективным.

В приведенном выше фрагменте WebSocket назван Main Component.

Прежде всего, мы создаем новый экземпляр WebSocket как свойство класса ws. Затем в componentDidMount методе мы можем подписаться и прослушивать некоторые события, предоставляемые нам WebSocket.

  • onopen: прослушиватель событий onopen вызывается при установлении соединения WebSocket.
  • onmessage: событие onmessage отправляется при получении данных с сервера.
  • onclose: прослушиватель onclose вызывается при закрытии соединения WebSocket.

Итак, все это настроено в componentDidMount, потому что мы хотим, чтобы эти прослушиватели событий были доступны, когда компонент отображается в DOM. Кроме того, мы можем передать экземпляр WebSocket в качестве реквизита дочернему компоненту, как мы это делали в <ChildComponent/>. Чтобы мы могли прослушивать любое событие в этом экземпляре WebSocket без необходимости создавать новый экземпляр в каждом компоненте, в котором нам нужно использовать этот WebSocket.

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

Готовые библиотеки: Socket.IO и react-use-websocket

Нативный WebSocket API достаточен для большинства задач, но для части сценариев проще взять готовую библиотеку:

  • Socket.IO — поверх WebSocket добавляет автоматический fallback на long polling для старых прокси, встроенное переподключение, комнаты (rooms) и namespace для группировки клиентов. Требует такой же Socket.IO-сервер — это не чистый WebSocket-протокол.
  • react-use-websocket — тонкий хук-обёртка над нативным API: даёт реконнект, очередь сообщений и статусы соединения из коробки, без смены протокола на сервере. Хорошо подходит, если сервер — обычный WebSocket без Socket.IO.

Правило выбора: если нужны комнаты, широковещательные события и fallback для сложных сетей — Socket.IO. Если сервер уже отдаёт «чистый» WebSocket и нужен только удобный React-хук — react-use-websocket или свой хук, как в примере выше.

Типичные ошибки при работе с WebSocket в React

  • Не закрывают соединение в cleanup useEffect — при размонтировании компонента или смене url сокет остаётся висеть, накапливая «зомби»-соединения.
  • Хранят инстанс WebSocket в useState вместо useRef — каждое сообщение вызывает лишний ре-рендер и может пересоздать соединение.
  • Игнорируют onerror — ошибка молча теряется, а onclose срабатывает без понятной причины разрыва.
  • Не реализуют переподключение с backoff — при обрыве связи клиент либо не восстанавливает соединение, либо долбит сервер запросами каждую секунду.
  • Отправляют сообщения без проверки readyState — вызов send() на закрытом или ещё не открытом сокете кидает исключение.
  • Держат одно соединение на весь проект вместо контекста — если WebSocket нужен нескольким компонентам, инстанс стоит поднимать один раз через React Context, а не в каждом компоненте отдельно.

Что важно запомнить

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

Доверьте техническую сторону вашего проекта профессионалам. Веб-услуги и разработка в YuSMP Group - это открытость, прозрачность и высокое качество. Кейсы YuSMP Group подтверждают высокую экспертизу команды. Мы всегда открыты, прислушиваемся к клиентам и создаем продукты, которые приносят бизнесам пользу. Оставили контакты для связи. Напишите нам, чтобы получить бесплатную консультацию и примерный расчет бюджета.

Часто задаваемые вопросы

Что такое WebSocket простыми словами?
WebSocket — протокол, который открывает одно постоянное соединение между браузером и сервером поверх TCP. После рукопожатия обе стороны обмениваются сообщениями напрямую, без повторных HTTP-запросов на каждое обновление.
Чем WebSocket отличается от HTTP?
HTTP — это запрос-ответ: клиент всегда инициирует обмен. WebSocket — постоянное двустороннее соединение, и сервер может сам отправить данные клиенту в любой момент, без нового запроса.
Нужен ли WebSocket, если хватает обычного REST API?
Нет, если данные обновляются редко — раз в минуты и реже, тогда достаточно REST с обычным опросом. WebSocket оправдан, когда важна задержка в миллисекунды: чаты, котировки, live-статусы, многопользовательские сценарии.
Как реализовать переподключение WebSocket в React?
В обработчике onclose запускают setTimeout с растущей задержкой (экспоненциальный backoff) и заново вызывают функцию подключения, а таймер сбрасывается в cleanup-функции useEffect при размонтировании компонента.
Что выбрать — нативный WebSocket API или Socket.IO?
Нативный API — если сервер уже отдаёт «чистый» WebSocket и нужен минимум зависимостей. Socket.IO — если нужны комнаты, широковещательные события и автоматический fallback на long polling для сложных сетей, но сервер тоже должен быть на Socket.IO.
Какие есть альтернативы WebSocket для real-time-обновлений?
Server-Sent Events (SSE) — для одностороннего потока от сервера к клиенту, и long polling — для инфраструктуры, где WebSocket режут прокси или файрволы. Оба проще в настройке, но уступают WebSocket в задержке и не поддерживают полный дуплекс.

Обсудим ваш проект?

Соберём команду под вашу задачу и оценим проект за 2 дня — бесплатно.

Обсудить проект