はじめに
本記事では、『CometとAjaxを利用したチャットサーバの実装』で紹介したCometを利用したWebチャットを、ASP.NET上で実現する方法を説明します。CometというWebアプリケーションのアーキテクチャや、クライアントとサーバーの動作については、前掲の記事を参照してください。
対象読者
本記事は中級以上のWebアプリケーション開発者を対象に、ASP.NETの非同期ハンドラのデザインを説明します。本記事の関心の対象は、ASP.NETの非同期ハンドラというデザインパターンのため、対象読者を必ずしもASP.NETを対象として開発されている方に限定していません。
ただし、開発環境としてWindows XPまたはWindows 2003 Serverと、Visual Studio 2005またはVisual Web Developer 2005 Express Editionの利用を前提とするため、実際にサンプルを動作させるにはこれらが必要となります。また、言語にはC#を利用します。
なお、本記事では『CometとAjaxを利用したチャットサーバの実装』で利用したクライアント側コードの一部を流用しています。この中で利用しているprototype.jsを利用している個所については説明を省略します。必要であれば前掲記事を参照してください。
必要な環境
本記事のサンプルを実行するには、Visual Studio 2005またはVisual Web Developer 2005 Express Editionと、これらを実行可能なWindowsのいずれかのエディションが必要です。また、IISで実行することもできます(図)。
クライアント側スクリプトの一部ではprototype.js(MITライセンス)を利用しています。ただし、prototype.jsについてはサンプルのアーカイブ内に含めてあります。
注意事項
本記事のサンプルのクライアントとして、IE6およびIE7で動作を確認しています。また、Firefox 2.0では2度目のリクエスト送信時にエラーが発生して再ロードされる現象を確認しています。従ってFirefox 2.0については受信動作以外は正しく行えないという制限を持ちます。これら以外のブラウザでの動作確認は行っていません。
上記制限の他に、XmlHttpRequestの制限から、IEを利用した場合であっても同一プロセスからの複数のウィンドウや複数のタブでの実行はサポートしていません。
ファイル構成
ダウンロードしたファイルはzipで圧縮してあります。展開すると「CometChat」というディレクトリを頂点としたディレクトリ階層ができます。Visual Studio 2005を利用している場合は[ファイル]メニューの[開く]-[Webサイト]を、Visual Web Developer 2005 Express Editionを利用している場合は[ファイル]メニューの[Webサイトを開く]-[ファイルシステム]を利用して、「CometChat」ディレクトリを開いてください。
ルートディレクトリ
- Web.Config
ChatHandler
クラスに割り当てています。なお、デバッグをtrueに設定しているので、IISへこのディレクトリを直接組み込む場合は設定し直してください。- Default.aspx
- Default.aspx.cs
App_Codeディレクトリ
- ChatHandler.cs
cssディレクトリ
- chat.css
jsディレクトリ
- prototype.js
解説(1/2)
ASP.NETの非同期HTTPハンドラ
Cometのキーとなる考え方は、クライアントからのXmlHttpRequestを使ったリクエストを一旦保留して、サーバー上のイベントの発生によってレスポンスを返す、という点にあります。通常、Webサーバーはこのような動作を想定していません。よくあるWebサーバーの実装は、クライアントからリクエストを受け付けるとスレッドプールからワーカスレッドを1つ確保して、そのスレッドにレスポンスを作成させるというものです(図)。この動作を前提とするWebサーバーでCometを実現しようとすると、クライアントがある程度の数になった時点でスレッドプールが枯渇してしまいます。
ASP.NETには非同期HTTPハンドラと呼ばれる、時間がかかる処理の途中でスレッドを一旦解放して、スレッドプールの枯渇を防止するための実装パターンが用意されています(図)。
ASP.NETは非同期HTTPハンドラを利用して次のようにクライアントからのリクエストを処理します。なお、HTTPハンドラはオブジェクトを複数のリクエストで共有するためのプロトコルを持つため、実際の処理はもう少し複雑です。
- クライアントからのリクエストURIのファイル名や拡張子をキーにして「Web.config」の
httpHandlers
要素から処理を実行するHTTPハンドラを検索します。 - 該当するオブジェクトを生成します。
- もし、登録されていたクラスがHTTPハンドラファクトリであれば、適切なHTTPハンドラのオブジェクトを作成させてそれを利用します(3の冒頭に戻ります)。
- 非同期HTTPハンドラはバックグラウンドタスクからのイベントを受け付ける
IAsyncResult
インターフェイスを実装したオブジェクトをASP.NETへ返します。 - ASP.NETはスレッドをプールへ戻します。
- バックグラウンドタスクからの終了通知を受けたオブジェクトは3.で与えられたデリゲートを呼び出します。
- ASP.NETは非同期HTTPハンドラの
EndProcessRequest
メソッドを呼び出してレスポンスをクライアントへ返します(終了)。
ProcessRequest
メソッドを呼び出してレスポンスをクライアントへ返します(終了)。BeginProcessRqeuest
メソッドを呼び出して処理を開始します(4.へ進みます)。この時、処理完了時に呼び出しを受けるデリゲートを引数に与えます。MSDNを読む限りでは、ASP.NETに非同期HTTPハンドラを用意した意図は、サーバーサイドで他のホストが提供するWebサービスを呼び出した場合に生じる待ち時間の有効活用のようです。しかし、この機構がCometを実装するのにも有効なことは明らかです。なぜならば、Cometの実装の要がまさに、イベントが発生するまで無駄になるリソース(ここではスレッド)の解放だからです。ASP.NETには非同期HTTPハンドラとして、あらかじめリクエスト処理を一時的に待ち状態に置くための機構が組み込まれています。