«1. Введение

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

Поскольку HTTP является синхронным, а приложения реального времени должны быть асинхронными, любые решения, такие как опрос или длительный опрос (Comet), как правило, сложны и неэффективны.

Чтобы решить указанную выше проблему, нам нужен основанный на стандартах, двунаправленный и полнодуплексный протокол, который мог бы использоваться как серверами, так и клиентами, и это привело к внедрению API JSR 356 — в в этой статье мы покажем пример его использования.

2. Настройка

Давайте включим в наш проект зависимости Spring WebSocket:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>5.2.2.RELEASE</version>
 </dependency>
 <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-messaging</artifactId>
    <version>5.2.2.RELEASE</version>
 </dependency>

Мы всегда можем получить последние версии зависимостей от Maven Central для spring-websocket и spring-messaging.

3. STOMP

Stream Text-Oriented Messaging Protocol (STOMP) — это простой, интероперабельный проводной формат, который позволяет клиенту и серверу обмениваться данными практически со всеми брокерами сообщений. Это альтернатива AMQP (протокол расширенной очереди сообщений) и JMS (служба обмена сообщениями Java).

STOMP определяет протокол для взаимодействия клиент/сервер с использованием семантики обмена сообщениями. Семантика находится поверх WebSockets и определяет фреймы, которые сопоставляются с фреймами WebSockets.

Использование STOMP дает нам гибкость для разработки клиентов и серверов на разных языках программирования. В этом текущем примере мы будем использовать STOMP для обмена сообщениями между клиентом и сервером.

4. Сервер WebSocket

Подробнее о создании серверов WebSocket можно прочитать в этой статье.

5. Клиент WebSocket

Для связи с сервером WebSocket клиент должен инициировать соединение WebSocket, отправив HTTP-запрос на сервер с правильно установленным заголовком Upgrade:

GET ws://websocket.example.com/ HTTP/1.1
Origin: http://example.com
Connection: Upgrade
Host: websocket.example.com
Upgrade: websocket

Обратите внимание, что URL-адреса WebSocket используют схемы ws и wss, вторая означает безопасные WebSockets.

Сервер отвечает, отправляя заголовок Upgrade в ответ, если включена поддержка WebSockets.

HTTP/1.1 101 WebSocket Protocol Handshake
Date: Wed, 16 Oct 2013 10:07:34 GMT
Connection: Upgrade
Upgrade: WebSocket

Как только этот процесс (также известный как рукопожатие WebSocket) завершен, первоначальное соединение HTTP заменяется соединением WebSocket поверх того же соединения TCP/IP, после чего обе стороны могут обмениваться данными.

Это соединение на стороне клиента инициируется экземпляром WebSocketStompClient.

5.1. WebSocketStompClient

Как описано в разделе 3, сначала нам нужно установить соединение WebSocket, и это делается с помощью класса WebSocketClient.

WebSocketClient можно настроить, используя:

    StandardWebSocketClient, предоставляемый любой реализацией JSR-356, такой как Tyrus JettyWebSocketClient, предоставляемый собственным WebSocket API Jetty 9+ Любую реализацию WebSocketClient Spring

Мы будем использовать StandardWebSocketClient, реализацию WebSocketClient в нашем примере:

WebSocketClient client = new StandardWebSocketClient();

WebSocketStompClient stompClient = new WebSocketStompClient(client);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());

StompSessionHandler sessionHandler = new MyStompSessionHandler();
stompClient.connect(URL, sessionHandler);

new Scanner(System.in).nextLine(); // Don't close immediately.

По умолчанию WebSocketStompClient поддерживает SimpleMessageConverter. Поскольку мы имеем дело с сообщениями JSON, мы устанавливаем конвертер сообщений на MappingJackson2MessageConverter, чтобы преобразовать полезную нагрузку JSON в объект.

При подключении к конечной точке мы передаем экземпляр StompSessionHandler, который обрабатывает такие события, как afterConnected и handleFrame.

Если наш сервер поддерживает SockJs, мы можем модифицировать клиент для использования SockJsClient вместо StandardWebSocketClient.

5.2. StompSessionHandler

Мы можем использовать StompSession для подписки на тему WebSocket. Это можно сделать, создав экземпляр StompSessionHandlerAdapter, который, в свою очередь, реализует StompSessionHandler.

StompSessionHandler предоставляет события жизненного цикла для сеанса STOMP. События включают обратный вызов при установлении сеанса и уведомления в случае сбоев.

«Как только клиент WebSocket подключается к конечной точке, StompSessionHandler получает уведомление и вызывается метод afterConnected(), где мы используем StompSession для подписки на тему:

@Override
public void afterConnected(
  StompSession session, StompHeaders connectedHeaders) {
    session.subscribe("/topic/messages", this);
    session.send("/app/chat", getSampleMessage());
}
@Override
public void handleFrame(StompHeaders headers, Object payload) {
    Message msg = (Message) payload;
    logger.info("Received : " + msg.getText()+ " from : " + msg.getFrom());
}

Убедитесь, что сервер WebSocket работает и работает клиента, сообщение будет отображаться на консоли:

INFO o.b.w.client.MyStompSessionHandler - New session established : 53b993eb-7ad6-4470-dd80-c4cfdab7f2ba
INFO o.b.w.client.MyStompSessionHandler - Subscribed to /topic/messages
INFO o.b.w.client.MyStompSessionHandler - Message sent to websocket server
INFO o.b.w.client.MyStompSessionHandler - Received : Howdy!! from : Nicky

6. Заключение

В этом кратком руководстве мы реализовали клиент WebSocket на базе Spring.

Полную реализацию можно найти на GitHub.