ここまでで 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 */