Aerial(エアリアル) - Ajax/Cometの次を行く リアルタイム双方向RPC

JavaScript - サーバー間で双方向のRPC通信を行う技術「Aerial」(エアリアルという名前になりました*1。アイディアを出していただいた皆様、ありがとうございましたm(_ _)m


Aerialは、通信にFlashを使い、JavaScriptとサーバープログラムとの間で双方向のRPC呼び出しを行う技術です。つまり、サーバー側からJavaScriptのメソッドを呼び出したり、逆にJavaScriptからサーバー側のプログラムを呼び出したりします。



サーバーから直接JavaScriptのコードを呼び出したり、逆にJavaScriptからサーバー側のメソッドを呼び出したりできるので、通信の内容を意識する必要がなく、バグの混入を抑えます。RPC成分入り!

ライブラリを開発するときも、HTTPやブラウザ間の実装の違いを意識する必要も無く、ごく普通のTCP接続で通信を行うので、Cometと比べてよりさわやかで簡潔に実装できます。


Aerialを使って、Shoutter!というデモシステムを作ってみました。より速くタイプすればするほど文字が大きく表示されます。シャウトした文字は、ウェブサイトに接続しているすべてのユーザーで同じように見えます。


BlazeよりBreeze - Aerialサーバーライブラリ

BreezeRubyによるAerialサーバーの実装です。燃えるような気合いを発揮しなくても、スクリプト言語を使って気軽に開発を始められます。そよ風にふかれるような心地よいプログラミングを。


Breezeを使うと、簡単なチャットサーバーは↓このように書けます。

require 'breeze'

# クライアントの一覧
$clients = []

# Breeze::Handlerを継承したクラス
# クライアント毎にインスタンスが作成される
class AerialChatClient < Breeze::Handler

  def initialize
    each_client {|c|
      # クライアントの"joinUser"メソッドを呼び出す
      c.proxy.joinUser
    }
    $clients.push(self)
  end

  # unbindは接続が切断されたときに呼ばれる
  def unbind
    $clients.delete(self)
    each_client {|c|
      # クライアントの"leaveUser"メソッドを呼び出す
      c.proxy.leaveUser
    }
  end

  # RPCハンドラ
  # msgを受け取って、すべてのクライアントに中継する
  def message(msg)
    each_client {|c|
      # クライアントの"addMessage"メソッドを呼びだす
      c.proxy.addMessage(msg)
    }
  end

  # RPCハンドラ
  # クライアントの数を返す
  def getClientCount
    $clients.length
  end

  private
  def each_client
    $clients.reject! {|c|
      begin
        yield c
        false
      rescue
        puts "Error: #{$!}"
        true
      end
    }
  end

end

# サーバーを開始
Breeze.start :Handler => AerialChatClient, :Port => 11500, :PolicyFile => File.read("crossdomain.xml")

ほんのり漂うRPCの香り。サーバーからJavaScript側のメソッドを直接呼び出せます。


Breezeはライブラリ自体も非常にコンパクトで、わずか300行足らずで実装されています。ソースコードCodeReposにあるので、どんどんいじってください!

それから、Breezeを使った簡単なサンプルプログラムをパッケージにしてみました。Aerialを使ったシンプルなチャットシステムです。

ダウンロードして展開し、中に入っている./start.shを実行してください。11500番ポートでAerialチャットサーバが、11600番ポートでHTTPサーバー(WEBrick)が動き出します。http://localhost:11600/にアクセスするとデモシステムを使えます。
※実行するにはRubyの'json'ライブラリと'eventmachine'ライブラリが必要です。rubygemsでインストールしてください:gem install json && gem install eventmachine


このサンプルプログラムを改造するところから始めると、スムーズに開発を始められます。ぜひ使ってみてください。

aerial.js - Aerialクライアントライブラリ

aerial.jsはAerialクライアントライブラリの実装です。

先ほどのチャットサーバーに対応するクライアントプログラムは↓こんな感じで書けます。少ないコードでも、バグ・無駄のない、本来の機能に。

<!-- head -->
<!-- swfobject 2.0 と aerial.js を読み込む -->
<script type="text/javascript" src="lib/swfobject.js"></script>
<script type="text/javascript" src="lib/aerial.js"></script>

<!-- body -->
<div id="aerial_swf"></div> <!-- ここにswfがロードされる -->
<script type="text/javascript">
    // swfをロード
    var aerial = new Aerial("path/to/Aerial.swf", "aerial_swf", function(){
        // 明示的にセキュリティポリシーをロード
        aerial.loadPolicyFile("127.0.0.1", 11500);

        // サーバーに接続
        //   connected  接続が成功したときに呼ばれる
        //   failed     接続が失敗したときに呼ばれる
        //   closed     接続が成功した後、切断されると呼ばれる
        aerial.connect("127.0.0.1", 11500, connected, failed, closed);

        // RPCメソッドを登録
        aerial.setHandler("addMessage", addMessage);
        aerial.setHandler("joinUser",   joinUser);
        aerial.setHandler("leaveUser",  leaveUser);
    });

    function connected() { updateUserCount();               }

    function failed(msg) { alert("connect failed: " + msg); }

    function closed()    { alert("connection closed");      }


// public RPC functions:
    function addMessage(msg) {
        var li = document.createElement("li");
        li.textContent = msg;
        li.innerText = msg;
        document.getElementById("comments").appendChild(li);
    }

    function joinUser()  { updateUserCount(); }

    function leaveUser() { updateUserCount(); }


// private functions:
    // 接続しているユーザーの数を取得して表示する
    function updateUserCount() {
        // サーバー側の"getClientCount"メソッドを呼び出し、
        // 返り値をHTMLに書き込む
        //   引数:なし
        //   コールバック関数:getClientCount()の返り値をHTMLに書き込む
        aerial.call("getClientCount", [], function(count){
            var p = document.getElementById("users");
            p.textContent = count.toString() + " users";
            p.innerText   = count.toString() + " users";
        });
    }

    function submitMessage() {
        var name = document.getElementById("name");
        var body = document.getElementById("body");
        if(body.value != "") {
            var msg = body.value + " by " + name.value;

            // サーバー側の"message"メソッドを呼び出す
            //   引数:msg
            //   コールバック関数無し(返り値を受け取らない)
            aerial.call("message", [msg]);

            body.value = "";
        }
        return false;
    }
//]]>
</script>

<!-- 入力フォーム -->
<form onsubmit="return submitMessage();">
    <input type="text" name="name" id="name" value="&lt;name&gt;" />
    <input type="text" name="body" id="body" value="&lt;comment&gt;" />
    <input type="submit" value="submit" />
</form>

<p id="users"></p> <!-- ここに接続しているユーザーの数を表示 -->
<ul id="comments"></ul> <!-- ここに発言を書き込んでいく -->

RPCで直接サーバー側のコードを呼び出せるので、とても直感的にサーバーとメッセージをやりとりできます。


aerial.jsもCodeReposソースコードがあります。


一部にActionScriptのプログラムが入っているのでコンパイルするにはFlex SDKが必要です。aerial-chat.tar.gzの中にコンパイル済みのswfファイル(aerial-chat/www/lib/Aerial.swf)が入っています。

aerial-multicast.js - クライアント間通信API

aerial-multicast.jsを使うと、サーバー側のコードを一切書かずにJavaScriptを書くだけでリアルタイム通信を行うことができます。あたかもクライアントとクライアントが直接通信しているかのようにプログラムを書けます。


APIには大きく3種類あり、unicastbroadcastmulticastがあります。

broadcastは接続しているすべてのユーザーにメッセージを送ります。このときに受信側は誰からメッセージが送られてきたかを知ることができ、そのユーザーにunicastを送ることができます。

また、joinメソッドを呼び出してグループに参加すると、そのグループ宛のmulticastを受け取れるようになります。leaveメソッドを呼び出すとグループから外れます。

aerial.leave(old_room);
aerial.join(room);
aerial.multicast(room, "enterRoom", [nick_name]);

function enterRoom(from, nick_name) {
    alert("<new user> "+nick_name);
}


実験的な実装ですが、デモシステムを作ってみました。

「部屋」が付いたチャットシステムです。部屋ごとにmulticastグループが割り当てられており、発言をmulticastでやりとりしてます。
最初は"center"というmulticastグループに属しています。入力フォームにユーザー名と部屋名を入れてjoinをクリックすると、別のmulticastグループに移動できます。


aerial-multicast.jsもaerial-chat.tar.gzの中に入っています(aerial-chat/aerial.js/lib/aerial-multicast.js)。またaerial-multicast.js用のサーバープログラムも入っています(aerial-chat/breeze/start.rb)。

Aerialを使ってできそうなこと

  • Aerialなチャット
  • Processing.jsを使って、マルチユーザーリアルタイムお絵かき
  • 文章やプレゼンテーションをマルチユーザーで共同編集


少ないプログラミングでサクサク使えるので、リアルタイム通信をメインにしたシステムでなくても、ウェブサイト一部にさりげなく使うのも良いと思います。

  • コメント欄だと思ったら実はチャットができる(送信してもページがリロードされない)
  • 「最新の記事」がリアルタイムで更新される


他のユーザーが起こしたアクションをみんなが同時に体感できるのが面白そうです。
Webは孤独から共感に? いろいろなところにリアルタイム通信が偏在しているのも面白そうです。

*1:[http://images.google.com/images?q=%E3%82%A2%E3%83%AA%E3%82%A8%E3%83%BC%E3%83%AB:title=出展]