WebSocket на Java: real-time приложения от А до Я

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

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

Начать курс

Введение: Почему WebSocket стал стандартом real-time коммуникаций

Современные веб-приложения требуют мгновенной реакции на события. Будь то чат, биржевой терминал или многопользовательская игра — пользователи ожидают, что данные будут обновляться без перезагрузки страницы. Традиционный HTTP (REST API) с его моделью "запрос-ответ" для таких задач неэффективен: клиент вынужден постоянно опрашивать сервер (polling), что создает лишнюю нагрузку и задержки.

Решение этой проблемы — протокол WebSocket, который устанавливает постоянное двустороннее соединение между клиентом и сервером. В этой статье мы разберем WebSocket на Java от основ до создания полноценного real-time приложения. Вы узнаете, как настроить сервер, написать клиент и обрабатывать ошибки.

Основы WebSocket: как это работает

Отличие от HTTP

WebSocket — это протокол, работающий поверх TCP. Он начинается как HTTP-запрос (рукопожатие), после чего соединение "обновляется" (upgrade) до постоянного двустороннего канала. В отличие от HTTP, где каждый запрос требует нового соединения, WebSocket держит его открытым, позволяя серверу отправлять данные клиенту самостоятельно (push-модель).

Основные преимущества:

  • Низкая задержка — данные передаются мгновенно без повторных рукопожатий.
  • Меньший трафик — минимальный заголовок (всего 2-14 байт) против сотен байт в HTTP.
  • Двусторонность — и клиент, и сервер могут инициировать отправку данных.

Архитектура real-time приложения на Java

Типичное приложение состоит из трех компонентов:

  1. Клиент (браузер или мобильное приложение), который инициирует соединение.
  2. Сервер приложений (Java), который управляет WebSocket-сессиями.
  3. Бэкенд (база данных, очереди сообщений), который обрабатывает бизнес-логику.

Java предоставляет мощные инструменты для работы с WebSocket — как на стороне сервера (Jakarta WebSocket, Spring WebSocket), так и на стороне клиента (стандартный Java WebSocket API).

Создание WebSocket сервера на Java

Подготовка среды

Для серверной части мы будем использовать Jakarta WebSocket (ранее Java EE WebSocket). Это стандартный API, который входит в состав Jakarta EE. Вам понадобится:

  • JDK 11 или выше.
  • Сервер приложений (например, Apache Tomcat 10 или GlassFish 7).
  • Maven для управления зависимостями.

Добавьте в pom.xml зависимость:

<dependency>
    <groupId>jakarta.websocket</groupId>
    <artifactId>jakarta.websocket-api</artifactId>
    <version>2.1.0</version>
    <scope>provided</scope>
</dependency>

Написание WebSocket-эндпоинта

Создадим простой эхо-сервер, который отправляет обратно полученное сообщение:

import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;

@ServerEndpoint("/chat")
public class ChatEndpoint {

    @OnOpen
    public void onOpen(Session session) {
        System.out.println("Новое соединение: " + session.getId());
    }

    @OnMessage
    public String onMessage(String message, Session session) {
        System.out.println("Получено от " + session.getId() + ": " + message);
        return "Эхо: " + message;
    }

    @OnClose
    public void onClose(Session session, CloseReason reason) {
        System.out.println("Соединение закрыто: " + session.getId() 
            + " по причине: " + reason.getReasonPhrase());
    }

    @OnError
    public void onError(Session session, Throwable error) {
        System.err.println("Ошибка в сессии " + session.getId() + ": " + error.getMessage());
    }
}

Аннотация @ServerEndpoint("/chat") указывает путь, по которому клиенты будут подключаться. Методы с аннотациями @OnOpen, @OnMessage, @OnClose и @OnError обрабатывают жизненный цикл соединения.

Управление сессиями и рассылка сообщений

Для real-time чата нам нужно хранить все активные сессии и рассылать сообщения всем участникам. Используем статическую коллекцию:

import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

@ServerEndpoint("/chat")
public class ChatEndpoint {
    private static final Set<Session> sessions = new CopyOnWriteArraySet<>();

    @OnOpen
    public void onOpen(Session session) {
        sessions.add(session);
        broadcast("Пользователь подключился. Всего: " + sessions.size());
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        broadcast("[" + session.getId() + "]: " + message);
    }

    @OnClose
    public void onClose(Session session) {
        sessions.remove(session);
        broadcast("Пользователь отключился. Всего: " + sessions.size());
    }

    private void broadcast(String message) {
        for (Session s : sessions) {
            if (s.isOpen()) {
                s.getBasicRemote().sendText(message);
            }
        }
    }
}

Мы используем CopyOnWriteArraySet для безопасной работы в многопоточной среде. Метод broadcast() отправляет сообщение всем открытым сессиям.

WebSocket клиент на Java

Клиент для тестирования из консоли

Java предоставляет клиентский API для подключения к WebSocket-серверу. Вот пример простого клиента:

import java.net.URI;
import jakarta.websocket.*;

@ClientEndpoint
public class WebSocketClient {

    @OnOpen
    public void onOpen(Session session) {
        System.out.println("Подключено к серверу");
        session.getBasicRemote().sendText("Привет, сервер!");
    }

    @OnMessage
    public void onMessage(String message) {
        System.out.println("Получено: " + message);
    }

    @OnClose
    public void onClose(Session session, CloseReason reason) {
        System.out.println("Соединение закрыто: " + reason.getReasonPhrase());
    }

    public static void main(String[] args) throws Exception {
        WebSocketContainer container = ContainerProvider.getWebSocketContainer();
        String uri = "ws://localhost:8080/chat";
        container.connectToServer(WebSocketClient.class, URI.create(uri));
        
        // Держим клиент открытым                

Блоги

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