ブラウザというプラットフォームの為の基礎技術〜ブラウザ間通信〜

#先日上げたslideshareのスライドを見つつ読んでもらえると、多少分かり易くなると思います。
#http://www.slideshare.net/ranha/b2b-presen-presentation?type=powerpoint


最初の方の理由は後付けです。こんな面倒くさい事先立って考えません。

ブラウザについて

最近では、ネットブックと呼ばれるようなマシンも出てきました。


ネットブックWebサービス等を中心活用し、ローカルに様々なデータを蓄えずとも、ネットワークインフラの発達に伴いネット上のリソースにアクセスしても速度的にある程度満足出来る為に成り立ちうるからこそ出来るのでは無いかと思います。


ネットブックの中心は、やはりWebブラウザです。


ここで、ブラウザを今後のローカル環境のシフト先として捉える事を少々考えてみたいと思います。
直接的なシフト先としては、WebOSなんてのが容易に想起出来るでしょう。

適当な予想

エンドユーザの使うマシンのコストに対するパフォーマンスはしばらく上がると思われます。

書籍:googleを支える技術では、サーバサイドでのスケールアウトを取り上げていました。
ここでクライアントサイドでの分散計算などについて考えてみると、スケールアウトとスケールアップの両方が同時に行われているのでは無いでしょうか?


そもそも私の家は狭いし働きたく無いでござる次第ですし、別個にデータセンター持ったりするのも嫌ですので、スケールアウトもスケールアップもしようがありません。


ユーザにも是非とも種々の計算、サービスの存続に計算リソース的な意味で参加してもらいつつ、サーバにはサーバでもうちょっと別の方向でも頑張ってもらおうかなと、そういう事が出来るかもしれません。

その為に何が必要だろうか。

サーバに負荷をかけずに、クライアントサイドで極力出来る事はやって欲しい・・・という事になると、P2Pがまず出て来ると思います。


ブラウザを新しいシフト先、プログラムを動かすプラットフォームとして考えてみたので、やはりブラウザを絡めてP2Pさせられると良いのでは、と短絡的に考えました。


短絡的に考えた結果が、ブラウザ間通信というわけですw
ブラウザがブラウザにデータを直接送る、あるいは送られて来たデータを読み、処理しサービスを稼働させていく...


何やらブラウザ間通信を可能にすると、

(ローカルで)Hogeプログラム
↓
ブラウザでHoge

とする事が出来そうです。


