Статьи

Реализация Spring Websocket Server и клиента

В этой статье описывается реализация сервера и клиента WebSocket на основе Spring Framework. Он демонстрирует полнодуплексную связь и то, как сервер может передавать сообщения клиенту. Вы немного узнаете о том, как работают WebSockets, а также о формате сообщений STOMP, используемом для связи между сервером и клиентом.

Чтобы получить максимальную отдачу от его статьи, вам необходимо хорошее знание Java, а также некоторое знакомство с Spring Framework, особенно Spring MVC. Исходный код сервера, который мы собираемся реализовать , доступен на GitHub .

Замечания Клиффа о веб-сокетах, STOMP и Spring

Чтобы начать работу с Websocket на JVM, вот краткое введение в то, что нам понадобится позже в этой статье.

Введение в WebSockets

WebSocket — это полнодуплексный протокол связи, работающий по протоколу TCP. Обычно он используется для интерактивного взаимодействия между браузером пользователя и внутренним сервером. Примером может служить чат-сервер с обменом данными между сервером и подключенными клиентами в режиме реального времени. Другим примером может быть приложение биржевой торговли, где сервер отправляет изменения цены акций подписанным клиентам без явного запроса клиента.

WebSocket — это канал связи, который использует TCP в качестве основного протокола. Он инициируется клиентом, отправляющим HTTP-запрос на сервер, запрашивающий обновление соединения до WebSocket. Если сервер поддерживает WebSockets, клиентский запрос удовлетворяется, и между двумя сторонами устанавливается соединение WebSocket. После того, как это соединение установлено, все общение происходит через WebSocket и протокол HTTP больше не используется.

Эта статья очень хорошо объясняет детали работы WebSockets.

STOMP через WebSocket

Сам протокол связи WebSocket не требует какого-либо определенного формата обмена сообщениями. Заявки должны согласовать формат сообщений, которыми обмениваются. Этот формат называется подпротоколом. (Это похоже на то, как веб-браузер и веб-сервер согласились использовать протокол HTTP через TCP-сокеты.)

Одним из широко используемых форматов является протокол STOMP (Streaming Text Oriented Message Protocol), используемый для обмена сообщениями общего назначения. Различные системы, ориентированные на передачу сообщений (MOM) , такие как Apache ActiveMQ , HornetQ и RabbitMQ, поддерживают STOMP.

Spring Framework реализует связь WebSocket с STOMP в качестве протокола обмена сообщениями.

Поддержка Spring для WebSockets

Spring Framework обеспечивает поддержку обмена сообщениями WebSocket в веб-приложениях. Версия Spring 4 включает в себя новый модуль spring-websocket , который используется для добавления поддержки WebSocket на сервер и клиент.

Spring Framework Guide содержит подробный HOWTO, демонстрирующий пример реализации сервера . Однако нет необходимости читать это руководство; Мы рассмотрим все детали, необходимые для понимания реализации в этой статье.

Одним из недостатков Spring Framework Guide является отсутствие информации о реализации клиента Java / Spring для взаимодействия с сервером. Мы решаем эту проблему в этой статье. Как вы увидите, реализация требует правильных заклинаний для правильной работы.

Внедрение Spring Chat Server

С необходимыми предпосылками пришло время реализовать сервер чата. Вот краткое изложение того, как это работает:

  • Сервер чата Spring создает конечную точку HTTP ( /chat ), которая используется для установления канала WebSocket.
  • Как только канал установлен, сервер и клиент обмениваются сообщениями по каналу.
  • Пока сервер слушает и отвечает на клиентские сообщения, клиент также может зарегистрировать обратный вызов для «push» сообщений с сервера. Это позволяет серверу отправлять уведомления клиенту по мере необходимости без явного запроса клиента.

Сообщение «push» от сервера к клиенту — это то, что отличает связь WebSocket от обычного HTTP-взаимодействия.

Конфигурация сборки

Зависимости Maven, необходимые для сервера, показаны ниже. Зависимость spring-boot-starter-websocket включает в себя библиотеки, необходимые для реализации WebSockets на стороне сервера.

