Weitere ähnliche Inhalte Ähnlich wie WebSocket Chat App Hands On on Microsoft Azure (20) Mehr von Yoshio Terada (20) Kürzlich hochgeladen (11) WebSocket Chat App Hands On on Microsoft Azure1.
Java on Azure ハンズオン
Azure で WebSocket を体験しよう!
Web Apps 編
Version: 1.0
Last updated: 2015/12/22
2.
2
もくじ
概要
.......................................................................................................................................................
3
演習内容
................................................................................................................................................
3
第
1
章:
WebSocket とは
......................................................................................................................
4
第
2
章:
ブラウザから WebSocket サーバ・エンドポイントへの接続
.............................................
8
Microsoft
Azure のアカウント設定、環境設定
....................................................................................
8
WebSocket のクライアント側の実装
.................................................................................................
11
WebSocket サーバ・エンドポイント側の実装
..................................................................................
22
第
3
章:
WebSocket チャット・アプリケーションの作成
...............................................................
35
WebSocket のクライアント側の実装
.................................................................................................
35
WebSocket サーバ・エンドポイント側の実装
..................................................................................
47
第
4
章:
参考⽂文献
.............................................................................................................................
52
3.
3
概要
Microsoft Azure は、マイクロソフトのクラウドプラットフォームで、マイクロソフトが管
理するデータセンターのグローバルネットワーク上で Web サイト ホスティング、クラウ
ド アプリケーション ホスティング (PaaS)、ならびに仮想マシン ホスティング (IaaS) 環
境を提供します。
このハンズオン ラボでは、Microsoft Azure で提供される PaaS 型のサービスの一つであ
る Web Apps を使って WebSocket のサンプル・アプリケーションを実装する方法について
学習します。こんかいは、学生の皆様を対象に WebSocket をかんたんに理解していただく
ため送受信データ(例 JSON データなど)と、Java オブジェクトの変換(マーシャリン
グ・アンマーシャリング)については対象外とします。
演習内容
このハンズオン ラボでは以下のことを学習します。
Ø WebSocket
の概要について理理解する
Ø WebSocket
サーバ・エンドポイントへの接続
Ø WebSocket
チャット・アプリケーションの作成
このハンズオン ラボの所要時間:約 60〜90 分
4.
4
第
1
章:
WebSocket とは
WebSocket(ウェブソケット)は、インターネットの標準化団体である W3C と IETF が
定める通信規格の一つで、W3C が API を、そしてプロトコルは IETF が規定していま
す。WebSocket プロトコルは、HTTP を拡張し、双方向で全二重の通信が可能です。
WebSocket の詳細を説明するまえに、かんたんに、ブラウザ上で Web ページを表示する
ための仕組みを説明します。通常、ブラウザ(HTTP クライアントともいいます)から、Web
サーバと呼ぶ Web ページを管理するサーバに対して接続します。ブラウザには、Internet
Explorer や Microsoft Edge (Windows 10 に含まれる新しいブラウザ)、Firrox、Safari のよう
なさまざまな製品があります。
Web サーバに接続するためには、ブラウザ上で http://で始まる URL をアドレス欄に入力
し、目的のサーバに対して接続します。
通常の、ブラウザと Web サーバとのやりとりは、必ずブラウザ(HTTP クライアント)側
からリクエストを送信し、リクエストに対応するレスポンスを受け取ります。この方法を
利用したデータ交換は伝統的なブラウザと Web サーバ間のデータ交換方法で、今後も多く
の場面でこの方法が利用されます。
上記の方法は多くの場面でうまく動作しますが、必ずブラウザ側からリクエストを送信
しなければ、結果を得られないという課題がありました。これにより、どのような問題が
発生するかを考えてみましょう。例えば、コンサート・チケットの予約サイトに対してア
クセスし、人気のあるアーティストのコンサートのチケットを予約する場合を考えてみま
5.
5
しょう。人気のあるアーティストのチケットの場合、チケットは争奪戦になります。そし
て刻一刻、もっというならば秒単位でチケットの残数は減っていくでしょう。このような
場合、あと何枚チケットが残っているかを確認するために、今まではブラウザを強制的に
リロード(再読み込み)するか、数秒ごとに再読み込みするための HTML のコードを埋め
込む必要がありました。画面全体を再読み込みするのはとても負荷の高い(ネットワーク
帯域や Web サーバに対する負荷)作業になります。実際に、数百人〜数千人の人が同じ
Web サーバに対して定期的に再読み込みをした場合、Web サーバに対しては非常に多くの
負荷がかかり、不要なデータがネットワーク回線上に流れることになります。
そこで、Ajax という JavaScript の技術を利用し、画面全体ではなく、必要なデータの一
部(この場合は、チケットの残数)だけを取得する方法が考えられました。しかし、この
方法も、上記と同様にブラウザ側から定期的に Web サーバに対して確認を行い、状況を取
得します(polling)。この時、たとえデータに変化がない場合でもデータの送受信は発生
します。
こうした問題を解消するために、Web サーバ側でなんらかのイベントが発生した際に、
クライアントに情報を配信するために Reverse Ajax(Comet)という技術がでてきました。
Reverse Ajax (Comet)では2種類の方法を選択することができます。一つ目の方法は、ブラ
ウザと Web サーバを常時接続し、サーバ側でなんらかのイベントが発生した際に、ブラ
ウザに通知する Streaming という方法、そして、もう一つは Long-Polling という方法で、常
時接続ではなく、サーバ側でなんらかのイベントが発生した際にクライアントに通知し、
接続を一旦切断したのち、改めて Web サーバに対して再接続する方法のいずれかの方法を
利用できました。
6.
6
かつて、Reverse Ajax(Comet)を利用した Web サーバ側からの通知に注目を浴びました
が、実際に Reverse Ajax を利用した場合、大量のアクセスに対して処理をさばききれない
問題が発生しました。理由はブラウザと Web サーバ間でのやりとりは HTTP というプロト
コルを利用しているため、HTTP のルールに乗っ取った余分なデータ(HTTP ヘッダ)の情報
交換が必要なためでした。また、標準的な技術ではなかったため、Web サーバごとに実装
をかえなければならない、もしくは Web サーバが用意する API を用いて実装しなければな
らないといったように、アプリケーションを実装する際にも課題を抱えていました。
こうした過去の経験を踏まえ、新たにブラウザ(HTTP クライアント)と Web サーバ
(HTTP サーバ)間で、双方向かつ全二重に通信ができる HTTP を拡張した新しい通信プロト
コルの策定がはじまりました。WebSocket はインターネットの標準化団体である W3C と
IETF が定める通信規格の一つです。これを利用すると、Web サーバ側で発生したなんらか
のイベントに応じて、クライアントであるブラウザに対して情報を通知することができる
ほか、Web ブラウザから Web サーバに対して情報を送信することも可能です。
以降のハンズオン・ラボでは WebSocket を利用した、サンプルのチャット・アプリケー
ションの構築を行います。WebSocket を利用しないチャット・アプリケーションの場合、
ブラウザを再読み込みしなければ他の参加者が記入したメッセージを確認することはでき
ませんでした。しかし WebSocket のチャット・アプリケーションの場合は、ブラウザを再
読み込みしなくても、他の参加者が記入したメッセージを確認することができます。
WebSocket は、チャット・アプリケーションのようなアプリケーションだけでなく、双
方向にデータの交換が必要な場合に幅広く利用できるため、アイディア次第ではとても面
8.
8
第
2
章:
ブラウザから WebSocket サーバ・エンドポイント
への接続
まず、WebSocket のクライアント・エンドポイントからサーバ・エンドポイントに接続
するための実装を記述してみましょう。
Microsoft Azure のアカウント設定、環境設定
学生の皆様は、まず 1,2 のステップを実施して 3, 4 のステップを実施してください。社会
人の皆様は、Microsoft Azure のアカウントを作成後、3, 4 のステップを実施してくださ
い。下記の手順を実施していない場合は、下記を実施した後に以降のハンズオンを進めて
ください。
【学生様向け】
1. DreamSpark プログラムについて
http://yoshio3.com/2015/10/13/about-dreamspark-program/
2. Microsoft Azure for DreamSpark プログラムの登録方法のご紹介
http://yoshio3.com/2015/10/14/config-azure-for-dreamspark/
【共通】
3. Java Web アプリケーション開発の準備 (JDK&NetBeans のインストール)
http://yoshio3.com/2015/10/15/prepare-java-dev-env/
4. Microsoft Azure Web App と NetBeans (Maven) の FTP 連携
http://yoshio3.com/2015/10/16/config-ftp-4-azure-netbeans/
9.
9
とくに、4. の中で、Microsoft Azure で「App Service」を作成しています。作成する際に、
「WebSocket」を有効にしているか否かを再度確認してください。「WebSocket」が有効に
なっているか否かは、下記の Microsoft Azure ポータル管理画面から、「App Service」を選
択し、作成した App Service のインスタンスを確認します。ここで画面上部に「設定」ボ
タンがありますのでボタンを押下すると「アプリケーション設定」を行うリンクがありま
すので、リンクを押下してください。
11.
11
WebSocket のクライアント側の実装
WebSocket のクライアントは、デスクトップでもブラウザからでもクライアント・エンド
ポイントの実装をおこなえば何でもかまいません。
今回は、ブラウザから WebSocket のサーバ・エンドポイントに対して接続する実装を行い
ます。
HTML と JavaScript で実装を行います。
まず、HTML と JavaScript を作成し、ボタンの押下で WebSocket サーバ・エンドポイント
にアクセスする部分を実装します。実際に作成する画面のイメージは下記になります。
起動すると下記の画面が表示されます。
「ログイン」ボタンを押下すると下記の画面が表示されます。
上記を実現するために、index.html ファイルに下記の HTML コードを記述してください。
12.
12
<!DOCTYPE html>
<html>
<head>
<title>WebSocket チャット・アプリケーション</title>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8"/>
<script type="text/javascript" src="js/websocket-chat.js"></script>
</head>
<body>
<div id="title" class="title">
<h2>WebSocket チャット・アプリケーション</h2>
</div>
<div id="login" class="login">
<input id="connect" type="button" value="ログイン"
onClick="connectServerEndpoint();">
</div>
<div id="logout" class="logout">
<input id="close" type="button" value="ログアウト"
onClick="closeServerEndpoint();">
</div>
</body>
</html>
13.
13
次に、上記 HTML を描画した際に画面制御をするための JavaScript コードを作成します。
「Web ページ」ディレクトリ配下に「js」ディレクトリを作成してください。「js」ディレ
クトリを作成するためには、「Web ページ」ディレクトリを右マウスクリックし「新規」
を選んだのち「その他…」を選択してください。
その他を選択すると、下記の画面が表示されます。ここで「カテゴリ(C):」から「その
他」を選び、「ファイル・タイプ(F):」から「フォルダ」を選択し「次 >」ボタンを押下し
てください。
16.
16
ボタンを押下すると「Web ページ」配下に「js」ディレクトリが表示されます。
「js」ディレクトリを作成したのち、このディレクトリ配下に「websocket-chat.js」ファイ
ルを作成します。JavaScript ファイルを作成するために「js」ディレクトリをマウスで右ク
リックし「新規」を選んだのち「その他…」を選択してください。
20.
20
window.attachEvent("onload", init);
} else {
window.onload = init;
}
/* 初期化の処理 */
function init() {
showLoginButton();
}
/* サーバ・エンドポイントとの接続処理 */
function connectServerEndpoint() {
var wsUri = "ws://"
+ document.location.hostname + ":"
+ document.location.port
+ document.location.pathname
+ "chat-server";
// FireFox との互換性を考慮してインスタンス化
if ("WebSocket" in window) {
websocket = new WebSocket(wsUri);
} else if ("MozWebSocket" in window) {
websocket = new MozWebSocket(wsUri);
}
websocket.onopen = function (evt) {
showLogoutButton();
};
websocket.onmessage = function (evt) {
;
};
websocket.onerror = function (evt) {
console.log("WebSocket Error : " + evt);
};
websocket.onclose = function (evt) {
closeServerEndpoint();
};
}
21.
21
/* サーバ・エンドポイントとの切断時の処理 */
function closeServerEndpoint() {
websocket.close(4001, "Close connection from client");
showLoginButton();
}
/* ログインボタンの表示(未切断の時に表示) */
function showLoginButton() {
document.getElementById("login").style.display = "block";
document.getElementById("logout").style.display = "none";
}
/* ログアウトボタンの表示(ログイン後に表示) */
function showLogoutButton() {
document.getElementById("login").style.display = "none";
document.getElementById("logout").style.display = "block";
}
22.
22
WebSocket サーバ・エンドポイント側の実装
つぎに、WebSocket サーバ・エンドポイントの実装を行います。実装をおこなうさい、サ
ーバ側の情報を取得するためにログ出力できるようにします。プロジェクトでログを出力
できるようにするため「pom.xml」 に slf4j の依存関係を追加してください。「プロジェク
ト・ファイル」から「pom.xml」をダブル・クリックしてください。
つぎに、下記にハイライトした行を追加してください。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
27.
27
ビルドすると、ネットワーク経由で必要なファイルを入手し log4j を利用できるようになり
ました。それでは実際に WebSocket のサーバ・エンドポイントを実装します。プロジェク
ト「Azure-Jetty9-WebSocket-Chat」プロジェクトを右クリックし、「新規」を選んだのち
「Java クラス…」を選択してください。メニューに「Java クラス…」が表示されない場合
は、「その他…」を押下し、「カテゴリ(C):」から「Java」を選び「ファイル・タイプ
(F):」から「Java クラス」を選択してください。
28.
28
選択すると下記の画面が表示されます。ここで「クラス名(N):」に「ChatServerEndpoint」
と入力し「終了(F)」ボタンを押下してください。
ファイルが作成されたのち、下記の Java コードを記載してください。
@ServerEndpoint(value = "/chat-server")
public class ChatServerEndpoint {
// ロガーの設定
private final static org.slf4j.Logger logger =
LoggerFactory.getLogger(ChatServerEndpoint.class);
// Jetty の実装バグにより今回は接続済みのセッション管理は
//自身で実装
private static final Set< Session> sessions
= Collections.synchronizedSet(new HashSet<Session>());
@OnOpen
public void onOpen(Session session) throws IOException {
logger.info("onOpen: " + session.getId());
sessions.add(session);
29.
29
logger.info("onOpen Number of sessions: " + sessions.size());
}
@OnClose
public void onClose(Session session) throws IOException {
logger.info("onClose: " + session.getId());
sessions.remove(session);
logger.info("onClose Number of sessions: " + sessions.size());
}
@OnError
public void onError(Throwable p) {
logger.error("WebSocket onError : ", p);
}
}
プログラムを書き終わったのち、プロジェクト「Azure-Jetty9-WebSocket-Chat」プロジェク
トを右クリックし「ビルド」を実行してください。
記載したプログラムの内容に問題がなく、Microsoft Azure に FTP 経由でファイルを転送で
きた場合、下記のように「BUILD SUCCESS」と表示されます。
30.
30
デプロイしたのち、Microsoft Azure の管理ポータル画面にアクセスしてください。ポータ
ルにアクセスしたのち、前回作成した「Web アプリ」を選択します。次に、画面上部の
「ツール」ボタンを押下してください。すると下記の画面が表示されます。
32.
32
ブラウザに入力する URL の例:
http://*****-*****.azurewebsites.net/Azure-Jetty9-WebSocket-Chat
「ログイン」ボタンを押下してください。ボタンを押下すると下記の画面が表示され、ボ
タンの表示が「ログアウト」に変わります。
画面が切り替わる、切り替わらないに関わらず、アプリケーションのログを確認してみま
しょう。画面が切り替わらない場合はなんらかの理由がログに明記されています。また、
正しく動作している場合も、アプリケーション・ログが表示されます。
$ ftp waws-prod-os1-001.ftp.azurewebsites.windows.net
Trying 138.91.24.26...
Connected to waws-prod-os1-001.drip.azurewebsites.windows.net.
220 Microsoft FTP Service
33.
33
Name (waws-prod-os1-
001.ftp.azurewebsites.windows.net:yoterada): yosshi2007-
jetty¥yosshi2007-dep
331 Password required
Password:
230 User logged in.
Remote system type is Windows_NT.
ftp> ls
229 Entering Extended Passive Mode (|||10083|)
125 Data connection already open; Transfer starting.
10-22-15 11:54AM <DIR> LogFiles
10-22-15 11:03AM <DIR> site
226 Transfer complete.
ftp> cd LogFiles
250 CWD command successful.
ftp> ls
229 Entering Extended Passive Mode (|||10085|)
125 Data connection already open; Transfer starting.
10-22-15 11:40AM <DIR> DetailedErrors
10-22-15 11:55AM 4655 eventlog.xml
10-22-15 11:04AM <DIR> http
10-22-15 11:55AM 0
jetty_2015_10_22.stderrout.log
10-22-15 11:03AM <DIR> kudu
10-22-15 11:16AM <DIR> SiteExtensions
10-22-15 11:40AM <DIR> W3SVC647188608
226 Transfer complete.
ftp>
34.
34
ftp> ls
229 Entering Extended Passive Mode (|||10094|)
125 Data connection already open; Transfer starting.
10-22-15 11:40AM <DIR> DetailedErrors
10-22-15 11:55AM 4655 eventlog.xml
10-22-15 11:04AM <DIR> http
10-22-15 11:57AM 0 jetty_2015_10_22.stderrout.log
10-22-15 11:55AM 1485
jetty_2015_10_22.stderrout.log.115749862
10-22-15 11:03AM <DIR> kudu
10-22-15 11:16AM <DIR> SiteExtensions
10-22-15 11:40AM <DIR> W3SVC647188608
226 Transfer complete.
ftp> get jetty_2015_10_22.stderrout.log.115749862
local: jetty_2015_10_22.stderrout.log.115749862 remote:
jetty_2015_10_22.stderrout.log.115749862
229 Entering Extended Passive Mode (|||10093|)
125 Data connection already open; Transfer starting.
100%
|**********************************************************************
******************| 1485 93.97 KiB/s 00:00 ETA
226 Transfer complete.
1485 bytes received in 00:00 (62.74 KiB/s)
ftp> quit
221 Goodbye.
正常に動作している場合、下記のようなログが出力されます。下記は「ログイン」、「ロ
グアウト」ボタンをそれぞれ4回実行した時の出力例です。
2015-10-22 11:55:23.848:INFO:oejs.Server:main: jetty-9.1.0.v20131115
2015-10-22 11:55:23.895:INFO:oejdp.ScanningAppProvider:main: Deployment
monitor [file:/D:/home/site/wwwroot/webapps/] at interval 1
2015-10-22 11:55:25.192:INFO:oejsh.ContextHandler:main: Started
o.e.j.w.WebAppContext@1fcfece{/Azure-Jetty9-WebSocket-
JSF,file:/D:/local/Temp/jetty-127.0.0.1-17537-Azure-Jetty9-WebSocket-
JSF-1.0-SNAPSHOT.war-_Azure-Jetty9-WebSocket-JSF-1.0-SNAPSHOT-any-
2705390846338938535.dir/webapp/,AVAILABLE}{D:¥home¥site¥wwwroot¥webapps
¥Azure-Jetty9-WebSocket-JSF-1.0-SNAPSHOT.war}
35.
35
2015-10-22 11:55:25.301:INFO:oejsh.ContextHandler:main: Started
o.e.j.w.WebAppContext@4718df{/,file:/D:/home/site/wwwroot/webapps/ROOT/
,AVAILABLE}{D:¥home¥site¥wwwroot¥webapps¥ROOT}
2015-10-22 11:55:25.411:INFO:oejs.ServerConnector:main: Started
ServerConnector@1dc3715{HTTP/1.1}{127.0.0.1:17537}
[qtp26111414-61] INFO com.yoshio3.ChatServerEndpoint - onOpen:
websocket-1
[qtp26111414-58] INFO com.yoshio3.ChatServerEndpoint - onClose:
websocket-1
[qtp26111414-62] INFO com.yoshio3.ChatServerEndpoint - onOpen:
websocket-2
[qtp26111414-61] INFO com.yoshio3.ChatServerEndpoint - onClose:
websocket-2
[qtp26111414-58] INFO com.yoshio3.ChatServerEndpoint - onOpen:
websocket-3
[qtp26111414-62] INFO com.yoshio3.ChatServerEndpoint - onClose:
websocket-3
[qtp26111414-61] INFO com.yoshio3.ChatServerEndpoint - onOpen:
websocket-4
[qtp26111414-58] INFO com.yoshio3.ChatServerEndpoint - onClose:
websocket-4
仮に、アプリケーションの動作がおかしいと感じた場合、サーバ側でのアプリケーション
の動作確認を行いたい場合、上記のように FTP コマンドを実行してログを確認してくださ
い。
第
3
章:
WebSocket チャット・アプリケーションの作成
上記で、クライアントのブラウザから WebSocket のサーバ・エンドポイントに対して接続
ができるようになりましたので、続いて WebSocket のチャット・アプリケーションを作成
します。
WebSocket のクライアント側の実装
アプリケーションの動作イメージは下記になります。ログイン画面でログイン名を入力し
て「Connect」ボタンを押下します。
37.
37
ログイン画面、チャット画面を作成するために、index.html ファイルに対して下記の
HTML を記載してください。
<!DOCTYPE html>
<html>
<head>
<title>WebSocket チャット・アプリケーション</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<style type="text/css">
</style>
<link href="./css/ws-chat.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="js/websocket-chat.js"></script>
</head>
<body>
<div id="title" class="title"><h2>WebSocket チャット・アプリケーシ
ョン</h2></div>
<div id="login" class="login">
ログインユーザ名:<input id="name"/>
<input id="connect" type="button" value="Connect"
onClick="connectServerEndpoint();">
</div>
<div id="chat" class="chat">
38.
38
<input id="inputMessage">
<input id="submitMessage" type="button"
onclick="submitMessage();" value="メッソージ送信"><br>
<input id="close" type="button" value="切断"
onClick="closeServerEndpoint();">
<div id="messages" class="messages">
<div>チャット・メッセージ</div>
<div id="insertpos" class="insertpos"></div>
</div>
</div>
</body>
</html>
追加・修正したコードはハイライトした箇所です。<div id=”chat”></div>で示した箇所は、
下記に示す websocket-chat.js の JavaScript コードに従い、WebSocket のサーバ・エンドポイ
ントに接続するまでは表示されません。
サーバに接続したのち、「テキスト・フィールド」、「メッセージ送信」ボタン、「切
断」ボタンなどが表示され、逆に<div id=”login”></div> で示す、「ログインユーザ名」の
入力フィールドなどが非表示となります。
39.
39
上記のような振る舞いを JavaScript で実装します。websocket-chat.js ファイルを下記のよう
に修正してください。
var websocket = null;
var username = null;
if (window.addEventListener) { //for W3C DOM
window.addEventListener("load", init, false);
} else if (window.attachEvent) { //for IE
window.attachEvent("onload", init);
} else {
window.onload = init;
}
/* 初期化の処理 */
function init() {
//起動時は chat 部分は非表示
showLoginButton();
}
/* サーバ・エンドポイントとの接続処理 */
function connectServerEndpoint() {
var wsUri = "ws://"
+ document.location.hostname + ":"
40.
40
+ document.location.port
+ document.location.pathname
+ "chat-server";
// FireFoxとの互換性を考慮してインスタンス化
if ("WebSocket" in window) {
websocket = new WebSocket(wsUri);
} else if ("MozWebSocket" in window) {
websocket = new MozWebSocket(wsUri);
}
//接続が完了し画面切り替え(Chat のメッセージ部分を表示)
showChatMessage();
websocket.onopen = function (evt) {
username = document.getElementById("name").value;
sendMessage(username + " さんが参加しました。");
};
websocket.onmessage = function (evt) {
writeToScreen(evt);
};
websocket.onerror = function (evt) {
console.log("WebSocket Error : " + evt);
};
websocket.onclose = function (evt) {
closeServerEndpoint();
};
}
/* サーバ・エンドポイントとの切断時の処理 */
function closeServerEndpoint() {
sendMessage(username + " さんが退出しました。");
websocket.close(4001, "Close connection from client");
showLoginButton();
}
/* サーバ・エンドポイントにメッセージ送信 */
function sendMessage(message) {
websocket.send(message);
}
/* チャット・メッセージの送信 */
41.
41
function submitMessage() {
msg = document.getElementById("inputMessage").value;
sendMessage(username + " : " + msg);
}
/* テーブルにメッセージの書き込み */
function writeToScreen(evt) {
var element = document.createElement('div');
element.className = "message";
element.textContent = evt.data;
element.style.backgroundColor = "white";
//メッセージの挿入位置(最新情報を先頭に記載)
var objBody = document.getElementById("insertpos");
objBody.insertBefore(element, objBody.firstChild);
// body要素にdivエレメントを追加
}
/* ログインボタンの表示(切断時) */
function showLoginButton() {
document.getElementById("login").style.display = "block";
document.getElementById("chat").style.display = "none";
}
/* チャット領域の表示(ログイン時) */
function showChatMessage() {
document.getElementById("login").style.display = "none";
document.getElementById("chat").style.display = "block";
}
この JavaScript のコードには、WebSocket サーバ・エンドポイントへの接続、切断、メッセ
ージ送信、メッセージ受信時のそれぞれの処理がすべて含まれています。
まずは、接続時に関する変更箇所を確認してみましょう。
var websocket = null;
var username = null;
websocket.onopen = function (evt) {
username = document.getElementById("name").value;
sendMessage(username + " さんが参加しました。");
};
… 中略
/* サーバ・エンドポイントにメッセージ送信 */
42.
42
function sendMessage(message) {
websocket.send(message);
}
接続時、テキスト・フィールドに入力された名前(文字列)を、
document.getElementById("name").value で取得し sendMessage()を呼び出しています。
sendMessage()では WebSocket のサーバ・エンドポイントに対して「username さんが参加し
ました。」というメッセージを送信しています。
//接続が完了し画面切り替え(Chat のメッセージ部分を表示)
showChatMessage();
…
中略
/* チャット領域の表示(ログイン時) */
function showChatMessage() {
document.getElementById("login").style.display = "none";
document.getElementById("chat").style.display = "block";
}
続いて、WebSocket のサーバ・エンドポイントと接続したのち、showChatMessage()のメソ
ッドを呼び出し、チャット領域の表示を行っています。ここではログイン領域を非表示
(“none”)にし、チャット領域を表示(“block”)にしています。
/* チャット・メッセージの送信 */
function submitMessage() {
msg = document.getElementById("inputMessage").value;
sendMessage(username + " : " + msg);
}
画面が切り替わったのち、テキスト・フィールドに対して文字を入力し、「メッセージ送
信」ボタンを押下すると、submitMessage()が呼び出されます。ここでは、入力した人物
(ログインした人物)を特定するために、「username : 入力したメッセージ」を
WebSocket サーバ・エンドポイントに対して送信します。
/* サーバ・エンドポイントとの切断時の処理 */
function closeServerEndpoint() {
sendMessage(username + " さんが退出しました。");
websocket.close(4001, "Close connection from client");
43.
43
showLoginButton();
}
「切断」ボタンを押下し、WebSocket のサーバ・エンドポイントとの切断する時には、
closeServerEndpoint()を呼び出します。ここでは誰が退出したのかを他の方に知らせるため
に「username さんが退出しました。」のメッセージを送信しています。
最後に、WebSocket のサーバ・エンドポイントからメッセージを受信した際の関連処理を
記載します。
websocket.onmessage = function (evt) {
writeToScreen(evt);
};
/* テーブルにメッセージの書き込み */
function writeToScreen(evt) {
var element = document.createElement('div');
element.className = "message";
element.textContent = evt.data;
element.style.backgroundColor = "white";
//メッセージの挿入位置(最新情報を先頭に記載)
var objBody = document.getElementById("insertpos");
objBody.insertBefore(element, objBody.firstChild);
// body要素にdivエレメントを追加
}
WebSocket のサーバ・エンドポイントからメッセージを受信した場合、JavaScript では
websocket.onmessage()で処理を行います。onmessage()では、writeToScreen()を呼び出してい
ます。この writeToScreen()メソッドの中では、<div id=”insertpos”>の先頭行
(objBody.firstChild)に、受信したメッセージを動的に追加(insertBefore)しています。
writeToScreen()で作成される HTML は下記のコードになります。
<div id="insertpos" class="insertpos">
<div class="message" style="background-color: white;">ユーザ名 : メッセージ
</div>
続いて、画面のデザインを整えるため、CSS を作成します。
44.
44
NetBeans のメニューから「Web ページ」を選択後、「右クリック」し「新規」→「フォル
ダ」を選択してください。選択すると下記の画面が表示されます。ここで、「フォルダ名
(N):」に「css」と入力したのち「終了(F)」ボタンを押下してください。
続いて、作成した「css」ディレクトリを選択後、「右クリック」し「新規」→「その他
…」を選択してください。
46.
46
ボタンを押下すると下記の画面が表示されます。ここで「ファイル名(N):」に「ws-chat」
と入力し、最後に「終了(F)」ボタンを押下してください。
ws-chat.css ファイルが作成されたのち、下記の内容を記載してください。下記スタイルシ
ートの内容は、自身の好みにあわせて適宜修正してください。
/*
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
*/
/*
Created on : 2015/11/10, 16:11:05
Author : Yoshio Terada
*/
.title{
position:relative;
margin:0 auto;
width:480px;
overflow:hidden;
47.
47
}
.login{
position:relative;
margin:0 auto;
width:480px;
overflow:hidden;
}
.chat{
position:relative;
margin:0 auto;
width:480px;
overflow:hidden;
}
.chat .messages{
width:100%;
height:480px;
overflow:hidden;
}
.chat .messages:hover{
overflow-y:scroll;
}
.chat .messages > div{
padding:15px;
border-bottom:1px dashed #999;
}
以上で、WebSocket クライアント・エンドポイントの実装は完了です。
WebSocket サーバ・エンドポイント側の実装
次に、WebSocket サーバ・エンドポイントのプログラムを実装しましょう。前回のコード
に対して追加するコードは onMessage()メソッドの部分になります。前回は接続、切断がで
きましたが、onMessage()を実装し、クライアント・エンドポイントから送信されたメッセ
ージを受信し、それに対する処理ができるようになります。
/*
* Copyright 2015 Yoshio Terada
*
48.
48
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.yoshio3;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
/**
*
* @author Yoshio Terada
*/
@ServerEndpoint(value = "/chat-server")
public class ChatServerEndpoint {
// Jetty の実装バグにより接続済みのセッション管理は自身で実装
private static final Set< Session> sessions
= Collections.synchronizedSet(new HashSet<Session>());
@OnOpen
49.
49
public void onOpen(Session session) throws IOException {
sessions.add(session);
}
@OnMessage
public void onMessage(String message, Session session) throws IOException {
//Jetty 9.1.0.v20131115 では Java SE 8 のコードを記載できないので
//Java SE 7 でコードを記述 (Lambda 式で書けない)
//Jetty の実装バグにより接続済みのセッション管理は自身で実装
//Set<Session> sessions = session.getOpenSessions();
//この結果が null を返し NullPointerException が発生
//おそらく、こちらのバグ
//https://bugs.eclipse.org/bugs/show_bug.cgi?id=422192
for (Session sess : sessions) {
if (sess.isOpen()) {
sess.getBasicRemote().sendText(message);
}
}
}
@OnClose
public void onClose(Session session) throws IOException {
sessions.remove(session);
}
@OnError
public void onError(Throwable p) {
System.out.println("onError " + p.getMessage());
}
}
ここでは、Set<Session> sessions に格納されている全クライアント・エンドポイントに対し
て、sess.getBasicRemote().sendText(message)を呼び出し、受信したメッセージをテキストと
して送信しています。
52.
52
以上で、WebSocket を利用したチャット・アプリケーション作成のハンズオンは終了で
す。今回作成したチャット・アプリケーションを改造して、ぜひ面白い WebSocket アプリ
ケーションを作成してみてください。
第
4
章:
参考⽂文献
WebSocket の標準化に関する情報へのリンク
RFC 6455 WebSocket Protocol https://tools.ietf.org/html/rfc6455
W3C The WebSocket API http://www.w3.org/TR/2011/WD-websockets-20110929/
JSR 356: Java™ API for WebSocket
https://jcp.org/en/jsr/detail?id=356
Java EE 7 を使用した WebSocket の解説、サンプル記事などへのリンク
SlideShare Java EE 7 Detail http://www.slideshare.net/OracleMiddleJP/java-ee-7-detail/142
SlideShare Java EE 7 ハンズオン http://yoshio3.com/2013/10/23/java-ee-7-hol-on-jjug-ccc/
ブログ: Twitter タイムライン http://yoshio3.com/2012/11/12/websocket-twitter-timeline-
sample/
ブログ:WebSocket Mailer http://yoshio3.com/2013/12/20/java-ee-7-websocket-anti-pattern/
以上