1概要
WebSocketは、双方向、全二重、リアルタイムのクライアント/サーバー通信を提供することによって、サーバーとWebブラウザー間の効率的な通信の制限に対する代替手段を提供します。サーバーはいつでもクライアントにデータを送信できます。 TCP上で動作するため、低遅延の低レベル通信も提供され、各メッセージのオーバーヘッドが削減されます 。
この記事では、チャットのようなアプリケーションを作成して、WebSocket用のJava APIを見ていきます。
2 JSR 356
JSR 356 またはWebSocket用のJava APIは、Java開発者がWebSocketをアプリケーションと統合するために使用できるAPIを指定します。両方ともサーバー側でJavaクライアント側と同じです。
このJava APIは、サーバーサイドコンポーネントとクライアントサイドコンポーネントの両方を提供します。
-
Server : javax.websocket.server パッケージ内のすべてのもの。
-
Client : javax.websocket パッケージの内容。
クライアント側のAPIとサーバーとクライアントの両方に共通のライブラリ。
3 WebSocketsを使用してチャットを構築する
私たちは非常に単純なチャットのようなアプリケーションを構築します。どのユーザーも、どのブラウザからでもチャットを開き、自分の名前を入力し、チャットにログインして、チャットに接続しているすべてのユーザーと通信を開始できます。
まず、 pom.xml ファイルに最新の依存関係を追加します。
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>
最新版はhttps://search.maven.org/classic/#search%7Cga%7C1%7Cjavax.websocket-api[ここ]にあります。
Javaの Objects をJSON表現に変換したり、その逆の変換をするには、Gsonを使います。
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.0</version>
</dependency>
最新版はhttps://search.maven.org/classic/#search%7Cga%7C1%7Ccom.google.code.gson[Maven Central]リポジトリにあります。
3.1. エンドポイント設定
エンドポイントを設定する方法は2つあります。 __annotation - -basedとextension-based。 javax.websocket.Endpoint__クラスを拡張するか、専用のメソッドレベルの注釈を使用することができます。アノテーションモデルはプログラムモデルと比較してよりクリーンなコードにつながるので、アノテーションはコーディングの従来の選択となっています。この場合、WebSocketエンドポイントライフサイクルイベントは次のアノテーションによって処理されます。
-
@ ServerEndpoint: @ ServerEndpointで装飾されている場合は、 コンテナ
を待機している WebSocket サーバーとしてのクラスの可用性を保証します。 特定のURIスペース ** @ ClientEndpoint :このアノテーションで装飾されたクラスが扱われる
WebSocketクライアントとして ** @ OnOpen : @ OnOpen を持つJavaメソッドがコンテナによって呼び出されます
新しい WebSocket 接続が開始されたとき ** @ OnMessage :__ @ OnMessageというアノテーションを付けたJavaメソッド。
メッセージがに送信されるときの 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<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 :aで表されるバイナリデータ(例:音声、画像など)
java.nio.ByteBuffer または byte[] (バイト配列) ** Javaオブジェクト :APIはネイティブ(Java)で動作することを可能にします。
コード内の表現)とカスタムトランスフォーマーの使用 互換性のあるオンワイヤフォーマットに変換する(エンコーダ/デコーダ) WebSocketプロトコルで許可されている(テキスト、バイナリ) ** ピンポン : javax.websocket.PongMessage は送信された確認通知です。
ヘルスチェック(ping)要求に応答してWebSocketピアによって
このアプリケーションでは、 Java Objectを使用します。 メッセージのエンコードとデコード用のクラスを作成します。
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<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 <T> または Decoder.Binary <T> インタフェースを使用して実装できます。
エンコーダで見たように、 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にアクセスするだけです。
__http://localhost:8080/java-websocket/。リポジトリhttps://github.com/eugenp/tutorials/tree/master/java-websocket[here]へのリンクがあります。