WebSocket用のJavaAPIのガイド
1. 概要
WebSocketは、双方向、全二重、リアルタイムのクライアント/サーバー通信を提供することにより、サーバーとWebブラウザー間の効率的な通信の制限に代わるものを提供します。 サーバーはいつでもクライアントにデータを送信できます。 TCP上で実行されるため、低遅延の低レベル通信も提供し、各メッセージのオーバーヘッドを削減します。
この記事では、チャットのようなアプリケーションを作成して、WebSocket用のJavaAPIを見ていきます。
2. JSR 356
JSR 356またはWebSocket用のJavaAPIは、Java開発者がサーバー側とJavaクライアント側の両方でWebSocketをアプリケーションと統合するために使用できるAPIを指定します。
このJavaAPIは、サーバー側とクライアント側の両方のコンポーネントを提供します。
- サーバー:javax.websocket.serverパッケージのすべて。
- Client : javax.websocket パッケージのコンテンツ。これは、クライアント側のAPIと、サーバーとクライアントの両方に共通のライブラリで構成されています。
3. WebSocketを使用したチャットの構築
非常にシンプルなチャットのようなアプリケーションを作成します。 すべてのユーザーは、任意のブラウザーからチャットを開き、名前を入力し、チャットにログインして、チャットに接続しているすべてのユーザーとの通信を開始できます。
まず、pom.xmlファイルに最新の依存関係を追加します。
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>
最新バージョンはここにあります。
Java Objects をJSON表現に、またはその逆に変換するために、Gsonを使用します。
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.0</version>
</dependency>
最新バージョンは、 MavenCentralリポジトリーで入手できます。
3.1. エンドポイント構成
エンドポイントを構成するには、アノテーション-ベースと拡張ベースの2つの方法があります。 javax.websocket.Endpoint クラスを拡張するか、専用のメソッドレベルのアノテーションを使用できます。 アノテーションモデルはプログラムモデルと比較してよりクリーンなコードにつながるため、アノテーションは従来のコーディングの選択肢になりました。 この場合、WebSocketエンドポイントライフサイクルイベントは次のアノテーションによって処理されます。
- @ServerEndpoint: @ServerEndpointで装飾されている場合、コンテナーは、特定のURIスペースをリッスンするWebSocketサーバーとしてクラスの可用性を保証します
- @ClientEndpoint :このアノテーションで装飾されたクラスは、WebSocketクライアントとして扱われます
- @OnOpen :新しい WebSocket 接続が開始されると、@OnOpenのJavaメソッドがコンテナーによって呼び出されます。
- @OnMessage : @OnMessageで注釈が付けられたJavaメソッド、は、メッセージがエンドポイントに送信されるときにWebSocketコンテナーから情報を受け取ります
- @OnError :通信に問題がある場合、@OnErrorのメソッドが呼び出されます
- @OnClose :WebSocket接続が閉じたときにコンテナーによって呼び出されるJavaメソッドを装飾するために使用されます
3.2. サーバーエンドポイントの作成
@ServerEndpoint でアノテーションを付けることにより、JavaクラスWebSocketサーバーエンドポイントを宣言します。 エンドポイントが展開される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<ChatEndpoint> chatEndpoints
= new CopyOnWriteArraySet<>();
private static HashMap<String, String> 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オブジェクト:APIを使用すると、コード内のネイティブ(Javaオブジェクト)表現を操作し、カスタムトランスフォーマー(エンコーダー/デコーダー)を使用して、互換性のあるオンワイヤ形式(テキスト、バイナリ)に変換できます。 WebSocketプロトコルによる
- Ping-Pong : javax.websocket.PongMessage は、ヘルスチェック(ping)要求に応答してWebSocketピアによって送信される確認応答です。
私たちのアプリケーションでは、
4.1. エンコーダー
エンコーダーはJavaオブジェクトを受け取り、JSON、XML、バイナリ表現などのメッセージとして送信するのに適した一般的な表現を生成します。 エンコーダーは、 Encoder.Text
以下のコードでは、エンコードするクラス 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<Message> {
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
エンコーダーで見たように、 decode メソッドは、エンドポイントに送信されたメッセージで取得されたJSONを取得し、Gsonを使用して Message:というJavaクラスに変換します。
public class MessageDecoder implements Decoder.Text<Message> {
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にアクセスするだけです。