ブラウザは最近では大概のマシンに入っていますし、一般ユーザにも親しみがあるソフトウェアだと思うので、余計な手数を踏ませずともサービスを提供する事も可能になるのでは無いでしょうか。(現状のWebサービスが十分それですが、新たにP2P的な意味を込めて

で、何をしたのか

今回の前提としては、ブラウザ上で動かすプログラムはJavaScriptとし、
JavaScriptでブラウザ間通信(Browser 2 Browserよりこのエントリ内では以後B2Bとする)をする為のライブラリ、というよりは機構を考えて実際に動かす所までやってみました。

JavaScriptでの相互通信

JavaScript(以下JS)単体で、クライアント同士(サーバを介さずに、サーバを介すならばXMLHttpRequestを使えばよろしい)を接続させ、相互にデータをやりとりさせるのは私の調べが及んだ範囲では無理そうでした。


幸い筑波大学内に、JSとActionScript(以下AS)のコラボレーションを行いJavaScriptとサーバ間での
双方向RPCを可能にしたid:viverさん(また、その技術について詳しくは氏のブログを参照:http://d.hatena.ne.jp/viver/20080504/p1)が居りますので、
そこから私もJS単体ではなくASと組み合わせる事で、取りあえず先に進めるのでは無いかと考えました。

ASを組み合わせる事で、何が出来るようになるのか?

ASのSocket(http://livedocs.adobe.com/flex/3_jp/langref/flash/net/Socket.html)を用いる事で、バイナリデータの読み書きが出来る様になります。


ちなみに、ASを使わなくともブラウザに梃入れした(プラグインの形で)方法としては、JNEXT(http://jnext.org)があります。

問題点有り

ASのSocketに関してですが、これは"connect"は出来るが"listen"は出来ません。
つまり、TCPの"接続"は出来るが"その接続を受け付ける"事は出来ないのです・・・。

Proxy Server

ブラウザはユーザのマシンに入っているから良いよね、とか言っておきながら、ASのSocketの問題を回避する為に
ユーザにProxy Serverを立ち上げてもらうという非常にオワッとる方法を取ります。


このProxy Serverの役割は、スライドにもあるように
1.自分のブラウザからのデータ送信先を、相手ブラウザでは無く、自分のProxy Serverにする。
2.Proxy Serverが代わりに、"相手のProxy Serverに"データを送る、
3.相手のProxy Serverは、相手のブラウザに対してデータを送る
ということで、疑似的にブラウザ間の通信を達成しています・・・。

AS + JS

ASからJSの関数やらをcall、逆にJSからASの関数をcallする際にはExternalInterfaceを用います。


具体的なコードも示しておきます。

package{

...
import flash.display.Sprite;
import flash.external.*; // ExternalInterfaceを用いる為に必要です。
...

    public class Socketlib extends Sprite
    {
        //ASでlogをcallすると、JavaScriptのwindow.console.logがcallされる。
        //ExternalInterface.call(JSの関数、対する引数
        private var log = function(msg){ExternalInterface.call("window.console.log",msg);};        

        public function sock_connect(host:String,port:uint):void
        {
            socket.connect(host,port);
        }

        public function Socketlib()
        {
            ...             
            //JS側からsock_connectをcallすると、ASのsock_connectが呼ばれる。
            ExternalInterface.addCallback("sock_connect",sock_connect);
        }
    }
}
<embed src="Socketlib.swf" name="movie" allowScriptAccess="always" width="0" height="0"/>
<script language="JavaScript">
...
var flash = window.movie || document.movie;

flash.sock_connect("localhost",12345);
...
</script>

とこんな感じになります。

ここまでの技術でサンプル

JS * AS + Proxy Serverで、非常に簡単なチャットプログラムを作ってみました。チャットプログラムのコード群


ローカルでもグローバルでも構いませんが、サーバを立ててもらって、
そこにこれらのファイルをぶち込んで貰う必要があります。


幾つかの作業が必要で、

1.ruby Hoge.rb ポート番号 でProxy Serverを起動
2.asファイルをコンパイルして、swfを作る必要あり

です。
その後はHTMLファイルを開いて貰えばstart,送信,受信ボタンが現れます。


ここでは2つのProxy Server(ポートは12345,12346)が立っているものとして使い方を示します。

使い方と非常に簡単な内部説明

まず適当なブラウザを立ち上げ、タブを2つ開き、それぞれでHTMLを読み込みます。


片方はstartボックスに12345を入力しstartボタンを、もう一方は12346を入力しstartボタンを押します。


この時点で、それぞれがProxy Serverへの初期接続を行いました。これによって、Proxy Serverとブラウザがまず一対一で対応します。


それぞれの送信ボックスに適当なメッセージを書いた後で受信を押してもらうと、

(12345/12346):メッセージ

が送信ボックスに追加されると思います。


この段階では、1つのProxy Serverに対し、1つのブラウザ(タブですが)しか割り当てられていませんので自分の発言しか読む事は出来ません。


それでは実際にチャットとして使ってみます。

1.12345とstartボックスに入力したタブで、今度は12346と入力しstart
2.12346とstartボックスに入力したタブで、今度は12345と入力しstart

この段階で、スライドの13枚目の状態になります。


後は先程と同じように適当なメッセージを送信ボックスに書き込んで、もう一方のタブで読み込んで見るとちゃんと相手側の発言が見える様になっているのが分かる筈です。


実装が丸見えに成ってると何ら大層な事をしていないのが分かるかと思いますが、まぁなんとなくブラウザ間で通信している様には見えるのでは無いでしょうかwww

サーバが落ちても大丈夫

ブラウザにJSとSWFが読み込まれた後ならば、このチャットプログラムの場合は既にサーバとのやりとりを必要としませんので、何らかの事故でサーバが落ちても起動し続けます。

チャットプログラムを改良する

先程のチャットプログラムでは幾つか面倒くさいステップがありました。

1.相手のProxy Serverに接続した時点で、相手側からも自分のProxy Serverに接続しなおして欲しい...
2.一々読み込みボタンを押すのが癪。我慢成らない...

では今度はこの問題を解決してみます。
ちなみに、2番はブラウザ間通信とは直接関係が有りませんw

改良1

上の2つの問題のうち前者は簡単なアプリケーションプロトコルを決めてやればどうとでも成ります。
例えば、"接続"と"通常の発言"を分けてやる事で、

・相手から"接続"コマンドが飛んで来た時は、勝手に相手に接続仕返す
・"通常の発言"が飛んで来た時には、特に何もしない

と言った具合です。


ちなみに、通常の発言が飛んで来た時にcall-back関数を呼び出してやれば2の問題も解決出来そうです。
ASにはonDataイベントが有りますので。


ただ今回は2に関しては違うアプローチを取ってみました。

改良2

2つの問題のうち後者を取り除く時です。
イベント駆動型で書くのも有りだとは思いますが、Read用のThreadを一本立ててやろうかと思った訳です。


という事でJavaScript ThreadでググったわけですがJSはThreadをサポートしていないようです。


"Concurrent.Thread"(http://www.infoq.com/jp/articles/js_multithread_2)や"そうめん"(http://www.libspark.org/wiki/Thread)はありましたがやはり疑似スレッドです。


ですが、ブラウザではJS、ASの他にJavaAppletがあります。
JavaApplet(以後JA)には当然Threadがあるわけで、内部がどうなっているかは分かりませんが、取りあえず上の二つを導入するのも面倒くさかったので今回はJAのThreadを使いました。

JA + JS

今度はExternalInterfaceはありません。


JAからJSを呼ぶ時には、JSの値を扱うJSObjectなどを
使える様にする為にimport netscape.javascript.*;する必要があります。


一方JSからJAを呼ぶのはASを呼ぶ時と然程変わりません。
ここの所は実際にサンプルのコードを見てもらえれば良いと思います。

改良チャットプログラム

http://d.hatena.ne.jp/ranha-study/20081004/1223146181:改良チャットプログラムのコード


RubyによるProxy ServerとASによるSocketは変わっていないので、新しくJavaAppletなThreadを使う為のJavaと、変更が加わったHTML(JS部ですが)を上げました。


見てもらえば分かると思いますが、大層な事はここでもしていませんw


ただ、ちゃんと問題点は改善されています。


それとFirebugを使う人はconsoleにreadメッセージが流れるのが見えると思いますが、これでちゃんとThreadが動いているのがお分かり頂けると思います。


ちなみに、未来会議ではチャットさせながらfiobnacciを計算させてキャッキャウフフしていました。

気になる点

JavaAppletのThreadがどうなってるのか。

結び

今回は、ブラウザとブラウザで直接相互通信するブラウザ間通信の概念と、現状如何にしてそれを実装するかを述べました。


ただし、この先に進む為にはNAT越えなどが必要になります。


今回苦し紛れにProxy Serverを導入した事で、その部分はユーザが好きな様に差し替える事が出来ますし、ASのSocketでは遺憾ともしがたかったUDPをProxy Server側でサポートすればUDP Hole Punchingやらも出来ます。


しかし、P2Pよりの技術になりますから、こいつを使おうと思うと分散ファイルシステム、分散ストレージ的なものがどうしても欲しく成って来るので、取りあえず次は簡単な分散ファイルシステムでも書いて、もうちょっと実用的なサービスを書いてみようと思ったりもしています。
(以前書いた論文のオススメ合いサービスのようなものですがw)



それと、AS,JSは今回初めてと言っていい位に初々しく使ったのですがなんとか未来会議に間に合えて良かったですw
HTMLが相変わらず汚いのはそろそろなんとかしたい感じなんですが・・・。

クライアントサイドプログラミング言語

の重要性もこれはありそうだなーとかは思います。


ただ、JavaScriptによるHoge言語の実装もあちこちにありますし、ブラウザベンダが頑張ってくれれば、

<script language="ruby">
とか
<script language="scheme">
とか
<script language="clisp">
とか
<script language="haskell">

とかも出来る様になってくるかもしれませんね。


schemeに関しては、ちょっと頑張ってこんな事もしました。
(XML)HTML駆逐 - Yet Another Ranha