「いや〜、node.jsってすごいよね」。Bamboo社内ではnode.jsの話題が何度も上がってきたのですが、実際にnode.jsを使って何をすれば良いかはちょっと考えあぐねていました。その頃は、node.jsを使用したWebフレームワークなどが雨後のタケノコのように出てきていたのですが、「今までのWebサーバで出来ることをただ置き換えるだけっていうのはあんまり面白くないよね」というのが正直な気持ちだったと思います。それから1カ月ほど経った2009年の12月、Webの世界に新たなニュースがありました。Googleが開発するブラウザのオープンソース版であるChromiumにWebSocketという新たな機能が加わるというのです。WebSocketを一言で言うと「WebのためのTCP」です。今までのWebはHTTPプロトコルを基本とした一方向のものでした。クライアント(ブラウザ)はサーバにリクエストを渡し、サーバはクラインとにレスポンスを返すというスタイルです。TCPはHTTPより下層に位置するネットワークプロトコルで、クライアントとサーバ間の双方向通信を可能とするものです。TCPサーバをnode.jsで書くと以下のようになります。'localhost');この状態でtelnetすると、以下のようにTCPサーバとやりとりすることができます。ここでTCPサーバは各セッションごとにarrayという配列を用意しておき、入力リクエストが来ると、そのデータを配列に代入し、全配列の結果をレスポンスとして戻しています。HTTPとの重要な違いですが、HTTPは各リクエスト、レスポンスはまったく別個のものであり、各リクエスト間で状態(ステート)を共有することはありません。逆に、TCPの方は一度クライアントとサーバとの間で接続が確立されると、その後のやり取りで状態を共有することができます。HTTPのことをステートレスと呼ぶのに対し、TCPのことをステートフルと呼びます。今までHTTPで擬似的にステートを共有するには、以下の方法が取られていました。クッキーに情報を保存するリクエストのURIパラメータにすべての値を指定する(例:http://example.com/?val=[a,b,c])もともとHTTPはHTML文書を表示するために規定されたものなので、「リクエスト/リスポンス」の単純なモデルで十分だったのですが、多くのWebサイトが「アプリ化」してきている現在では、上のような方法で擬似的にダイナミックなアプリを作るには限界があります。WebSocketはそんな限界を打ち破る可能性を秘めた新たなプロトコルといって良いでしょう。WebSocketのステートフルな性質を利用することで「サーバサイドプッシュ」を実現することができます。必要なときにサーバサイドから情報を送ることができれば、ムダなトラフィックを減らせます。雪が降ったり、災害のときなどに交通機関のサイトやニュースサイトがアクセス超過でサイトがダウンすることがあります。これは、情報が更新されるタイミングが分からないために、みんなが何度も「リフレッシュボタンを」押すからでしょう。これはリクエスト/レスポンスというモデルの弊害と言ってよいでしょう。いったサイトをWebブラウザで開いておいて(TCPでいう接続の確立)、ニュースがあったときのみサーバからクライアントの方に情報をプッシュしてくれれば、余計なトラフィックを抑えられるはずです。W3Cによって規定されるAPIとIETFによって規定されてい通信プロトコルによってなりたちます。」に統一されたようです。APIは非常に単純ではないでしょうか。1行目でWebSocketオブジェクトを作成した後、「onopen,onmessage,onclose」という3つのコールバックファンクションを定義するだけです。サーバの方にメッセージを送りたい場合は「send()」というファンクションを呼び出すだけです。私はこの例を見たときに「WebSocketとnode.jsって合うのでは」と思いました。node.jsのWebSocketサーバを探してみたところ、プロトタイプ版のようなライブラリが2つほど見つかりました。MonitorのようなもののWeb版を作ってみることにしました。サンプルとして作成したリアルタイム性の高いモニターの動画(クリックで再生)ソースの全文はgithub上にあります。当時は「websocket-server-node.js」というライブラリを使っていたのですが、今はすでにメンテされていないようなので「node-websocket-server」というライブラリに変更して書き直してみました。今回のサンプルのiostat-client.htmlですが、わざわざWebサーバ上に置かなくとも、ファイルをローカル上で開くだけで実行可能なはずです。"+conn.id);});server.listen(8000);「connection」と「close」という2つのコールバック関数を指定している点は、TCPサーバの例と大変似ていますね。1"]);のところで子プロセスを作り、iostatというコマンドが1秒おきにCPUやIO情報を出力するようにしている点です。});のところでチャイルドプロセスの出力結果を1秒ごとに出力するようにしています。ブラウザ側のコードもいたってシンプルです。drawCharts(total_array.reverse());};「onmessage」コールバックにデータが送られてくるたびに、JSONデータを解析し、その結果をグラフとして再描画しています。AjaxやCometと何が違うの?ここまで読んだ読者の皆さんは思うかもしれません。「AjaxやCometでも同じことができるんじゃないの?」まず以下の図を見てみてください。WebSocket最初の例はAjaxです。毎秒ごとにHTTPリクエストを送っています。HTTPのリクエストとレスポンスにはヘッダー部分に以下のようにいろいろな付帯情報を付けなければいけません。charset=UTF-8※http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocolからの例1リクエスト/レスポンスヘッダー自体は数KBと、そう大きくありませんが、リクエストの更新頻度が密になってくると、毎回のリクエスト量は馬鹿になりません。更新頻度を遅くすると、欲しい情報がすぐに手に入らないし、更新頻度を上げるとサーバへのリソース要求が高くなる、というジレンマを抱えることになります。疑似サーバプッシュ技術を総称する用語として「Comet」というものがあります。Polling」と呼ばれるものがあります。これは次のように流れになります。コネクションをつなぎっぱなしにしておきます。何か情報を更新するときになってからレスポンスを返します。これは私が最初に述べた「災害情報の更新」やチャットなど数秒以上に一度しかレスポンスを返さないようなケースでは有効ですが、更新頻度が上がってくるにつれ、AJAXと同じような問題を抱えることになります。Cometで定期的に疑似プッシュしようとした場合、「どの時点までのデータをクライアントに送信したか」というステート情報も別に管理しなければいけません。Polling方式で実装しました」と教えていただいたので、彼のコードの一部を見てみましょう。});});「/update」のURIにクライアントが最後にデータを受け取った時間を毎回送付して、サーバの現在時刻と比較。差分のデータを送るようにしています。毎リクエストごとのステートを管理するコードを別途書かないといけない上に、今回のように時間を扱う場合、「クライアントマシンの時間設定がちゃんと設定されていない場合はどうなるの」といったケースにも対応する必要があるでしょう。一方のWebSocketですが最初に接続を確立する時に以下のようなリクエストをサーバの方に送ります。http://example.com^n:ds[4Uサーバサイドはリクエストを受け取った後、以下のようなレスポンスを返します。sample8jKS'y:G*Co,Wxa-基本的には通常のHTTPリクエスト/レスポンスとほぼ同じです。ここまでだとAJAXやCometと同じように見えるかもしれませんが、このやりとりを行ってクライアントとサーバの間で接続が確立された後は、以下のようにデータの前後に1バイトずつ付けた「データフレーミング」という形式でデータのやりとりを行うことになります。0x00[Data]0xFF【注】WebSocketのデータフレーム形式(0x00[Data]0xFF)は、この夏ごろから仕様の変更が議論されています。今後、連載中にも仕様が変わる可能性があります。接続時のHTTPリクエスト/レスポンスに比べて明らかにデータ転送のオーバヘッドが少なくなるのが見て取れると思います。Hicksonさんはメーリングリストの中で以下のように述べています。Google.「数キロバイトのデータ転送量を(最低)2バイトへ、150msの遅延を50msにできるとしたら、それはわずかな向上といったような生やさしいものではない。実際この2つの事実だけをもっても、WebSocketをGoogleにとって本気にさせるには十分なものだ」またWebSocketのうれしい点としてクロスドメインが可能な点も挙げられます。AJAXやCometの場合、原則クロスドメインスクリプティングができないため、JSONPなどのテクニックと併用する必要がありますが、WebSocketは原則可能です(最初のリクエストヘッダーの部分でSec-WebSocket-Locationを指定しているので、それを利用して既知のドメインのみアクセスを許すといった制限をかけることも可能です)。リアルタイムWebとは何かということと、WebSocketの実例を示し、AjaxやCometとの違いについて述べました。次回は、WebSocketのブラウザの対応状況などを交えつつ、技術的課題やサーバサイドの実装例などについてご紹介したいと思います。メールマガジンをご購読ください。申し込みページへITmediaはアイティメディア株式会社の登録商標です。