Остальные зависимости org.webjars — это jQuery, Bootstrap и клиентские библиотеки для обмена сообщениями WebSocket и STOMP, необходимые клиенту чата HTML / Javascript. Эти зависимости Javascript не используются реализацией сервера Java WebSocket. Они необходимы для клиентского интерфейса, который полностью отделен от серверного приложения. Эти зависимости можно безопасно удалить из POM, если клиент HTML / Javascript не используется или упакован отдельно от приложения Spring.

 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator</artifactId> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>sockjs-client</artifactId> <version>1.0.2</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>stomp-websocket</artifactId> <version>2.3.3</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>3.3.7</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.1.0</version> </dependency> 

Контроллер сообщений

В Spring MVC украшение класса с @Controller аннотации @Controller помечает класс как веб-контроллер . Это работает в сочетании с украшением @RequestMapping чтобы пометить метод как конечную точку HTTP. Аналогичным образом, аннотация @MessageMapping доступна в Spring Framework Messaging для регистрации метода в качестве прослушивателя сообщений.

Для нашего простого сервера чата ответ повторяет сообщение, полученное от клиента, и добавляет пару полей. В реальном приложении этот метод используется для реализации бизнес-логики. Вот реализация метода:

 @MessageMapping("/chat/{topic}") @SendTo("/topic/messages") public OutputMessage send( @DestinationVariable("topic") String topic, Message message) throws Exception { return new OutputMessage(message.getFrom(), message.getText(), topic); } 

