Веб-сокеты (WebSockets ), возможно, самое интересное нововведение в веб-технологиях со времен появления «Aсинxpoннoгo JavaScript и XML»(AJAX). Они стали популярными с выходом HTML5 и поддерживаются множеством веб-фреймворков.
Однако потребовалось немало времени для создания стабильной и совместимой спецификации веб-сокетов.
Модель протокола передачи гипертекста (НТТР) была спроектирована задолго до того, как стал популярен Интернет, она основывается на простых спецификации и дизайне. В традиционной модели НТТР клиент открывает соединение с сервером прикладной части, отправляет HTTP-запрос типа GET, POST, PUT или DELETE, а HTTP-сервер возвращает соответствующий ответ.
Традиционная модель НТТР была обременительной практически для любого приложения, выходящего за пределы простой модели данных «получить и отправить контент«. Представьте себе клиент приложения для интерактивной переписки, в котором участники могут отправлять сообщения в любом порядке и сотни участников могут общаться одновременно.
Для подобных целей стандартный подход «запрос-ответ» налагает слишком сильные ограничения. Первыми попытками обойти эти ограничения стали AJAX и Comet. Оба были основаны на так называемых длинных опросах: открытии НТТР-соединения и поддержании его в активном состоянии (сохранении соединения открытым) посредством незавершения отправки ответа.
С помощью веб-сокетов клиент может создать «сырой» сокет для сервера и осуществлять полнодуплексную связь. Поддержка веб-сокетов была введена в JSR-356.
Пакет javax.websocket и его серверный подпакет содержат все относящиеся к веб сокетам классы, интерфейсы и аннотации.
Чтобы реализовать веб-сокеты на платформе Java ЕЕ, вам необходимо создать класс конечной точки с методами жизненного цикла веб-сокета, как показано ниже:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// Пример конечной точки package com.devchronicles.websockets; public class HelloEndpoint extends Endpoint { @Override public void onOpen(final Session session, EndpointConfig config) { session.addMessageHandler(new MessageHandler.Whole<String>() { @Override public void onMessage(String msg) { try { session.getBasicRemote().sendText("Hello " + msg); } catch (IOException e) { } } }); } } |
Класс Endpoint предоставляет три метода жизненного цикла: onOpen, onClose и onError. Расширяющий его класс должен реализовать как минимум метод onOpen.
Вы можете развернуть конечную точку двумя способами: с помощью конфигурации или программными средствами.
Чтобы выполнить развертывание кода ваше приложение должно вызвать следующее:
1 |
ServerEndpointConfig.Builder.create(HelloEndpoint.class, "/hello").build(); |
Развернутый веб-сокет доступен на ws://<host>:<port>/<application>/hello. Однако лучше использовать конфигурацию с помощью аннотации. При этом та же конечная точка становится кодом который приводится ниже:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// Пример конечной точки с аннотациями package com.devchronicles.websockets; @ServerEndpoint("/hello") public class HelloEndpoint { @OnMessage public void onMessage(Session session, String msg) { try { session.getBasicRemote().sendText("Hello " + msg); } catch (IOException e) { } } } |
Такой подход позволяет вам использовать аннотации, придерживаясь подхода простых Jаvа-объектов в старом стиле (POJO), поскольку вы не расширяете базовый класс. У аннотированной конечной точки те же методы жизненного цикла, что и в первом примере кода, но она вводит дополнительный метод жизненного цикла onMessage.
Вместо реализации OnOpen и добавления обработчика onMessage в основанном на аннотациях подходе достаточно реализовать аннотированный метод жизненного цикла onMessage. Вы можете аннотировать с помощью @ОnМеssаgе несколько методов, чтобы получать различные типы данных, такие как String или ByteBuffer для двоичных данных.
Реализация веб-сокета со стороны клиента зависит от используемого веб-фреймворка. Как бы то ни было, в следующем фрагменте показана простая версия на языке JavaScript:
1 2 |
var webSocket = new WebSocket('ws://127.0.0.1:8080/websockets/hello'); webSocket.send("world"); |
Лучшим примером будет отправка сложного объекта в формате нотации объектов JavaScript (JSON), который может быть организован в объект так, как показано в следующем фрагменте:
1 2 3 4 5 6 7 8 |
var msg = { type: "message", text: "World", date: Date.now() }; webSocket.send(JSON.stringify(msg)); webSocket.onmessage = function(evt) { /* Должен получить hello world */ }; |
Веб-сокеты отлично подходят для создания веб-приложений, требующих асинхронного обмена сохраняемыми сообщениями между клиентом и сервером. Платформа Jаvа ЕЕ предоставляет удобную реализацию веб-сокетов.
Конфигурационных настроек и вариантов реализации у них гораздо больше, чем обсуждается здесь. Если мы пробудили у вас интерес к веб-сокетам, рекомендуем заглянуть в руководство Oracle по платформе Java ЕЕ, детальнее рассказывающее о программировании вебсокетов с помощью Java API.