SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

最新ブラウザ「Internet Explorer10」と「HTML5」のAPI紹介(PR)

IE10で動くHTML5アプリ実装例
「Node.jsとSocket.IOを使用したリアルタイムアプリ」

最新ブラウザ「Internet Explorer10」と「HTML5」のAPI情報(3)

  • このエントリーをはてなブックマークに追加

 本連載は、「Developers Summit 2012」(デブサミ2012)において、2月16日に行われた日本マイクロソフト株式会社 春日井 良隆氏によるセッション「次期Internet Explorer、IE10とHTML5 API」(セッション資料はこちら)をもとに、Internet Explorer 10の新機能やHTML5 APIとの関連について、3回に分けて特集します。第3回目の今回は、Node.jsとSocket.IOを使用してリアルタイムアプリを作成してみます。

  • このエントリーをはてなブックマークに追加

はじめに

 Internet Explorer 10(以下、IE10)では、HTML5や関連APIの実装が大きく進みますが、中でもWebSocketに対応することはアプリケーション開発者にとって非常に重要です。

 WebSocketは、HTTPリクエストで確立した接続を「繋ぎっぱなし」にすることで、サーバとクライアントの双方向で効率的な通信を実現する技術です。WebSocketは新しい通信プロトコルです(RFC)が、HTTPと同じポート番号を使用するためファイアウォールを越えやすいのと、JavaScript APIに関する仕様もあらかじめ用意されており、Webアプリケーションとの親和性が非常に高いのも特徴です。

 WebSocketは、IE10だけではなくGoogle Chrome、Firefox、Safariと言ったブラウザでもサポートされています(Operaでは、デフォルトで無効にされていますが、実装は提供されています)。

 ここでは、Developers Summit 2012の「次期Internet Explorer、IE10とHTML5 API」というセッションで使用したWebSocketのサンプルを題材に、WebSocketの可能性について知っていただくことを主眼としています。

 このデモンストレーションのソースコードは、こちらからダウンロードすることができます。

 このサンプルでは、WebSocketのAPIを直接使用するのではなく、Node.jsとSocket.IOを使用して実装しました。Node.jsは最近人気が急上昇しているプラットフォームで、JavaScriptを使用してサーバやスタンドアローンのプログラムを作成することのできる環境です。Socket.IOはNode.jsの上で動作するフレームワークで、WebSocketプロトコルの実装と使いやすいAPIを提供してくれます。

 また、このサンプルはWindows Azure上でホスティングされています。Windows Azureは昨年からNode.jsアプリケーションを実行できるようになりました。

 Node.jsとSocket.IOを使ってこのサンプルの実装を行った結果、そのパフォーマンスの高さと開発生産性に驚きました。こうした、2つの意味での「速さ」についても、この記事から伝われば幸いです。

デモの説明

 今回作成したデモンストレーションは、複数ユーザで同時編集可能な「オンライン黒板」です。

 この黒板の最大の特徴は、複数人で同時編集可能なことです。もちろんその機能の実装にはWebSocket(Socket.IO)を使用しています。

 チョークで書く、黒板消しで消す、と言った操作だけではなく、他のユーザのマウスポインタの位置もリアルタイムに把握することができます。

Canvasを使用したUIの作成

 まず黒板の部分はHTML5 Canvasを利用して実現しています。チョークで書く、黒板消しで消す、マウスポインタの位置を共有するといった機能については、マウス移動のイベントを捕捉して、それぞれの処理に分岐しています。

 その部分のコードを抜粋します。細かいところを省略してしまえば、処理の流れは非常に単純です。

// マウス移動のイベントを捕捉
canvas.mousemove(function(e) {
    // Canvas上での現在のマウス座標を取得
    var curPos = posOnCanvas(e.pageX, e.pageY);
    var currentX = curPos.x;
    var currentY = curPos.y;

    // マウス移動をサーバに送信する
    COMMAND_OPS.mouseMove({...}, true);
    if (!drawing) {
        return;
    }
    // 黒板消しで消す
    if (eracing) {
        COMMAND_OPS.erase({...}, true);
    }
    // チョークで書く
    else {
        COMMAND_OPS.drawLine({...}, true)
    }
    prevX = currentX;
    prevY = currentY;
});

 CanvasのAPIを使用しているコードの例として、黒板に線を書く部分を見てみましょう。線書き出しの開始位置(start)は前回のマウス座標、終了位置(end)は今回のマウス座標となります。

var start = param.start, end = param.end;
// 線のスタイル(色に応じた画像を使用)
ctx.strokeStyle = LINE_PATTERNS[param.color];
ctx.lineWidth = lineWidth; // 線の太さ
ctx.lineJoin = "round"; // 線の結合部分を丸める
ctx.lineCap = "round"; // 線の端を丸める
ctx.beginPath(); // パスの書き出し開始
ctx.moveTo(start.x, start.y); // 開始位置に移動
ctx.lineTo(end.x, end.y); // 終了位置に向けて直線を引く
ctx.stroke(); // パスの書き出し
ctx.closePath(); // パスを閉じる

 ここでは、CanvasのAPIを素直に使用しているだけですので、説明は省きます。

