はじめに
『簡単なHTTPサーバの自作』第5回目の今回は、クライアントとの通信に、ブラウザ自身によるリクエスト/レスポンスに加えてAjaxとCometを利用した簡単なチャットアプリケーションを実装します。
Webアプリケーションでチャットを実装する場合に考えなければならないのは、他の発言者のメッセージをどうやってブラウザへ反映させるかです。自分が発言する場合は、サーバへ対してメッセージが送信されます。従って応答データに現時点の発言を返せば済みます。しかし、この方法だけでは他の参加者の発言を発言時に読むことはできません。
この問題を解決する手段は複数あります。
次の図は最も原始的ですが、確実な『F5連打法』を図示したものです。
F5連打法では、ユーザーは最新の発言がありそうだと感じた時に[F5]を叩きます。するとブラウザが最新の情報をサーバへ取りに行くため、もし発言が更新されていればその情報を表示できます。
しかし、この方法は発言が更新されていない場合にも無駄なリクエストが行われるためサーバにとって負荷が高く、結果的にユーザーにとってはなかなか更新されないといった弊害を持ちます。
次の図は、F5連打法を改善し、ブラウザ自身の機能を利用して再読み込みを行う『META Refresh法』を図示したものです。
META Refresh法はHTMLのMETA
タグを利用して一定時間ごとに、ブラウザにサーバの最新の情報を読ませます。この方法は、自動化されているという長所はあるものの、F5連打法の短所に加えて、更新間隔を短くすると入力がしにくくなる(場合によっては入力できない)ことや、更新間隔を長くすると反映が遅れるといった短所が加わります。
Ajaxを利用すると、META Refresh法の短所である入力しにくさという問題は起きません。
しかし、Ajaxを利用してもサーバへの無駄な読み取り要求は解消しません。また、更新間隔を長くとると反映が遅れるという問題点は残ったままです。
Cometは、Ajaxが利用するのと同じXmlHttpRequest
(XHRと略されることもあります)オブジェクトを利用しますが、Webサーバ側でデータが更新されるまで応答を返さないという違いがあります。このため、無駄な読み込みを抑えられると同時に、即座に更新を反映できます。
本記事では、前回作成した非ブロックIOを利用したWebサーバの実装にComet用の実装とチャットアプリケーションを追加して、実際のCometの動作を確認できるようにします。
なお、Cometの実装方法には、クライアント側がデータ取得のつど再接続する「ロングポーリング」と呼ばれる方法と、同一コネクション上でデータを分割して送る「マルチパートXmlHttpRequest」と呼ばれる方法がありますが、ここでは複数のブラウザで動作可能なロングポーリングを利用します。
対象読者
本記事は、Javaプログラミングの中級者以上を対象に、Cometという比較的新しいWeb技術を解説することを目的とします。Javaのプログラム自体は前回の記事「NIOの非ブロック接続を利用した多重IOの実装」で示したものに多少手を加えただけなので、解説は省略します。
ただしCometを実装するにはサーバ側だけではなく、クライアント側についてもAjaxを利用するためのJavaScriptプログラミングが必要です。しかも通信に利用するXmlHttpRequestはブラウザによって利用方法が異なります。またすべての処理をJavaScriptで記述するとコードが煩雑になってしまいます。
そのため、ここではクライアント側の記述に「prototype.js」というAjax用JavaScriptライブラリ(MITライセンス)を利用します。
従って、本記事では「prototype.js」を利用したAjaxの利用方法については簡単に解説します。
必要な環境
本記事のソースをビルド/実行するには、J2SE 5.0以上を利用してください。ソースファイルアーカイブは、直接NetBeans 5.0のプロジェクトとして開けるように構成してあります。ただし、NetBeans 5.0を利用しなくても、直接コマンドラインからJDKおよびAntを利用してビルドすることもできます。また、チャットアプリケーションを実行するには、「prototype.js」が必要です。本記事の作成に利用した「prototype.js」のバージョンは1.5です。
なお、ソースファイルアーカイブには、あらかじめJ2SE 5.0でビルドしたクラスファイルとjar、および「prototype.js」を含めています。そのため、JREがあればチャットアプリケーションを試すことができます。
参考までに筆者が利用した本記事のテスト環境は以下のものです。
OS | J2SE | Ant | IDE | ブラウザ |
Windows XP | 1.5.0_09 | 1.6.5 | NetBeans5.0J | Firefox 2.0/IE6SP2 |
OS X | - | - | - | Safari 2.0.4 |
コマンドラインからの実行について
NetBeansを利用せずにコマンドラインから実行するには、JavaとAntを実行できるようにPATHを設定し、アーカイブを展開したディレクトリの中(「build.xml」が存在するディレクトリ)で
ant run
を実行してください。これによりポート8801でチャットアプリケーションがサービスを開始します。
チャットアプリケーションのURLは、「http://localhost:8801/html/chat.html」です。他のマシンからアクセスする場合には、localhostの箇所を実行しているマシンのものに変えてください。また、パーソナルファイアウォールなどでポート8801を塞いでいる場合には事前に開放しておく必要があります。
終了するには、コマンドラインでブレイクさせるか(Windowsであれば[Ctrl]+[C]を押す)または、ブラウザから「http://localhost:8801/quit」にアクセスしてください。
注意事項
各ブラウザがHTTP 1.1で稼働する同一ホストに対して同時に呼び出せるXmlHttpRequestの数には、2個という制限があります。この「2」という数字はRFC2616で無条件準拠の要件として規定されたものです。このため、例えばIEを利用している場合であれば、[ファイル]-[新しいウィンドウ]を実行すると、2つのウィンドウがXmlHttpRequestの利用待ちでデッドロックします(他のブラウザから発言することでポーリングを完了させて一時的にロックを解除できます)。これを避けるには、複数のプロセスで実行したり、異なるブラウザを利用したり、別のマシンから呼び出したりしてください。
また、本実装は一応動作しますが完成したアプリケーションではありません。このため、リロードしてもロングポーリングしているXmlHttpRequestはポーリングを継続したままとなり、「新しいウィンドウ」を作ったのと同じ状態となります(ただしこの状態は一定時間経過後に元のリクエストが廃棄されて解消します)。ページのリロードが必要な場合には、リロード後に他のブラウザから発言してポーリングを完了させるか、またはブラウザを再起動してください。
NetBeans 5.0からの利用
メニューから[ファイル]-[プロジェクトを開く]を順に選択し、ソースファイルアーカイブを展開したディレクトリの「httpserver」ディレクトリを選択して、[プロジェクトフォルダを開く]をクリックしてください。