WebSocket用Java APIガイド

WebSocketのJava APIガイド

1. 概要

WebSocketは、双方向、全二重、リアルタイムのクライアント/サーバー通信を提供することにより、サーバーとWebブラウザー間の効率的な通信の制限に代わるものを提供します。 サーバーはいつでもクライアントにデータを送信できます。 TCP上で実行されるため、低遅延の低レベル通信も提供し、各メッセージのオーバーヘッドを削減します.

この記事では、チャットのようなアプリケーションを作成して、WebSocket用のJavaAPIを見ていきます。

2. JSR 356

JSR 356またはWebSocket用のJava APIは、Java開発者がWebSocketをアプ​​リケーションと統合するために使用できるAPIを指定します(サーバー側とJavaクライアント側の両方で)。

このJava APIは、サーバー側とクライアント側の両方のコンポーネントを提供します。

  • Serverjavax.websocket.serverパッケージ内のすべて。

  • Clientjavax.websocketパッケージのコンテンツ。これは、クライアント側APIと、サーバーとクライアントの両方に共通のライブラリで構成されます。

3. WebSocketを使用したチャットの構築

非常にシンプルなチャットのようなアプリケーションを構築します。 すべてのユーザーは、任意のブラウザからチャットを開き、名前を入力し、チャットにログインして、チャットに接続している全員と通信を開始できます。

まず、最新の依存関係をpom.xmlファイルに追加します。


    javax.websocket
    javax.websocket-api
    1.1

最新バージョンはhereで見つかる可能性があります。

JavaObjectsをJSON表現に、またはその逆に変換するために、Gsonを使用します。


    com.google.code.gson
    gson
    2.8.0

最新バージョンは、Maven Centralリポジトリで入手できます。

3.1. エンドポイント構成

エンドポイントを構成するには、annotation-ベースと拡張ベースの2つの方法があります。 javax.websocket.Endpointクラスを拡張するか、専用のメソッドレベルのアノテーションを使用できます。 注釈モデルは、プログラムモデルに比べてコードが簡潔になるため、注釈は従来のコーディングの選択肢になりました。 この場合、WebSocketエンドポイントライフサイクルイベントは、次のアノテーションによって処理されます。

  • @ServerEndpoint:@ServerEndpoint,で装飾されている場合、コンテナは特定のURIスペースをリッスンするWebSocketサーバーとしてクラスの可用性を保証します

  • @ClientEndpoint:このアノテーションで装飾されたクラスはWebSocketクライアントとして扱われます

  • @OnOpen:新しいWebSocket接続が開始されると、@OnOpenを使用するJavaメソッドがコンテナーによって呼び出されます。

  • @OnMessage:メッセージがエンドポイントに送信されると、@OnMessage,アノテーションが付けられたJavaメソッドがWebSocketコンテナーから情報を受け取ります。

  • @OnError:通信に問題がある場合、@OnErrorのメソッドが呼び出されます

  • @OnCloseWebSocket接続が閉じたときにコンテナによって呼び出されるJavaメソッドを装飾するために使用されます

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仕様は、テキストとバイナリの2つのオンワイヤデータ形式をサポートしています。 APIは、これらの両方の形式をサポートし、仕様で定義されているJavaオブジェクトとヘルスチェックメッセージ(ピンポン)を操作する機能を追加します。

  • Text:任意のテキストデータ(java.lang.String、プリミティブ、またはそれらと同等のラッパークラス)

  • Binary:バイナリデータ(例: java.nio.ByteBufferまたはbyte[](バイト配列)で表されるオーディオ、画像など)

  • Java objects:APIを使用すると、コード内のネイティブ(Javaオブジェクト)表現を操作し、カスタムトランスフォーマー(エンコーダー/デコーダー)を使用して、WebSocketで許可されている互換性のあるオンワイヤー形式(テキスト、バイナリ)に変換できます。プロトコル

  • Ping-Pongjavax.websocket.PongMessageは、ヘルスチェック(ping)要求に応答してWebSocketピアによって送信される確認応答です。

このアプリケーションでは、Java Objects.を使用します。メッセージをエンコードおよびデコードするためのクラスを作成します。

4.1. エンコーダ

エンコーダーはJavaオブジェクトを受け取り、JSON、XML、バイナリー表現などのメッセージとしての送信に適した典型的な表現を生成します。 エンコーダーは、Encoder.Text<T>またはEncoder.Binary<T>インターフェースを実装することで使用できます。

以下のコードでは、エンコードするクラスMessageを定義し、メソッドencodeでは、JavaオブジェクトをJSONにエンコードするためにGsonを使用します。

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オブジェクトに変換するために使用されます。 デコーダは、Decoder.Text<T>またはDecoder.Binary<T>インターフェースを使用して実装できます。

エンコーダーで見たように、decodeメソッドは、エンドポイントに送信されたメッセージで取得されたJSONを取得し、Gsonを使用してMessage:というJavaクラスに変換する場所です。

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. 結論

この記事では、WebSocketのJava APIとは何か、このリアルタイムチャットなどのアプリケーションの構築にどのように役立つかについて説明しました。

エンドポイントを作成するための2つのプログラミングモデル、アノテーションとプログラマティックを見ました。 アプリケーションのアノテーションモデルとライフサイクルメソッドを使用してエンドポイントを定義しました。

また、サーバーとクライアント間でやり取りできるようにするには、JavaオブジェクトをJSONに、またはその逆に変換するエンコーダーとデコーダーが必要であることがわかりました。

JSR 356 APIは非常にシンプルで、WebSocketアプリケーションの構築を非常に簡単にする注釈ベースのプログラミングモデルです。

この例で作成したアプリケーションを実行するには、warファイルをWebサーバーにデプロイし、次のURLにアクセスするだけです。http://localhost:8080/java-websocket/.リポジトリhereへのリンクがあります。