WebSocketによる共有

 そしてこのサンプルの一番のポイントである、Socket.IOを使用したユーザ体験のリアルタイムな共有です。

 このサンプルではマウスポインタが移動した」「黒板消しで消した」「線を書いた」と言った操作をJSON形式でサーバに送信しています。それを受け取ったサーバは、それを送信元以外のクライアントに対してブロードキャストします。各クライアントはそれを受け取ってその操作を黒板に対して実行する、という実装を行っています。

 実際のコードで見てみましょう。図で言うと(1)の処理を行うコードです。

(1)クライアントからサーバへのデータ送信をSocket.IOで行う

 例えばマウスポインタが移動した際、以下のようなコードでsendCommand()というメソッドを呼び出しています。

mouseMove: function(param, share) {
    if (!share) {
        return;
    }
    // サーバへのデータ送信
    sendCommand({
        type : "mouseMove",
        param : param
    });
}

 sendCommand()メソッドは、Socket.IOのAPIを使用しています。クライアント側におけるSocket.IOは、io.connect(URL)によって生成される「ソケット」というオブジェクトを使用します。

var socket = io.connect(location.protocol + '//' + location.host + '/chalkboard');

 ソケットが持つ「emit()」というメソッドを用いて、サーバに対してJSONオブジェクトを送信することができます。第一引数はイベント名で、イベントの種別を識別するために汎用的に利用できます。

sendCommand = function(command) {
    socket.emit('command', command);
};

(2)Node.jsとSocket.IOを使用したサーバプログラム

 次に掲載するコードは、サーバサイドで(2)の処理を実行する箇所です。Node.jsを利用しているため、サーバサイドのコードもJavaScriptで記述しています。

 サーバは、クライアントから送られてきたコマンドを受け取って、送信元以外にブロードキャストします。サーバプログラムの主要部分を以下に掲載します。

const COMMANDS_MAX = 2000;
var commands = [];

// クライアントからの操作ログを配列に保持する
function storeCommand(command) {
    if (commands.length === COMMANDS_MAX) {
        // 古い操作ログは削除する
        commands.shift();
    }
    commands.push(command);
}

// Socket.IOを使用し、/chalkboardというURLを待ち受ける
var sockets = io.of('/chalkboard').on('connection', function(socket) {
    // 累積したコマンドをクライアントに向けて送る
    socket.emit('init', commands);
    // クライアントからコマンドを受け取る
    socket.on('command', function(command) {
        // コマンドにセッションIDを紐付ける
        command.sessionId = socket.id;
        // mouseMoveイベントは保存しない
        if (command.type !== 'mouseMove') {
            storeCommand(command);
        }
        // 送信元以外のクライアントにブロードキャスト
        socket.broadcast.emit('command', command);
    });
    // 接続が切断されたら、全クライアントに通知
    socket.on('disconnect', function() {
        socket.broadcast.emit('leave', socket.id);
    });
});

 中でもポイントとなる部分を抜粋します。なんと、クライアントから受け取ったコマンドを送信元以外のクライアントにブロードキャストする処理を、一行で記述できています。

 Socket.IOのAPIが非常に使いやすいことがお分かりいただけるのではないでしょうか。

// クライアントからコマンドを受け取る
socket.on('command', function(command) {
    ...
    // 送信元以外のクライアントにブロードキャスト
    socket.broadcast.emit('command', command);
});

(3)サーバからコマンドを受け取り、黒板を更新する

 (3)はクライアント側のJavaScriptコードになります。"command"イベントを受け取ってCanvasへの描画を行う処理になりますが、Socket.IOを使用すると以下のようなコードになります。

// "command"イベントの監視
socket.on('command', function(command) {
    ...
    processCommand(command);
});

サンプルのパフォーマンスについて

 このサンプルのコードをもう少し読むと、パフォーマンスを高めるような努力が一切行われていないことがわかります。マウスイベントが発生するたびにサーバに対して送信を行っているため、非常に頻繁にサーバとの通信を行っています。

 しかしWebSocketのパフォーマンスが非常に良いため、これほどシンプルな実装でも、5,6人程度の同時接続数であれば、全く遅延なくアプリを利用できます。今回は本格的なサービスではなく、あくまでデモンストレーションですので、このレベルのパフォーマンスで十分と判断しました。

まとめ

 この記事でお伝えしたかったことは以下の2つです。

  • Node.jsやSocket.IOの生産性の高さ:サンプルのコア部分は、非常にシンプルなコードで実現できています。
  • WebSocketのパフォーマンスの高さ:全くパフォーマンスチューニングを行っていないにもかかわらず、一定のリアルタイム性を備えています。

 Node.jsとSocket.IOを使えば、リアルタイムなアプリケーションを非常に容易に実現できます。そこにHTML5の表現力が加われば、魅力的なアプリケーションを短期間で実装することができます。

 今後やってくるリアルタイムWebのイノベーションに備えて、今からWebSocketに親しんでおくとよいのではないでしょうか。

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/6502 2012/04/18 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング