ACE Tutorial 007
Creating a thread-pool server

それでは、いつも通り server.cpp から始めよう。



// $Id$


/* 我々は main() をとても単純に保とうとした。
   その方法の一つとして、複雑な部分をワーカーオブジェクトに任せている。
   このケースでは、メインソースで acceptor ヘッダーをインクルードすればよい。
   我々は"本来の仕事"に集中できる。
*/


#include "client_acceptor.h"


/* 以前と同様に、終了フラグをセットするだけのシンプルなシグナルハンドラを準備する。
   もちろん、これよりエレガントな終了方法はいくらでもあるだろうが、
   それは今回の目的ではないので、一番簡単な方法を取ることにする。
*/

static sig_atomic_t finished = 0;
extern "C" void handler (int)
{
  finished = 1;
}


/* サーバは知られている TCP/IP ポートでクライアントの接続を待つ。
   ACE のデフォルトポートは(少くとも私のシステムでは) 10002 であり、
   我々の目的には十分である。
   当然ながら、もっと頑強なアプリケーションであれば、コマンドラインや
   設定ファイルを利用して、賢くさせることができるだろう。
   しかしながら、シグナルハンドラの場合と同様に、それは我々の目的ではない。
   よって、この場合も単純な方法を取ることとする。
*/

static const u_short PORT = ACE_DEFAULT_SERVER_PORT;


/* 最後に main() を書く。
   いくつかのコンパイラは「関数シグネチャがプロトタイプと合わない」といって、
   うるさく警告を出すかもしれない。
   ここでもまた、パラメータを利用しないので、そのことを宣言しておく。
*/

int
main (int argc, char *argv[])
{
  ACE_UNUSED_ARG(argc);
  ACE_UNUSED_ARG(argv);

  
/* 以前のサーバではリアクターを得るためにグローバルなポインタを使った。
   この方法はあまり良くないので、今回は変数を main() 中に移した。
   Client_Handler オブジェクトの利用の時に、このポインタをどのように
   管理しているかを見ることになるだろう。
*/
  ACE_Reactor reactor;

  
/* acceptor はクライアントが接続しに来た時に面倒を見る。
   これは Client_Handler のアレンジであり、新規クライアントごとに作られる。
   我々は一つの TCP/IP ポートを使うと決めたので、acceptor も一つでよい。
   もし必要であれば、これをいくつか用意し、同時に複数のポートを待つこともできる。
    (That's what we would do if we wanted to rewrite inetd for instance.)
*/
  Client_Acceptor peer_acceptor;

  
/* 接続のエンドポイントを表す ACE_INET_Addr を用意する。
   その後、acceptor オブジェクトにそのアドレスを渡してオープンする。
   この操作により、acceptor にどのポートで接続を待つかを教えている。
   一般的にサーバは"よく知られた"ポートを利用する。
   そうしない場合、クライアントがサーバのアドレスを知るための何らかの
   メカニズムが必要となる。

   acceptor をオープンするのに失敗した場合の ACE_ERROR_RETURN の用法に注意。
   このテクニックはチュートリアル中で繰り返し使われる方法である。
*/
  if (peer_acceptor.open (ACE_INET_Addr (PORT), &reactor) == -1)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "%p\n",
                       "open"),
                      -1);

  
/* シグナルハンドラをインストールする。
   シグナルハンドラを reactor と一緒に、いくつか登録することもできる。
   シグナルハンドラが"実際の仕事"に責任を持つのであれば
   そのようにするのも良いだろう。
   しかし、我々の単純なフラグ設定用ハンドラは ACE_Event_Handler を継承しておらず、
   十分なコールバック関数も定義されていない(ので、そのようには使えない)。
*/
  ACE_Sig_Action sa ((ACE_SignalHandler) handler, SIGINT);

  
/* ACE_ERROR_RETURN と同様に、ACE_DEBUG マクロもよく登場する。
   これはプログラムから統一されたデバッグ出力を生成するのに丁度よい。
*/
  ACE_DEBUG ((LM_DEBUG,
              "(%P|%t) starting up server daemon\n"));

  
/* このループは"いつまでも" reactor の handle_events()
   メソッドを起動し続ける。
   handle_events() は登録されたハンドラについて監視を続け、必要があれば
   適切なコールバックを起動する。
   コールバックプログラミングは ACE の重要な位置を占める。
   もしシグナルハンドラが何かをキャッチしたら、終了フラグがセットされ、
   我々のプログラムは終了する。
   便利なことに、handle_events() はシグナルによる割り込みを受けると
   while ループまで戻るようになっている。(もしイベントループにおいて
   シグナルによる割り込みを回避したいのならば、ACE_Reactor のrestart
   フラグについて調べるとよい)
*/
  while (!finished)
    reactor.handle_events ();

  ACE_DEBUG ((LM_DEBUG,
              "(%P|%t) shutting down server daemon\n"));

  return 0;
}

#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION)
template class ACE_Acceptor <Client_Handler, ACE_SOCK_ACCEPTOR>;
template class ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>;
template class ACE_Guard<ACE_Mutex>;
template class ACE_Atomic_Op<ACE_Mutex, int>;
#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA)
#pragma instantiate ACE_Acceptor <Client_Handler, ACE_SOCK_ACCEPTOR>
#pragma instantiate ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>
#pragma instantiate ACE_Guard<ACE_Mutex>
#pragma instantiate ACE_Atomic_Op<ACE_Mutex, int>
#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */

ふむ…これは前回と全く同じように見える。 実際のところ、コメント部分を除き、このソースは変更されていない。 それでは続いて client_acceptor.h に行ってみよう。


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