噂のnode.websocket.jsでサーバサイドJSとHTML5 WebSocketを体験してみたの巻

WebSocketを体験してみたいのと、サーバサイドJSを試したいのと、さらにはmac版のChromeをインストールしてみたという条件が重なり、これはもう深夜だけどnode.websocket.jsを試してみるしかないな、という状況に追い込まれました。

構成

最近あたらしく調達したばかりのmacbook airvirtualboxを入れています。その上でCentOSが動いています。
macbookをクライアントに、CentOS側をサーバとみたてて話を進めていきます。

ちなみに、virtualboxではアダプタ1をNATに、アダプタ2をホストオンリーアダプタ(IPはstaticに設定)としているので、CentOSからは外にも抜けられるしmacbook側からも自由にアクセスできます。ごきげんな環境です。

Node.JSのインストール

まずは土台となるnode.jsが必要。ソースをダウンロードしてくるか、もしくはgitからcloneするかします。

http://nodejs.org/

展開して./configure & make します。やや時間かかりますが、とくに問題なくコンパイルできました。
make test すると私の環境では「abが入ってないよ」と文句言われましたが、しょうがないじゃんapache入れてないんだから。なのでシカトしてsudo make install。
/usr/local/bin/nodeがインストールされました。

さて、これでgoogle V8エンジンによるサーバサイドjavascriptな環境ができたはずです。早速ためしてみます。

var sys = require('sys'), 
   http = require('http');
http.createServer(function (req, res) {
  setTimeout(function () {
    res.sendHeader(200, {'Content-Type': 'text/plain'});
    res.sendBody('Hello World !\n');
    res.finish();
  }, 2000);
}).listen(8000);
sys.puts('Server running at http://127.0.0.1:8000/');

http://nodejs.orgに書いてあったサンプルのままですが、これをexample.jsなど、適当な名前で保存します。
そしてnodeでjsを走らせます。

[miki@gamella nodejs]$ node example.js
Server running at http://127.0.0.1:8000

おお、サーバが起動した!netstat で確認してみたらちゃんと8000番でlistenしてるよ!

macbook側(クライアント側)からHTTPをたたいてみます。

godzilla:Hacks miki$ lwp-request http://gamella:8000/
Hello World !
godzilla:Hacks miki$

ちゃんと返ってきました!

次にTCPサーバのサンプルもあるので、それも試してみました。

var tcp = require('tcp');
var server = tcp.createServer( function(socket) {
    socket.setEncoding("utf8");
    socket.addListener("connect", function() {
        socket.send("hello\r\n");
    });
    socket.addListener("receive", function(data){
        socket.send(data);
    });
    socket.addListener("eof", function(){
        socket.send("goodby\r\n");
        socket.close();
    });
});
server.listen(7000, '192.168.56.101');

これもサンプルのままなんですが、最後の行だけオリジナルとは変えています。

オリジナルでは「server.listen(7000, "localhost")」となっていましたが、
自分のサーバ環境(virtualbox上のCentOS)だとlocalhostと指定するとアダプタ1(NAT)のIPになってしまうので、
ここではアダプタ2(ホストオンリーアダプタ)の方のIPを直で書いています。

(本質とは関係ないことなので、どうでもいいことですが。)


なにはともあれ、こんな簡単にサーバサイドJSが実現できてしまって、ちょっと驚きです。
今までは事あるごとにperlでAnyEvent/POEでサーバを書いてきましたが、ちょっとした用途であればJavascriptでもいけそうです。

node.js.websocketを試してみる

さぁて、ようやく本題です。HTML5 WebSocketです!

まずはここのサイトをザッと読んでおくといいでしょう。

http://devthought.com/blog/2009/12/nodejs-and-the-websocket-protocol/

  • Node.JSを読んで触発されてNode.JS.Websocketを書いたよ
  • WebSocketいいよ COMETの対抗馬だよ
  • Node.JSはネットワークなイベントドリブンなフレームワークgoogleのV8エンジン搭載だよ
  • loggingにはRedis使うけどなくても大丈夫だよ
  • WebSocketはまだ限られたブラウザでしか使えないよ(Webkit, Chromium, trunk Firefox (and possibly Opera) have a decent degree of support of WebSocket)