Метод Message Controller принимает POJO (простой старый объект Java), в который десериализовано сообщение из JSON. Аннотация @DestinationVariable используется для захвата topic переменной шаблона из места назначения. Следующий класс Message определяется сервером для чтения сообщений от клиента.

 public class Message { private String from; private String text; // adding getters and setters here } 

Чтобы отправить ответы обратно клиенту, метод контроллера сообщений использует аннотацию @SendTo указывающую очередь на стороне клиента, в которую должен быть отправлен ответ. Значение, возвращаемое методом, сериализуется в JSON и отправляется в указанное место назначения. В нашем случае мы определяем ответное сообщение следующим образом. Обратите внимание, что text поля входного сообщения просто копируется в сообщение поля ответного message . В вашем приложении вы можете выполнить любую дополнительную обработку, а также включить дополнительные поля в классы запроса или ответа.

 public class OutputMessage { private String from; private String message; private String topic; private Date time = new Date(); // add getters and setters here } 

Настройка Spring

Приложение сконфигурировано для WebSockets с использованием класса @Configuration который расширяет AbstractWebSocketMessageBrokerConfigurer и предоставляет реализацию для следующих методов.

 @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/chat").setAllowedOrigins("*").withSockJS(); } 

Обратите внимание на следующие моменты:

  • Префикс назначения приложения установлен на « /app ». Это означает, что отображения WebSocket имеют префикс « /app » для получения реального значения.
  • Конечная точка « /chat » зарегистрирована для запуска рукопожатия по протоколу WebSocket.
  • Метод setAllowedOrigins("*") используется для CORS-включения сервера, чтобы клиенты с несколькими источниками могли подключаться к серверу. Если ваши требования не нуждаются в клиентском соединении между источниками, вы можете удалить этот вызов.

Клиент и сервер общаются через WebSockets

Клиент веб-браузера

Образец веб-приложения, написанного на HTML и Javascript, включен в код — он реализует простой интерфейс для приложения чата. Снимок приложения показан ниже.

Браузер чат клиент

Подключение и регистрация Push Callback

Приложение использует библиотеку SocksJS для работы с соединением WebSocket и библиотеку stomp-websocket для поддержки STOMP. Приведенный ниже код подключения создает канал WebSocket и использует его для создания клиента STOMP. Как только WebSocket подключен, для обратных сообщений с сервера регистрируется обратный вызов. Сообщение десериализуется из JSON и отображается для пользователя.

 var wsocket = new SockJS('/chat'); var client = Stomp.over(wsocket); client.connect({}, function(frame) { client.subscribe('/topic/messages', function (message) { showMessage(JSON.parse(message.body)); }); }); 

Отправка сообщений на сервер

Клиент отправляет сообщения на сервер с помощью send() . Сообщение находится в формате JSON и в том же формате, который ожидается контроллером сообщений на стороне сервера.

 client.send("/app/chat/" + topic, {}, JSON.stringify({ from: $("#from").val(), text: $('#text').val(), })); 

Сборка и запуск приложения

Сервер реализован в виде приложения Spring Boot и включает встроенный веб-сервер. Создайте приложение, используя Maven.

 mvn clean package 

Запустите сервер, указав порт, который веб-сервер должен прослушивать при необходимости.

 java -Dserver.port=9090 -jar target/chat-server-0.1.0.jar 

После запуска сервера откройте приложение чата по адресу http://<hostname>:9090/

Java / Spring Chat Client

Хотя клиент HTML / Javascript полезен для демонстрации использования WebSocket в браузере, клиент Java полезен для взаимодействия с сервером из приложения. Может быть, у вас есть приложение Forex Trading, которое должно сообщать обновленные цены всем подключенным приложениям. Или, может быть, вы хотите подписаться на опубликованные сообщения, чтобы получать уведомления об обновлениях. Независимо от варианта использования, полезно узнать, как взаимодействовать с сервером WebSocket из приложения Java.

Настройте базовые транспорты и создайте клиент SockJS. Клиентский модуль SockJS в Spring реализует протокол sockjs (здесь JS является просто частью имени — JavaScript не используется) в качестве запасного варианта в случае, если WebSocket не поддерживается на клиенте (браузере). Это позволяет приложениям использовать WebSockets, но при необходимости использовать альтернативные варианты во время выполнения — без необходимости изменения кода приложения. Альтернативным транспортом является потоковая передача Ajax / XHR, используемая Internet Explorer 8 и 9.

 WebSocketClient simpleWebSocketClient = new StandardWebSocketClient(); List<Transport> transports = new ArrayList<>(1); transports.add(new WebSocketTransport(simpleWebSocketClient)); SockJsClient sockJsClient = new SockJsClient(transports); 

SockJsClient используется для создания клиента STOMP, который поддерживает используемый протокол обмена сообщениями.

 WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient); 

И, конечно же, нам нужно отобразить JSON в POJO и обратно.

 stompClient.setMessageConverter(new MappingJackson2MessageConverter()); 

Подключитесь к серверу, и после установления соединения обработчик сообщений будет зарегистрирован для прослушивания push-сообщений с сервера.

 String url = "ws://localhost:9090/chat"; StompSessionHandler sessionHandler = new MyStompSessionHandler(); StompSession session = stompClient.connect(url, sessionHandler).get(); 

Подписаться на сообщения с сервера следующим образом:

 session.subscribe(topic, new StompFrameHandler() { @Override public Type getPayloadType(StompHeaders headers) { return ServerMessage.class; } @Override public void handleFrame(StompHeaders headers,Object payload) { System.err.println(payload.toString()); } }); 

Отправляйте сообщения на сервер следующим образом:

 ClientMessage msg = new ClientMessage(userId, line); session.send("/app/chat/java", msg); 

Полный исходный код для клиента можно скачать здесь .

Резюме

Давайте рассмотрим.

Мы начали с краткого введения в WebSockets и STOMP. STOMP — это суб-протокол обмена сообщениями, работающий через WebSockets, который предоставляет такие средства, как подписка на темы.

Spring Framework (используется для сервера WebSocket) предоставляет модули для WebSocket как для сервера, так и для клиента. Модули легко настраиваются с помощью аннотаций, определенных Spring. Spring также выполняет сериализацию и десериализацию сообщений в POJO и из них.

Клиент веб-браузера показывает взаимодействие с сервером и способы приема и обработки push-сообщений с сервера. Наконец, Spring Client иллюстрирует, как общаться с сервером WebSocket и подписываться на темы сообщений.