Рубрики
Без рубрики

Руководство по Java API для WebSocket

WebSocket обеспечивает двунаправленную, полнодуплексную связь между клиентом и сервером в режиме реального времени между сервером и веб-браузером.

Автор оригинала: baeldung.

1. Обзор

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

В этой статье мы рассмотрим Java API для WebSockets, создав приложение, похожее на чат.

2. JSR 356

JSR 356 или Java API для WebSocket, определяет API, который разработчики Java могут использовать для интеграции WebSocket в свои приложения – как на стороне сервера, так и на стороне клиента Java.

Этот Java API предоставляет как серверные, так и клиентские компоненты:

  • Сервер : все в javax.websocket.сервер пакет.
  • Клиент : содержимое пакета javax.websocket , который состоит из API на стороне клиента, а также общих библиотек как для сервера, так и для клиента.

3. Создание чата с помощью WebSockets

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

Мы начнем с добавления последней зависимости в pom.xml файл:


    javax.websocket
    javax.websocket-api
    1.1

Последнюю версию можно найти здесь .

Чтобы преобразовать объекты Java | в их представления JSON и наоборот, мы будем использовать Gson:


    com.google.code.gson
    gson
    2.8.0

Последняя версия доступна в репозитории Maven Central .

3.1. Конфигурация конечной точки

Существует два способа настройки конечных точек: на основе аннотаций и на основе расширений. Вы можете либо расширить javax.websocket.Конечная точка класс или используйте выделенные аннотации уровня метода. Поскольку модель аннотаций приводит к более чистому коду по сравнению с программной моделью, аннотация стала обычным выбором кодирования. В этом случае события жизненного цикла конечной точки WebSocket обрабатываются следующими аннотациями:

  • @ServerEndpoint: Если он украшен @ServerEndpoint, контейнер обеспечивает доступность класса как WebSocket сервер, прослушивающий определенное пространство URI
  • @ClientEndpoint : Класс, украшенный этой аннотацией, рассматривается как WebSocket клиент
  • @onOpen : Метод Java с @onOpen вызывается контейнером при инициировании нового соединения WebSocket
  • @onMessage : Метод Java, аннотированный @onMessage, получает информацию из контейнера WebSocket при отправке сообщения в конечную точку
  • @onError : Метод с @onError вызывается, когда возникает проблема со связью
  • @OnClose : Используется для украшения метода Java, который вызывается контейнером при закрытии соединения WebSocket

3.2. Запись конечной точки сервера

Мы объявляем класс Java WebSocket конечной точкой сервера, аннотируя его с помощью @ServerEndpoint . Мы также указываем URI, в котором развертывается конечная точка. URI определяется относительно корня контейнера сервера и должен начинаться с прямой косой черты:

@ServerEndpoint(value = "/chat/{username}")
public class ChatEndpoint {

    @OnOpen
    public void onOpen(Session session) throws IOException {
        // Get session and WebSocket connection
    }

    @OnMessage
    public void onMessage(Session session, Message message) throws IOException {
        // Handle new messages
    }

    @OnClose
    public void onClose(Session session) throws IOException {
        // WebSocket connection closes
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        // Do error handling here
    }
}

Приведенный выше код является скелетом конечной точки сервера для нашего приложения, похожего на чат. Как вы можете видеть, у нас есть 4 аннотации, сопоставленные с их соответствующими методами. Ниже вы можете увидеть реализацию таких методов:

@ServerEndpoint(value="/chat/{username}")
public class ChatEndpoint {
 
    private Session session;
    private static Set chatEndpoints 
      = new CopyOnWriteArraySet<>();
    private static HashMap users = new HashMap<>();

    @OnOpen
    public void onOpen(
      Session session, 
      @PathParam("username") String username) throws IOException {
 
        this.session = session;
        chatEndpoints.add(this);
        users.put(session.getId(), username);

        Message message = new Message();
        message.setFrom(username);
        message.setContent("Connected!");
        broadcast(message);
    }

    @OnMessage
    public void onMessage(Session session, Message message) 
      throws IOException {
 
        message.setFrom(users.get(session.getId()));
        broadcast(message);
    }

    @OnClose
    public void onClose(Session session) throws IOException {
 
        chatEndpoints.remove(this);
        Message message = new Message();
        message.setFrom(users.get(session.getId()));
        message.setContent("Disconnected!");
        broadcast(message);
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        // Do error handling here
    }

