この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは。tomcat7でWebSocketを使ってみました。
今回はcanvasに書いた情報を共有するといことをやりたいと思います。
使用する環境はpleiades3.7.2、Java7になります。またEclipseのtomcatホームの設定はしてあるものとして進めます。
Eclipseでプロジェクトを作成します
「ファイル」>「新規」>「その他」>「Java」からTomcatプロジェクトを選択し、プロジェクトを作成します。
(今回はサブディレクトリにWebContentsを設定して作成しました)
srcフォルダ配下にws.appパッケージを作成します。ここにサーバ側のソースを格納します。
クライアント側のソースはWebContents直下にhtmlファイルを作成します。
以下フォルダ構成になります。
ビルドパスを追加します
tomcatでwebsocketを実装するのにはWebSocketServletを継承してサーブレットを実装する必要があります。
それにはcatalina.jarとtomcat-coyote.jarが必要なのでビルドバスに追加をします。
プロジェクトを右クリックして「プロパティ」から「Javaのビルド・バス」を選択します。ライブラリタブから「変数の追加」ボタンをクリックし、「TOMCAT_HOME」を選択します。次に拡張ボタンをクリックし、「lib」フォルダからcatalina.jarを選択します。同様にtomcat-coyote.jarも追加します。
サーブレットを作成します
ws.app配下にDrawServletクラスを作成します。
package ws.app;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
import org.apache.catalina.websocket.WsOutbound;
@WebServlet(urlPatterns={"/DrawServlet"})
public class DrawServlet extends WebSocketServlet{
private static final long serialVersionUID = 1L;
private static List<DrawMessageInbound> messageList = new ArrayList<DrawMessageInbound>();
private class DrawMessageInbound extends MessageInbound{
WsOutbound drawOutbound;
// 接続時の処理
@Override
public void onOpen(WsOutbound outbound){
System.out.println("open");
this.drawOutbound = outbound;
messageList.add(this);
}
// 接続解除時の処理
@Override
public void onClose(int status){
System.out.println("close");
messageList.remove(this);
}
// メッセージ受信時の処理
@Override
public void onTextMessage(CharBuffer message) throws IOException{
System.out.println("message"+ message);
for(DrawMessageInbound in: messageList){
CharBuffer buffer = CharBuffer.wrap(message);
in.drawOutbound.writeTextMessage(buffer);
in.drawOutbound.flush();
}
}
// メッセージ受信時の処理
@Override
public void onBinaryMessage(ByteBuffer bb) throws IOException{
}
}
@Override
public StreamInbound createWebSocketInbound(String arg0, HttpServletRequest arg1) {
return new DrawMessageInbound();
}
}
ソースについて簡単に説明します。
冒頭にも書きましたが、WebSocketServletを継承する必要があります。アノテーションはURLマッピングのためのものになります。Servlet3.0ではHttpServletクラスを継承していれば、@WebServletアノテーションを書くだけでweb.xmlを記載しなくてもサーブレットを呼び出すことができます。WebSocketServletクラスはHttpServletを継承してているクラスなので記述しておけばweb.xmlがなくても呼び出されます。
@WebServlet(urlPatterns={"/DrawServlet"})
public class DrawServlet extends WebSocketServlet{
}
WebSocketServletクラスを継承するとcreateWebSocketInboundを実装する必要があります。クライアントからの要求があるとこのメソッドが呼び出されます。またWebSoket通信はStreamInboundクラスを返却する必要があります。ここではMessageInboundクラス(StreamInboundを継承したクラス)を継承したDrawMessageInboundクラスを生成して返却しています。
@Override
public StreamInbound createWebSocketInbound(String arg0, HttpServletRequest arg1) {
return new DrawMessageInbound();
}
接続時の処理を記述します。ここでは自身の内部クラスをリストに追加しています。
// 接続時の処理
@Override
public void onOpen(WsOutbound outbound){
System.out.println("open");
this.drawOutbound = outbound;
messageList.add(this);
}
接続解除時の処理を記述します。接続時に追加した自身をリストから削除します。
@Override
public void onClose(int status){
System.out.println("close");
messageList.remove(this);
}
メッセージ受信時の処理を記述します。ここではクライアントから送信されたメッセージ(マウス座標の文字列)を接続された全てに対してメッセージを送信しています。
// メッセージ受信時の処理
@Override
public void onTextMessage(CharBuffer message) throws IOException{
System.out.println("message"+ message);
for(DrawMessageInbound in: messageList){
CharBuffer buffer = CharBuffer.wrap(message);
in.drawOutbound.writeTextMessage(buffer);
in.drawOutbound.flush();
}
}
バイナリメッセージを受信した時の処理を記述します。今回は文字列のみを取り扱うので何も実装しません。
// メッセージ受信時の処理
@Override
public void onBinaryMessage(ByteBuffer bb) throws IOException{
}
HTMLファイルの作成
WebContentsフォルダ直下にindex.htmlを作成します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>canvas</title>
<script>
var ws = new WebSocket("ws://localhost:8080/websocket/DrawServlet");
ws.onopen = function(){
// 接続時の処理
};
var isMouseDown = false;
var canvas;
var context;
window.onload = function() {
canvas = document.getElementById("canvas1");
context = canvas.getContext('2d');
canvas.addEventListener("mousemove", function(event){
mouseX = event.pageX;
mouseY = event.pageY;
if(isMouseDown){
draw(mouseX, mouseY);
ws.send(mouseX + "," + mouseY);
}
});
canvas.addEventListener("mousedown", function(){
isMouseDown = true;
});
canvas.addEventListener("mouseup", function(){
isMouseDown = false;
});
}
// 描画処理
function draw(x, y){
context.beginPath();
context.fillStyle = "#0099ff";
context.arc(x, y, 10, 0, Math.PI*2, false);
context.fill();
}
ws.onmessage = function(message){
// 受信時の処理
var value = message.data.split(",");
draw(value[0], value[1]);
};
// 接続解除
function closeConnect(){
ws.close();
}
</script>
</head>
<body>
<div>
<canvas id="canvas1" width="500" height="400" style="background-color:#999999;">
</canvas>
</div>
<input type="button" value="接続解除" onclick="closeConnect()">
</body>
</html>
クライアント側のソースについて説明をします。
サーバに対して接続を行っています。
var ws = new WebSocket("ws://localhost:8080/websocket/DrawServlet");
ページを開いた時の初期処理を記述しています。ここではcanvasを取得してmousemove、mousedown、mouseupのイベントを登録しています。
window.onload = function() {
canvas = document.getElementById("canvas1");
context = canvas.getContext('2d');
canvas.addEventListener("mousemove", function(event){
mouseX = event.pageX;
mouseY = event.pageY;
if(isMouseDown){
draw(mouseX, mouseY);
ws.send(mouseX + "," + mouseY);
}
});
canvas.addEventListener("mousedown", function(){
isMouseDown = true;
});
canvas.addEventListener("mouseup", function(){
isMouseDown = false;
});
}
mousemoveイベント内ではマウスがクリックされた状態で、マウスが動いた場合にdrawメソッドを呼び出し、線を引きサーバに対してカンマ区切りでマウスのX、Y座標を送信しています。
if(isMouseDown){
draw(mouseX, mouseY);
ws.send(mouseX + "," + mouseY);
}
サーバからの受信時に呼び出されます。受信メッセージをカンマで分割しX、Y座標を取得し描画メソッドを呼び出しています。
ws.onmessage = function(message){
// 受信時の処理
var value = message.data.split(",");
draw(value[0], value[1]);
};
サーバとの接続を解除します。
function closeConnect(){
ws.close();
}
動かしてみましょう
http://localhost:8080/websocketにブラウザからアクセスしてみます。
ブラウザ複数起動させます。canvasに線を引くと別ブラウザにも線が引かれるのが分かります。ローカル環境だとスムーズに描画されるかと思います。 接続解除を押すとサーバとの接続が解除されるので描画が共有されなくなります。
最後に
tomcatを使って簡単にWebSocket通信が出来たかと思います。ただしWebSocketの仕様については仕様が確定していませんので、今後変わる可能性があるみたいです。
Java7EEではWebSocket APIが含まれる予定なので、そうすれば 仕様も確定するのかなと思います。