はじめに
初めまして。NTTアドバンステクノロジの金城と申します。幸運にも記事を執筆させていただけることになりました。WebSocketという新しいウェブの規格についての連載を、全4回の予定でお届けします。
用語統一について
WebSocketは「WebSocket」「 WebSockets」 、単語を切り離した「Web Socket」等、表記に揺れがあります。2009年12月22日のワーキングドラフトのタイトルは「The Web Sockets API 」となっていますが、2010年4月26日のエディターズドラフトでは「The WebSocket API 」となっています。この連載では、最新の仕様書に則り、用語を「WebSocket」で統一します。
HTML5とWebSocketの関係
WebSocketは、もともとHTML5の一機能として仕様の策定が進められていました。しかし、Web Storage等の技術とともにHTML5からは切り離され、現在はWebSocket単独での規格化が進められています。切り出されはしましたが、その生い立ちからも分かる通り、HTML5の周辺技術として重要な役割を担っていくでしょう。
クライアント・サーバ間の通信の歴史
連載初回は、WebSocket登場までのウェブページの歴史を、チャットアプリケーションを作ることを念頭に置きながら、クライアント・サーバ間の通信を中心に振り返ってみたいと思います。
なお、説明の都合上、CSSファイルやJavaScriptファイル、FRAME要素等は考慮せず、ウェブページは単一のHTMLファイルで作成されるものと仮定します。
静的なウェブページ
インターネットが一般化し始めた頃のウェブページは静的でした。クライアントからの要求があると、表示させたいコンテンツを画面のレイアウトに組み込んだHTMLをサーバで生成し、クライアントに返します。クライアントのブラウザは、受け取ったHTMLを画面に表示します。コンテンツを取得し表示した後は、画面を読み込み直さない限りその表示内容を変更されることはありません。
チャットアプリケーションでは、チャットの発言とレイアウト用のタグが混在したHTMLが生成されます。新しい発言を取得するには、レイアウトとすべてのコンテンツを再度取得する必要があります。新しい発言が複数あっても、発言がひとつもなかったとしても、全データを取得し画面をすべて書き換えるしかないのです。
図1 静的なウェブページ
DHTMLとiframe
JavaScriptの登場により、それまで静的だったウェブページが動的になり、サーバとの通信なしにウェブページに対話性を持たせることが可能になりました。DHTMLは視覚効果のためのものというイメージが先行してしまいますが、ウェブページの一部のコンテンツだけを書き換えられるようになったことに注目してください。レイアウトを操作しなくても、コンテンツの置き換えや追加が可能ですが、追加等により表示される内容は最新の情報ではありません。つまり、ウェブページを最初に読み込んだ時に取得したコンテンツでしかありません。
しかし、ここでトリッキーな手法が生まれます。iframeを使った通信です。後述するAjaxで非同期通信[1] が可能になったと説明されることもありますが、正確には静的なウェブページでもimg要素やiframeでは非同期通信が行なわれていました。この特性を利用し、ウェブページ上で動的にiframeタグを生成して、サーバとの非同期通信を任意のタイミングで行なわせる方法が誕生しました[2] 。様々な実装方法がありますが、例えば、コンテンツの要求をiframe内で行ない、読み込まれたコンテンツを親フレームから取得するか、iframe内でJavaScriptを実行し親フレームに反映させます。
例えばチャットでは、iframeで新しい発言を要求し親フレームに発言を反映させることで、ウェブページを読み込み直さなくてもよくなりました。
図2 DHTMLによるコンテンツの追加
図3 img要素の変更で画像はサーバから取得可能
図4 iframeによるコンテンツの取得
Ajax
iframeで強引にサーバと非同期通信が行なわれていた中で、マイクロソフトのInternet Explorer 5からXMLHTTPというコンポーネントを呼び出せるようになりました。その後、他のブラウザもXMLHttpRequest(以下、XMLHTTPも合わせてXHRとする)という名前でこの機能をブラウザの標準機能として組み込みました。XHRはウェブページから直接サーバに通信を行なうための技術で、XHRの登場によりiframeを使用しなくても、簡単にサーバとの通信を行なうことができます。
最初はほとんど注目されなかったXHRですが、Google Mapsの登場により存在が認知され、Ajaxと命名され脚光を浴びるようになりました。AjaxとはXHRによる情報の取得と、取得したコンテンツをDHTMLにより画面に反映させる技術の概念です。Ajaxは「Asynchronous JavaScript + XML」の略で、JavaScriptによる非同期通信[3] を概念に含み、取得されるデータもXMLが想定されていました。今では、取得するデータがテキストの場合だけでなく、XHRを使わずDHTMLだけで画面を変化させるものでさえも、Ajaxと呼ばれることがあります。
チャットの例では、iframeを使用した場合と同様に、ウェブページ全体を読み込むことなく新しい発言を画面に反映させることができます。
図5 Ajaxによるコンテンツの取得
Comet
XHRにより非同期な通信が可能となり、様々なウェブアプリケーションが登場しましたが、新たな問題が認知されるようになりました。XHRはクライアントからサーバへ通信を行いデータを取得する技術ですが、サーバからクライアントへ通信を行なう方法がないという問題です。つまり、プル(Pull)型の通信は可能だったのですが、プッシュ(Push)型の通信(配信)はできず、片手落ちの状態でした。チャットの例だと、他の人が発言した内容はサーバまではほぼリアルタイムで届くのですが、サーバから他のチャット参加者まではクライアントのXHRによる発言の取得処理を待たなければなりません。
ここで、またトリッキーな手法が誕生しました。Cometです。これまでは、通信はクライアントから開始し、サーバはクライアントの要求に合わせて応答を返していました。サーバに求められていたのは、できるだけ早く応答を返すことですが、Cometは全く逆の発想を元にした通信方法です。クライアントから要求を受け取ったサーバは、できるだけ接続を長引かせようと試みます(これをロングポーリングと言います) 。iframeによる非同期通信と同様に、Cometも実装により様々な方法があります。
この方法だけがCometだとは言えないのですが、一例を示します。クライアントからのデータ要求に対して、サーバはタイムアウトの限界ギリギリまで応答を返しません。サーバからクライアントにデータを渡す必要があった生じたとき、サーバは応答を返します。クライアントは、タイムアウトが発生した時は再接続を行ない、応答があった時には画面にコンテンツを反映させた後に再接続を行ないます。
チャットで例えると、タイムアウトが30秒の場合、他の参加者の発言がない場合は30秒毎に再接続を行い、発言があった場合はその発言を画面に反映後すぐに再接続を行ないます。
図6 Comet登場以前のチャットの例
図7 Cometを使用したチャットの例
WebSocket
Cometにより可能となったリアルタイム通信ですが、かなり無理をした実現方法のためか、Ajax程は普及しませんでした。ここで登場してきたのがWebSocketです。元々はHTML5の一部として仕様の策定が進められていたのですが、File API等とともにHTML5からスピンアウトし、個別の仕様となりました。
ここまであげてきた通信方法とWebSocketの決定的な違いはプロトコルです。WebSocketプロトコルは接続の確立にHTTPを使用しますが、その後の通信はWebSocket独自のプロトコルで行ないます。また、ヘッダが非常に小さくオーバーヘッドが少ないという特徴があります。長時間の接続を前提としており、接続した状態であれば、クライアントからもサーバからもデータ送信が可能です。また、データの送信用と受信用に個別にコネクションを持つ必要がなく、ひとつのコネクションでデータの送受信が可能となっています。通信時に指定するURLは「http://www.sample.com/」のような形式ではなく、「 ws://www.sample.com/」のようになります。
図8 HTTPによる接続と通信方法
図9 WebSocketによる接続と通信方法
まとめ
機能的な観点から歴史を振り返ると、iframeを使った非同期通信の登場でクライアントからサーバへの通信が、Cometの登場でサーバからクライアントへの通信が可能になったことがわかります。また、実装の難易度を考えると、亜流がたくさんあり複雑だったiframeによる非同期通信がAjaxで、実装が複雑だったCometがWebSocketで、それぞれ整理され簡単になりました。WebSocketの登場は、ある意味必然だったのかもしれません。
次回予告
次回からはWebSocketを使ったチャットを作成し、具体的な実装方法を解説したいと思います。