ここまでで main() ループによる acceptor の準備と、その型の作成が終了した。 見て来たように、ほとんどコードを書く必要はないのである。 しかしながら、ここからは若干様相が変わる。
まずは Client_Handler クラスの宣言を行う client_handler.h を見てみよう。 その後にアプリケーションの要となる実際の定義を見ることにする。
// $Id$ #ifndef CLIENT_HANDLER_H #define CLIENT_HANDLER_H /* このクライアントハンドラは ACE_Event_Handler 型階層の中に存在しなければならない。 これは ACE_Reactor が ACE_Event_Handler ポインタを用いて、登録されたハンドラ群を管理しているからである。 ここで ACE_Event_Handler から直接 Client_Handler クラスを派生することもできるが、通信用の ACE_SOCK_Stream も準備しなければならない。 直接 ACE_Event_Handler から派生した場合には、メンバとして ACE_SOCK_Stream を管理するようコードを書く必要がある。 ACE_Svc_Handler(これは ACE_Event_Handler から派生している)を利用することで、その詳細部分を任せてしまうことができる。 */ #include "ace/Svc_Handler.h" #if !defined (ACE_LACKS_PRAGMA_ONCE) # pragma once #endif /* ACE_LACKS_PRAGMA_ONCE */ #include "ace/SOCK_Stream.h" /* ACE_Svc_Handler のもう一つの能力としては、ACE_Task<> インターフェースを提供していることだ。 そのために ACE_NULL_SYNCH が下で指定されている。 今回は省略するが、この部分が次のチュートリアルで並列オプションを設定する場合に重要となる。 */ class Client_Handler : public ACE_Svc_Handler <ACE_SOCK_STREAM, ACE_NULL_SYNCH> { public: // コンストラクタ Client_Handler (void); /* destroy() メソッドはオブジェクトの破棄を実装するのに推奨される場所だ。 delete 演算子をオーバーロードすることもできるが、(少なくとも著者にとっては)簡単でも直観的でもない。 その代わりに破棄操作のためのメソッドを新たに作成し、デストラクタを protected として、削除操作をフレンドと派生クラスからしか行えないようにする。 これはよい方法である。 */ void destroy (void); /* ACE のほとんどのオブジェクトは open() メソッドを持つ。 これは仕事の準備をするのによい場所である。 ACE_Event_Handler も仮想的な open() を持っており、オーバーライドが可能だ。 ACE_Acceptor<> はクライアントの接続に対し、Client_Handler オブジェクトを生成した後、このメソッドを呼び出す。 open() のパラメータは void* であることを覚えておこう。 ここにはオブジェクトを作成した acceptor へのポインタが格納される。 このパラメータを ACE_Acceptor<>* として利用することもできるが、より一般的に ACE_Event_Handler とする方が ACE_Acceptor<> との強い結合を作らないため、推奨される。 実際にこのメソッドの定義を見れば、これらのことについての理解が深まるだろう。 */ int open (void *acceptor); /* 登録済みのハンドラが活性化されると、 handler_input() が呼ばれる。 このメソッドが(-1 のような)エラーコードを返した場合には reactor はオブジェクトを終了させるために handler_close() を呼ぶ。 ハンドラはコールバックのタイプごとに複数登録できるため、どれが呼ばれたのか識別できるように handler_close() にはマスクも渡される。 そのため、各 handler_* メソッドによる状態の管理は必要なくなる。 また、handle パラメータについては以降に説明がある。 副作用として、(このメソッドが)-1 を返すと remove_handler() メソッドが呼ばれる。 これはつまり、自身でこのような呼び出しを行う必要は無いということである。 */ int handle_close (ACE_HANDLE handle, ACE_Reactor_Mask mask); protected: /* reactor への登録の際、READ イベントに対して報告するように指定を行う。 reactor は読み込みイベントがあると handle_input() を起動する。 その際、handle パラメータには起動の元となったハンドル(UNIX ではファイルディスクリプタ)が渡される。 ACE_Svc_Handler<> から派生した場合には、それが自身の peerオブジェクト(ACE_SOCK_Stream)を管理するため、これは冗長となる。 しかしながら、もし ACE_Event_Handler から直接派生していた場合にはオブジェクト中に peer を含まないかもしれない。 こういうケースでは handle パラメータがクライアントのデータを受け取る上で重要となる。 */ int handle_input (ACE_HANDLE handle); /* これは ACE とは何の関係もない。 このメソッドは handle_input() から呼び出されるワーカー関数として用意した。 これは後のチュートリアルでワーカー関数自体に変更を加えることなく並列化を進めるのに役立つ。 我々は、この process() をアプリケーションコードとして、その他をフレームワークコードとして考えることができる。 */ int process (char *rdbuf, int rdbuf_len); /* 今のところデストラクタで処理すべきことは無いが、一般の削除呼び出しを避けるために protected として宣言しておく。 上で述べているように、実際の処理は destroy() に実装する方が好みである。 */ ~Client_Handler (void); }; #endif /* CLIENT_HANDLER_H */