読者です 読者をやめる 読者になる 読者になる

Comet/Ajaxの上を行く技術

上を行くかどうかは知りませんが :-p

Ajaxはクライアントの都合でサーバーに通信を仕掛けるpull型の通信ができ、Cometはサーバーが好きなタイミングでクライアントへデータを送りつけるpush型の通信ができるわけですが、新たに双方向の通信ができる技術を開発しました。


具体的には、JavaScriptとサーバーの間で双方向のRPCができます。すなわち、サーバーからクライアント側のJavaScriptのメソッドが呼べるし、逆にクライアント側からサーバー側のメソッドを呼ぶこともできます。
サーバー側で call("addMessage", "Hello!") とやると、JavaScript側の function addMessage(msg) { ... } という関数が呼ばれたりします。


この技術を使って、試しにチャットシステムを作ってみました > デモソースコード*1
リアルタイムでチャットができるほか、接続しているユーザーの数もリアルタイムで更新されます。かなり軽快なレスポンスで使えると思います。
Operaで接続すると発言が表示されないというバグがあります。原因不明><)


仕組みはどうなっているかと言うと、通信部分にFlashを使っています。



Flashとサーバーとの通信には、ActionScriptSocketを使っています。
FlashとJavaScriptには相互に連携できる機能があるので、それを使っています。外部APIを使用したFlashとJavaScriptの接続 - Adobe - デベロッパーセンター


サーバー側のライブラリは、今のところRubyで書かれています。イベントループにEventMachineを使っているので、そこそこの接続数にも耐えられる、かもしれません。
プロトコルは単純なJSON-RPCなので、他の言語で実装するのも簡単だと思います。


さて、実際のアプリケーションでは、JavaScript側のコードは↓こんな感じになります。

<div id="flashcontent"></div>  <!-- ここに0ピクセルのswfがロードされる -->
<script type="text/javascript">
    // サーバーに接続する
    var meteor = new Meteor("Meteor.swf", "flashcontent", function(){
        // crossdomain.xmlを読み込む
        meteor.loadPolicyFile("127.0.0.1", 5757);
        // コールバック関数を指定して接続
        meteor.connect("127.0.0.1", 9240, connected, failed, closed);
    });

    // 接続できたら呼ばれる
    function connected() {
        // サーバーがaddMessage関数を呼べるようにする
        meteor.setHandler("addMessage", addMessage);
    }

    // 接続に失敗したら呼ばれる
    function failed(msg) {
        alert("connect failed: " + msg);
    }

    // 接続が切断されたら呼ばれる
    function closed() {
        alert("closed");
    }

    /** 発言をHTMLに書き込む
     * @param msg  チャットのメッセージ
     */
    function addMessage(msg) {
        var li = document.createElement("li");
        li.textContent = msg;
        li.innerText = msg;
        document.getElementById("comment").appendChild(li);
    }

    /** サーバーに発言を送る
     */
    function submitMessage() {
        // サーバー側のmessage関数を呼ぶ
        meteor.call("message", [document.getElementById("body").value]);
        return false;
    }
</script>

<!-- 発言フォーム -->
<form onsubmit="return submitMessage();">
    <input type="text" name="body" id="body" />
    <input type="submit" value="submit" />
</form>

<ul id="comment"></ul>  <!-- ここに発言を追加していく -->

(loadPolicyFileはFlash Player 9,0,115,0の<policy-file-request/>に対処する方法を参照)


接続する呼ぶ/呼ばれる という簡単な構図です。サクサクな感じですねー。
実際のDOMの操作にはjQueryなどを使うのがいいと思います。


サーバー側は↓こんな感じです。短い!

require 'meteor'

$clients = []

class MeteorChat < Meteor::Handler
  # 新しいクライアントが接続してくるごとにinitializeされる
  def initialize(*args)
    super
    $clients << self
  end

  # クライアントから呼ばれる
  # 発言を受け取って、すべてのクライアントに転送する
  def message(msg)
    del = []
    $clients.each {|c|
      begin
        # JavaScript側のaddMessage関数を呼ぶ
        c.send_request("addMessage", msg)
      rescue
        # 失敗したら削除
        del << c
      end
    }
    del.each {|d|
      $clients.delete d
    }
  end

  # 接続が切断されたときに呼ばれる
  def unbind
    $clients.delete self
  end
end

Meteor.start :Handler => MeteorChat, :Port => 9240


Cometと比べた問題として、プロトコルがHTTPではないので、ファイアウォールを越えにくいという問題があります。一方でCometほどブラウザの実装に依存していないので、実装は楽です。


というわけで、Ajax、Cometに次ぐ次世代の技術として期待される(?)わけですが、名前が思いつきません。
仮にMeteorと言う名前が付いているのですが、同じ名前でCometサーバーの実装があり、思いっきりカブっています。
Ajax、Cometと同じように洗剤の名前を付けるとすれば、アタックとかホーミングとかジョイとかも面白いかなー思ったのですが、どうもしっくりこない。
何かいい名前をひらめいたら教えてくださいm(_ _)m

*1:このデモは近いうちに消滅します