ACE Tutorial 005
On the road to a multithreaded server


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


[インデックスへ] [次へ進む]