チュートリアル1と同様に、今回も簡単なプログラムである。 新しいアイデアをいくつか盛り込む予定ではいるが、それで不用意に飾り立てるつもりはない。 細心の注意を払ってシンプルなアクセプタにするつもりである。
Kirthika による概要
ここで Logging_Acceptor は Logging_Handler や ACE_SOCK_ACCEPTOR と協調して動作する ACE_Acceptor クラスのことだ。 クライアントからの接続要求を待ち受け、通信を開いた上でリアクタへ渡すのが役目である。
また、今回は Ctrl-C で発生する(SIGINTという)シグナルも ACE_SigAction と ACE_SignalHandler を通じて取り扱う。 このシグナルはリアクタの(無限ループによる)イベントハンドリングを停止させる目的で利用する。
つまりリアクタは Ctrl-C によるシャットダウンが発生するまで無限にループし続け、その後はアクセプタと共に破棄される。
Logging_Handler は Event_Handler ではなく、SOCK_Stream を持ち、リアクタからの呼び出しに応対できる ACE_Svc_Handler から派生される。 Svc_Handler はイベントに応答し、渡されたデータストリームに対してやりとりを行う能力を持つ。
リアクタにおけるタイマも、その利用方法を示すために簡単な実装が行われる。 その時間周期を決定するのが ACE_TimeValue である。
また、オブジェクトの破棄に関して関数の分離を行うなどの最適化も施してある。
ではこれから ACE コンポーネントを適切に利用することで、単純なサーバをどれほど簡潔に作成することができるか、お目にかけよう。
それでは main (server.cpp) 関数からプログラムを見ていこう。
// $Id$
/*
  Logging_Handler のために必要ないくつかの ACE オブジェクトを宣言する。
*/
#include "ace/Acceptor.h"
#include "ace/SOCK_Acceptor.h"
#include "ace/Reactor.h"
#include "handler.h"
/*
  ここでもリアクタはグローバルポインタとして利用する。
  後のサーバチュートリアルで改善するので、それまで待ってほしい。
*/
ACE_Reactor *g_reactor;
/*
  これはチュートリアル1でも触れたが、アクセプタは手書きではなくテンプレートを利用して作成した方が簡単である。
  アプリケーションに直接関係無いコードを書いていると感じた時こそ ACE のコンポーネントの出番である。
*/
typedef ACE_Acceptor <Logging_Handler, ACE_SOCK_ACCEPTOR> Logging_Acceptor;
/*
  新しい項目の一つは綺麗にプログラムを終了させるためのシグナルハンドラ設定である。
  無限ループの代わりに"finished"フラグが利用され、SIGINT が(Ctrl-Cによって)発生するとハンドラがフラグを操作する。
  ACE_Reactor::notify() の呼び出しはフラグの新しい値を読み出させるために handle_events() から一度戻らせる。
static sig_atomic_t finished = 0;
extern "C" void handler (int)
{
  finished = 1;
  g_reactor->notify();
}
static const u_short PORT = ACE_DEFAULT_SERVER_PORT;
int
main (int, char **)
{
  // イベントハンドラ登録のために Reactor を生成する。
  ACE_NEW_RETURN (g_reactor,
                  ACE_Reactor,
                  1);
  // クライアントの接続要求を受け入れる acceptor を生成する。
  Logging_Acceptor peer_acceptor;
  
  /*
    この open() 呼び出しがチュートリアル1のそれと同様であることに注目してほしい。
  */
  if (peer_acceptor.open (ACE_INET_Addr (PORT),
                          g_reactor) == -1)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "%p\n",
                       "open"),
                      -1);
  
  /*
    これがアプリケーションの簡単なシグナル応答設定である。
    捕捉すべきシグナルと C 形式の関数を渡して ACE_Sig_Action オブジェクトを生成すればよい。
    想像できるようにシグナルハンドラをリアクタに登録する方法もあるが、今回は簡単な方法を選んだ。
 */
  ACE_Sig_Action sa ((ACE_SignalHandler) handler, SIGINT);
  ACE_DEBUG ((LM_DEBUG,
              "(%P|%t) starting up server logging daemon\n"));
  // SIGINT シグナルをハンドラが受けるまで、ロギングサービスを継続する。
  while (!finished)
    g_reactor->handle_events ();
  // これ以上の接続を受け入れないため、acceptor を閉じる。
  peer_acceptor.close();
  // リアクタに割り当てられたメモリを解放する。
  delete g_reactor;
  ACE_DEBUG ((LM_DEBUG,
              "(%P|%t) shutting down server logging daemon\n"));
  return 0;
}
#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION)
template class ACE_Acceptor <Logging_Handler, ACE_SOCK_ACCEPTOR>;
template class ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>;
#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA)
#pragma instantiate ACE_Acceptor <Logging_Handler, ACE_SOCK_ACCEPTOR>
#pragma instantiate ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>
#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */