JavaとJavaScriptでwebブラウザとのソケット通信②


背景

過去記事:https://qiita.com/take4eng/items/d0b009c48ee8c3fe420a

上記の過去記事に記述しているように、Javaでサーバープログラムを作成しソケット通信を実施。
⇒ HTTP通信をゴリ押しで解析しているため、無駄に複雑なコードになっている。

Java EEにはsocket通信に関するAPIが多数存在し、非常に簡単に実装することが可能。
既に多くの人がまとめてくれてはいるが、実装した内容をまとめておく。

便利なAPIが用意されているのにゴリ押しで解析なんて誰もやらないよね
そりゃググってもなかなか出てこないわ…

実践内容

  1. WebSocket API の使用方法
  2. APIを使用したサーバープログラムを作成
  3. 過去記事内のクライアントプログラムコードを編集しチャットアプリ作成

WebSocket API の使用方法

基本的なWebSocket APIについて説明する。
ここで紹介するもの以外にも多数のAPIが存在するが、必要ならググると良い。

Endpointクラスの作成

import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/コンテキストパス")
public class SanpleEndpoint {
}
  1. ServerEndpointクラスをインポート
  2. クラスにアノテーション@ServerEndpointを付与
  3. ファイルの場所を示すコンテキストパスを記述

処理メソッドの作成

/*
各クラスをインポートしておく
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
*/

//クライアントと接続したときの処理
@OnOpen
public void onOpen(Session session) {
}

//クライアントからメッセージを受け取ったときの処理
@OnMessage
public void onMessage(String message) {
}

//エラーが発生したときの処理
@OnError
public void onError(Throwable error) {
}

//クライアントと接続が切れたときの処理
@OnClose
public void onClose(Session session) {
}
  1. 必要なクラスをインポート
  2. メソッドに対応した各アノテーションを付与
  3. 引数は必要に応じて変更 , 追加可能

APIを使用したサーバープログラムを作成

サーバープログラム
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

// Webソケットのサーバ側クラスであること表すアノテーション。
// 引数(wSck)はクライアントから接続時、使われるURIを表す。
@ServerEndpoint(value = "/wSck")
public class SocketFree2 {

    //クライアントのセッションスレッドを作成(クライアント毎にそれぞれのセッションを保存)
    //Set:重複要素のないコレクション
    //CopyOnWriteArrayList:java.util.Setをスレッドセーフにしたもの
    private static Set<Session> user = new CopyOnWriteArraySet<>();

    @OnOpen//クライアントと接続したとき
    public void onOpen(Session mySession) {
        System.out.println("connect ID:"+mySession.getId());//session.getId():セッションIDを取得
        user.add(mySession);//クライアント毎のセッションをリストに追加
    }

    @OnMessage//クライアントからデータが送信されたとき
    public void onMessage(String text , Session mySession) {//引数は送信されたテキストと送信元のセッション
        System.out.println(text);
        //getAsyncRemote():RemoteEndpointのインスタンスを取得
        //sendText(String):クライアントにテキストを送信
        for (Session user : user) {
            user.getAsyncRemote().sendText(text);
            System.out.println(user.getId()+"番目に"+mySession.getId()+"番目のメッセージを送りました!");
        }
        if(text.equals("bye")) onClose(mySession);//textが「bye」なら切断する
    }

    @OnClose//クライアントが切断したとき
    public void onClose(Session mySession) {
        System.out.println("disconnect ID:"+mySession.getId());
        user.remove(mySession);//切断したクライアントのセッションをリストから削除
        try {
            mySession.close();//closeメソッドで切断
        } catch (IOException e) {
            System.err.println("エラーが発生しました: " + e);
        }
    }

}

コードの解説

1.クラスにEndpointアノテーション@ServerEndpointを付与し、コンテキストパスを記述
@ServerEndpoint(value = "/wSck")

2.各クライアントを識別するリストを作成
private static Set<Session> user = new CopyOnWriteArraySet<>();

3.onOpenメソッド:クライアントのセッションをリストに追加
user.add(mySession);

4.onMessageメソッド:受信したテキストをそのままクライアントへ送信
⇒ for文で接続しているクライアント全員に送信する
user.getAsyncRemote().sendText(text);

5.onCloseメソッド:接続が切れたクライアントを削除
5-1.userリストから削除:user.remove(mySession);
5-2.セッションを削除し接続を切る:mySession.close();

実行結果

クライアントプログラムのアドレスを変更し、webブラウザで実行。
※コード内容は過去記事を参照
var wSck= new WebSocket("ws://localhost:8080/プロジェクト名/コンテキストパス");
(今回なら"ws://localhost:8080/freeWeb2/wSck")


送信した内容が表示されるチャットアプリが完成。
複数のブラウザからのアクセスにも対応している。

感想

APIを使用することで非常に簡単にソケット通信を行うことができた。
過去記事で記述しのものと比べるとコードの記述量は約1/4。すごい。超簡単。

他にもエンコード、デコード処理やjsonデータの扱いについても簡単にできるらしい。
今回は使用していないが、本格的な開発をするなら必要になるだろう。
詳細は参考ページを参照。

参考ページ