    private static void broadcast(Message message) 
      throws IOException, EncodeException {
 
        chatEndpoints.forEach(endpoint -> {
            synchronized (endpoint) {
                try {
                    endpoint.session.getBasicRemote().
                      sendObject(message);
                } catch (IOException | EncodeException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

Когда новый пользователь входит в систему ( @onOpen ), он немедленно сопоставляется со структурой данных активных пользователей. Затем создается сообщение и отправляется всем конечным точкам с помощью метода broadcast .

Этот метод также используется всякий раз, когда новое сообщение отправляется ( @onMessage ) любым из подключенных пользователей – это основная цель чата.

Если в какой-то момент возникает ошибка, метод с аннотацией @onError обрабатывает ее. Этот метод можно использовать для регистрации информации об ошибке и очистки конечных точек.

Наконец, когда пользователь больше не подключен к чату, метод @OnClose очищает конечную точку и передает всем пользователям, что пользователь был отключен.

4. Типы сообщений

Спецификация WebSocket поддерживает два проводных формата данных – текстовый и двоичный. API поддерживает оба этих формата, добавляет возможности для работы с объектами Java и сообщениями проверки работоспособности (пинг-понг), как определено в спецификации:

  • Text : Любые текстовые данные ( java.lang.String , примитивы или их эквивалентные классы-оболочки)
  • Двоичные : Двоичные данные (например, аудио, изображение и т.д.), представленные java.nio.ByteBuffer или byte[] (массив байтов)
  • Java objects : API позволяет работать с собственными представлениями (Java object) в вашем коде и использовать пользовательские преобразователи (кодеры/декодеры) для преобразования их в совместимые проводные форматы (текстовые, двоичные), разрешенные протоколом WebSocket
  • Пинг-понг : A javax.websocket.PongMessage – это подтверждение, отправленное одноранговым узлом WebSocket в ответ на запрос проверки работоспособности (ping)

Для нашего приложения мы будем использовать объекты Java. Мы создадим классы для кодирования и декодирования сообщений.

4.1. Шифратор

Кодировщик берет объект Java и создает типичное представление, подходящее для передачи в виде сообщения, такого как JSON, XML или двоичное представление. Кодеры можно использовать, реализовав кодер .Текст или Кодировщик.Двоичные интерфейсы .

В приведенном ниже коде мы определяем класс Message для кодирования, а в методе encode мы используем Gson для кодирования объекта Java в JSON:

public class Message {
    private String from;
    private String to;
    private String content;
    
    //standard constructors, getters, setters
}
public class MessageEncoder implements Encoder.Text {

    private static Gson gson = new Gson();

    @Override
    public String encode(Message message) throws EncodeException {
        return gson.toJson(message);
    }

    @Override
    public void init(EndpointConfig endpointConfig) {
        // Custom initialization logic
    }

    @Override
    public void destroy() {
        // Close resources
    }
}

4.2. Декодер

Декодер является противоположностью кодера и используется для преобразования данных обратно в объект Java. Декодеры могут быть реализованы с помощью декодера .Текст или Декодер.Двоичные интерфейсы .

Как мы видели с кодировщиком, метод decode заключается в том, что мы берем JSON, полученный в сообщении, отправленном в конечную точку, и используем Gson для преобразования его в класс Java с именем Message:

public class MessageDecoder implements Decoder.Text {

    private static Gson gson = new Gson();

    @Override
    public Message decode(String s) throws DecodeException {
        return gson.fromJson(s, Message.class);
    }

    @Override
    public boolean willDecode(String s) {
        return (s != null);
    }

    @Override
    public void init(EndpointConfig endpointConfig) {
        // Custom initialization logic
    }

    @Override
    public void destroy() {
        // Close resources
    }
}

4.3. Настройка кодера и декодера в конечной точке сервера

Давайте соберем все вместе, добавив классы, созданные для кодирования и декодирования данных на уровне класса аннотации @ServerEndpoint :

@ServerEndpoint( 
  value="/chat/{username}", 
  decoders = MessageDecoder.class, 
  encoders = MessageEncoder.class )

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

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

В этой статье мы рассмотрели, что такое Java API для WebSockets и как он может помочь нам создавать такие приложения, как этот чат в реальном времени.

Мы рассмотрели две модели программирования для создания конечной точки: аннотации и программирование. Мы определили конечную точку, используя модель аннотаций для нашего приложения вместе с методами жизненного цикла.

Кроме того, чтобы иметь возможность обмениваться данными между сервером и клиентом, мы увидели, что нам нужны кодеры и декодеры для преобразования объектов Java в JSON и наоборот.

API JSR 356 очень прост, а модель программирования на основе аннотаций позволяет очень легко создавать приложения WebSocket.

Чтобы запустить приложение, которое мы построили в примере, все, что нам нужно сделать, это развернуть файл war на веб-сервере и перейти по URL: http://localhost:8080/java-websocket/. Вы можете найти ссылку на репозиторий здесь .