ほかにも最後の方で今後の抱負みたいなことがかいてありますが、まぁだいたいこんな内容のことが書いてあります。

というわけでさっそくソースを落としてきます。

http://github.com/guille/node.websocket.js/

README.mdの最初の方をサラリと読んだだけなんですが、どうやら簡単に使えるみたいです。

まずは同梱されているtest/test.htmlをサーバ側に設置します。

あ、そういえばapache入れてないんだった。面倒いからyum install httpd してしまえ。えいっと。

test.htmlはそのまま使えます。メインのjavascript部分だけ書き出しておきます。

    <script>
        var webSocket = new WebSocket('ws://192.168.56.101:8080/time');

        webSocket.onopen = function(event){
            document.getElementById('time').innerHTML = 'waiting for socket';
            webSocket.send('start');
        };

        webSocket.onmessage = function(event){
            var object = JSON.parse(event.data);
            document.getElementById('time').innerHTML = object.time;
        };

        webSocket.onclose = function(event){
            document.getElementById('time').innerHTML = 'socket closed';
        };
    </script>

これがWebSocketかぁ。なんだかえらく簡潔なコードですね〜。
いずれ、HTML5が本格普及した暁には、こんな簡単なコードで永続セッション張れてしまうようになるんですね。
(いつ頃になるのかは怪しいところですが・・・)


つづいてサーバ側です。

node runserver.js とたたけば起動するんですが、何度も書いている通り 自分の環境ではホスト名をIPにしないとだめなので、--hostオプションをつけます。

[miki@gamella node.websocket.js]$ node runserver.js --host="'192.168.56.101'"
[Wed Dec 16 2009 03:16:58 GMT+0900 (JST)] [info] 0 clients connected

ふむ。websocketサーバが起動した。0 clients connected だってさ。

ではでは、クライアント側(macbook)からブラウザでアクセスしてみます。ブラウザはmac版のGoogle Chrome 4.0です。

やた!成功!。。。ってなんかえらく地味なデモですね。

でもちゃんと現在の時刻がPushされて画面上で更新されています。

ちなみにコンソール上ではこんなログが出力されました。

[miki@gamella node.websocket.js]$ node runserver.js --host="'192.168.56.101'"
[Wed Dec 16 2009 03:22:16 GMT+0900 (JST)] [info] 0 clients connected
[Wed Dec 16 2009 03:22:21 GMT+0900 (JST)] [info] 0 clients connected
[Wed Dec 16 2009 03:22:26 GMT+0900 (JST)] [info] 0 clients connected
[Wed Dec 16 2009 03:22:31 GMT+0900 (JST)] [info] [client 192.168.56.1] Server created
[Wed Dec 16 2009 03:22:31 GMT+0900 (JST)] [info] [client 192.168.56.1] Performing handshake
[Wed Dec 16 2009 03:22:31 GMT+0900 (JST)] [info] [client 192.168.56.1] Handshake sent
[Wed Dec 16 2009 03:22:31 GMT+0900 (JST)] [info] 1 clients connected
[Wed Dec 16 2009 03:22:36 GMT+0900 (JST)] [info] 1 clients connected

実際のところどうなのか?

見た目はぱっとしませんが、WebSocketにはちょっと面白い一面があります。

wsプロトコルはHTTPをベースとしている、ということです。
自分はFlash-XMLSocketの用に、socket的にハンドリングしているのかと思っていたんですが、どうやら違うようです。

実際にtcpdumpしてみたんですが、HTTPのコネクションが確立した後、jsonでやり取りしている様子を確認しました。


しかしなんでわざわざHTTP的なステージでやり取りさせる必要があるんだろ??

ここまで書いてきて何なんですが、これだけのことならFlash-XMLSocketで既にできているんだよなぁ、とか思ったりして。

リアルタイムWebの本命となるのは、COMETなのかXMLSocketなのか、はたまたWebSocketなのか?

google waveの動きも含めて、どこらへんの技術がデファクトになるのか、今後もしばらく注目する必要がありそうです。