Весенний Веб-Сокет. Как Это Работает?

Доброго времени суток, уважаемые хабровцы.

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

Протокол состоит из двух этапов:

Весенний веб-сокет. Как это работает?

Запрос Handshake использует запрос HTTP GET, в результате чего соединение обновляется до WebSocket. В этой статье мы подробно рассмотрим механизм установления связи между клиентом и сервером, прокачки соединения с WebSocket и пересылки сообщения в Spring-приложение.

В анализе мы будем использовать конфигурацию на основе Spring-WebSocket и аннотаций.



Создание класса конфигурации

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

Чтобы использовать WebSocket в классе конфигурации, нам нужно использовать аннотацию @EnableWebSocket. Из описания этой аннотации следует, что нам необходимо реализовать интерфейс WebSocketConfigurer с нашим классом конфигурации.

Интерфейс WebSocketConfigurer содержит один метод. RegisterWebSocketHandlers (реестр WebSocketHandlerRegistry) .

С помощью входного параметра WebSocketHandlerRegistry мы добавляем обработчики (WebSocketHandler) входящих сообщений на определенный URL. Давайте посмотрим, как работает аннотация @EnableWebSocket более подробно.

  
  
   

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(DelegatingWebSocketConfiguration.class) public @interface EnableWebSocket { }

Основная задача этой аннотации — импортировать класс конфигурации DelegatingWebSocketConfiguration, который с помощью @Autowired будет получать экземпляры интерфейса WebSocketConfigurer (наш класс конфигурации).

Эти экземпляры WebSocketConfigurer используются в родительском классе WebSocketConfigurationSupport для создания bean-компонента HandlerMapping. Создание bean-компонента HandlerMapping необходимо, чтобы DispatcherServlet мог позже определить обработчик для этого URL-адреса.



Весенний веб-сокет. Как это работает?

Чтобы преобразовать WebSocketHandler в экземпляр HandlerMapping, нам нужны адаптеры WebSocketHttpRequestHandler и WebSocketHandlerMapping. Используя WebSocketHttpRequestHandler, мы преобразуем WebSocketHandler в HttpRequestHandler. А затем, используя комбинацию url, по которому мы ждём запрос на открытие WebSocket, и HttpRequestHandler, мы создадим экземпляр WebSocketHandlerMapping, который будет зарегистрирован в DispatcherServlet для обработки HTTP-запроса.



Весенний веб-сокет. Как это работает?

Когда клиент отправляет запрос на открытие соединения WebSocket, запрос поступает через DispatcherServlet к методу handleRequest (HttpServletRequest servletRequest, HttpServletResponse servletResponse) наш декоратор WebSocketHttpRequestHandler. Этот метод вызывает перехватчики, объявленные в классе конфигурации при настройке WebSocketHandlers, и вызывает метод doHandshake, стандартный или пользовательский экземпляр HandshakeHandler.

Весенний веб-сокет. Как это работает?



Обработка запроса на рукопожатие

Давайте подробнее рассмотрим, как работает метод doHandshake. Именно здесь происходит вся магия преобразования соединения в WebSockets. Но сначала давайте посмотрим на параметры запроса пользователя и ответа сервера.

Типичный пример пользовательского запроса выглядит так:

GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

  • Origin — содержит URL-адрес, с которого сделан запрос.

    Используется для проверки действительных адресов.

  • Sec-WebSocket-Protocol — определяет набор подпротоколов, например STOMP, о котором речь пойдет в будущих статьях.

  • Sec-WebSocket-Key — это случайный ключ, генерируемый браузером: 16 байт, в кодировке Base64.
  • Sec-WebSocket-Version — версия протокола.

  • Sec-WebSocket-Extensions — дополнительные расширения, например, permessage-deflate указывает, что сообщения будут передаваться в сжатом виде.

Типичный ответ сервера:

HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Код ответа HTTP 101 указывает на переключение протокола, в нашем случае на WebSocket. Sec-WebSocket-Accept — расчетное значение на основе переданного Sec-WebSocket-Key и константы «258EAFA5-E914-47DA-95CA-C5AB0DC85B11» — по сути это подтверждение от сервера о том, что он готов инициировать соединение WebSocket. Работу метода doHandshake схематически можно представить следующим образом:

Весенний веб-сокет. Как это работает?



Запросить стратегию обновления до WebSocket

Стратегии обновления соединения, доступные в настоящее время
  • TomcatRequestUpgradeStrategy
  • JettyRequestUpgradeStrategy
  • Стратегия UndertowRequestUpgrade
  • СтеклоРыбаЗапросОбновитьСтратегия
  • WebLogicRequestUpgradeStrategy
  • WebSphereRequestUpgradeStrategy
Процесс обновления стратегии состоит из следующих этапов:

Весенний веб-сокет. Как это работает?

* ?Экземпляр EndPoint создается путем упаковки WebSocketHandler в StandardWebSocketHandlerAdapter, который является наследником EndPoint. Для создания сеанса используется StandardWebSocketSession. ** Для обновления HttpServletRequest используем стандартный метод обновления этого интерфейса, которому мы передаем реализацию HttpUpgradeHandler, в случае работы с Tomcat — это WsHttpUpgradeHandler. При инициализации экземпляра HttpUpgradeHandler создается EndPonit и регистрируется в WebSocketContainer. После этих настроек наша реализация WebSocketHandler готова принимать входящие сообщения, и с помощью WebSocketSession мы можем отправлять сообщения клиенту.

Большое спасибо за ваше внимание.

В следующих статьях мы рассмотрим работу резервного механизма с использованием SockJS и возможности субпротокола STOMP. Использованные источники: → Протокол вебсокетов Весенний веб-сокет Теги: #java #spring framework #websocket #java

Вместе с данным постом часто просматривают: