TECHBLOGスキルブログ

WebSocketで個別送信をする

2015.11.02

経緯

社内で使用する簡易な採点システムを作成するのに、WebSocketを使用しました。
その中で、特定のクライアントにだけメッセージを送信したい、と思ったのですが、意外とWebSocketを
紹介しているサイトで例を見かけなかったので、紹介してみようと思います。
なお、本記事では、サーバ側の処理(コード)を紹介します。
クライアント側については、必要な機能には触れていますが、処理(コード)までは言及しません。

採点システム

今回作成した採点システムについてです。

概要

プレゼンのリアルタイム採点システムです。
採点画面・点数表示画面があり、システムを利用する際にサーバに接続を行います。
スクリーンにプレゼン資料と点数表示画面を表示します。採点者は手元の端末に採点画面を表示します。
採点画面には、採点ボタンとスクリーンに表示されている点数表示画面と同じ点数が表示されます。
採点ボタンが1回押される度に、点数が1点加算されます。採点者は、決められた持ち点まで好きな
タイミングで採点する事が出来、採点数が持ち点に達すると採点ボタンは押下出来なくなります。
また、後で振り返る為に、誰が何時採点したかをログに記録します。

仕様

1.クライアント接続時

  1. ログイン画面にて、IDを入力する。
  2. IDをクエリ文字列に設定し、サーバに接続要求を行う。
  3. サーバにて、クエリ文字列で受け取ったIDをキーに、セッションをマップに格納する。
  4. IDにより、採点画面又は点数表示画面を表示する。

2.メッセージ送信時

specification

  1. 採点画面:採点ボタン押下時、IDと採点数をサーバに送信する。
  2. サーバ:マップから保持しているセッションを順に取得する。
  3. サーバ:得点を各クライアントに送信する。
    点数表示画面・採点画面:点数の表示を更新する。
  4. サーバ:セッションを保持しているマップから、採点画面より受信したIDのセッションを取得する。
  5. サーバ:採点画面より受信した採点数に1加算した値を新しい採点数として、採点画面に送信する。
    採点画面:サーバから受信した採点数が持ち点に達していた場合、採点ボタンを押下不可とする。
  6. サーバ:採点画面より受信したIDをログに出力する。

開発環境

  • Java 1.8.0_40
  • Tomcat 8.0.20

ちなみに、JavaEEのWebSocket APIを使用していますが、その他の実装でも流用可能かと思います。

実装方法

一般的に紹介されている一斉送信のみを行う場合の実装方法と、今回作成した個別送信も行う場合の
実装方法を紹介します。

一斉送信

色々なサイトで紹介されている、接続しているクライアント全てにメッセージを送信するパターンです。

ここでの動きは、以下の通りです。

  1. クライアントはサーバにメッセージを送信する。
  2. サーバは接続している全てのクライアントに受信したメッセージを送信する。

SendAll

 

では、実際のコードを見てみます。

まずは、クライアントからの接続リクエストに応答するonOpenイベントハンドラの処理です。

private static List sessionList = new ArrayList();

@OnOpen
public void onOpen(Session session) {
    sessionList.add(session);
}

4行目のonOpen()の引数として受け取ったセッションを、5行目でリストに格納しています。

続いて、クライアントからのメッセージに応答するonMessageイベントハンドラの処理です。

@OnMessage
public void onMessage(String message) throws IOException {
    for (Session session : sessionList) {
        session.getBasicRemote().sendText(message);
    }
}

3行目でリストからセッションを1つづつ取り出し、4行目でセッションを使用してメッセージを送信しています。

個別通信

つづいて、本題の特定のクライアントに、メッセージを送る方法です。

一斉送信のみの場合、リストでセッションを保持していましたが、マップにてセッションを保持する様にします。
マップに保存する際のキーとして、今回はログイン時に入力したIDを使用します。WSプロトコルが持つ
Sec-WebSocket-Keyなどもキーの候補となりますが、システムにて自動生成された値ではログに出力し後で
確認する、という要件を満たせない為に今回は使用していません。

では、コードを見てみましょう。

まずは、クライアントからの接続リクエストに応答するonOpenイベントハンドラの処理です。

private static Map sessionMap = new HashMap();

@OnOpen
public void onOpen(Session session) {
    sessionMap.put(session.getQueryString(),session);
}

4行目でクエリ文字列を取得し、5行目でキーとしてonOpen()の引数として受け取ったセッションを、マップに
格納しています。

続いて、クライアントからのメッセージに応答するonMessageイベントハンドラの処理です。
採点画面から送信されるメッセージは
“id,採点数”
の形式とします。

private static Map sessionMap = new HashMap();
private int score = 0;

@OnMessage
public void onMessage(String message) throws IOException {
    String[] splitMessage = message.split(",");
    score++;

    for (Session session : sessionMap.values()) {
        session.getBasicRemote().sendText(score);
    }

    Session sessionSender = sessionMap.get(splitMessage[0]);
    scoreMap.put(sessionSender.getQueryString(), splitMessage[1]);
    sessionSender.getBasicRemote().sendText(Integer.parseInt(splitMessage[1]) + 1);

}

9~11行目で、マップから順にセッションを取り出し、全てのクライアントに得点を送信しています。
その後、13~15行目で、マップからメッセージを送信した採点画面のセッションを取得し、受信した採点数に
1点加算した値を送信しています。

公開

記事の主題から少し外れますが、外部に実際に公開をする際の注意事項です。

APサーバ

APサーバにはTomcat8.0を使用しました。Tomcat7からWebSocketに対応しています。

HTTPサーバ

HTTPサーバには、Nginxを使用しました。WebSocketはコネクションの確立にはHTTPプロトコルを
使用し、その後WSプロトコルにプロトコルを切り替えて通信を行います。この際、HTTP1.1のUpgradeヘッダを
使用しています。Nginxにて、Upgradeヘッダに対応したのは1.3.13の為、これ以降のバージョンが必要と
なります。Linuxの一部のディストリビューションでは、yumに古いバージョンのリポジトリが設定されている
場合があるようです。最新のバージョンのリポジトリを設定して、インストールする様に気を付けてください。

まとめ

簡単にですが、WebSocketにて特定の相手にメッセージを送る方法を紹介させていただきました。
手法としては、セッションの管理をリストからマップに変更するという、非常にシンプルな方法を取っています。
後は作成するシステムの要件に合わせて、マップのキーに何を使用するかを使い分けていけば良いかと思います。

              

OTHER CONTENTSその他のコンテンツ

UNITRUST会社を知る

  • 私たちについて

  • 企業情報

SERVICE事業内容

  • システム開発

  • 自社サービス

CONTACT
お問い合わせ

あなたの「想い」に挑戦します。

どうぞお気軽にお問い合わせください。

受付時間:平日9:00〜18:00 日・祝日・弊社指定休業日は除く

お問い合わせ