ACE Tutorial 001
初心者のための ACE ツールキットガイド
それでは logger オブジェクトを見ていくことにしよう。
// $Id$
#ifndef _CLIENT_HANDLER_H
#define _CLIENT_HANDLER_H
/*
コネクションハンドラも ACE_Event_Handler クラスから派生させる。
これは Reactor へ登録できるようにするためである。
*/
#include "ace/Event_Handler.h"
#if !defined (ACE_LACKS_PRAGMA_ONCE)
# pragma once
#endif /* ACE_LACKS_PRAGMA_ONCE */
#include "ace/INET_Addr.h"
/*
TCP/IP 接続を行うためには SOCK_Stream が必要となる。
*/
#include "ace/SOCK_Stream.h"
class Logging_Handler : public ACE_Event_Handler
{
public:
/*
acceptor と同様、単純化のためコンストラクタとデストラクタは省く。
*/
/*
クライアントハンドラを open する際には(Logging_Handler)自身を
reactor へ登録する。
この時点では ACE_SOCK_Stream メンバ変数を open() する必要は無い。
なぜか?
acceptor の accept() メソッドがこの面倒をみるからである。
*/
int open (ACE_Reactor *reactor)
{
/*
リアクタの場所を覚えておこう。
*/
reactor_ = reactor;
/*
今回は READ_MASK を利用する。
acceptor の時と同様、reactor は handle_input() 関数を呼び出すが
違う役割に違うマークを付けておくのは良い管理方法である。
*/
if (reactor->register_handler (this,
ACE_Event_Handler::READ_MASK) == -1)
ACE_ERROR_RETURN ((LM_ERROR,
"(%P|%t) can't register with reactor\n"),
-1);
return 0;
}
/*
もし明示的に close() が呼ばれたら、ファイルハンドルを
閉じなければならない。
この結果として、クライアントとの接続は閉じ、もし登録済みであればリアクタからも登録を抹消する。
*/
int close (void)
{
return this->handle_close (ACE_INVALID_HANDLE,
ACE_Event_Handler::RWE_MASK);
}
/*
これは些細な魔術である。
acceptor オブジェクトに対して accept() メソッドが呼ばれる時には
(そのパラメータは)ACE_SOCK_Stream として扱われる。
我々はクライアントとの接続のためにそれを(メンバ変数として)持っているので
それをそのまま提供すればよい。
このキャスト演算子の実装により、acceptor は Logging_Handler を
あたかも ACE_SOCK_Stream のように取り扱うことができるようになる。
*/
operator ACE_SOCK_Stream &()
{
return this->cli_stream_;
}
protected:
/*
繰り返すが、acceptor と同様にリアクタへコネクションハンドルを渡す必要がある。
*/
ACE_HANDLE get_handle (void) const
{
return this->cli_stream_.get_handle ();
}
/*
ここが handle_input() 関数である。
これが実際のアプリケーションの動作となる。
*/
virtual int handle_input (ACE_HANDLE)
{
/*
小さい受信用バッファを準備して初期化する。
追加の1バイトは文字列における終端の NULL 文字でオーバーフローしないためだ。
*/
char buf[BUFSIZ + 1];
/*
データを読み出すために ACE_SOCK_Stream の recv() を呼ぶ。
エラーが発生した場合には -1 が返る。
もしくは読み出したバイト数を返す。
もちろん、0バイト読み出した場合はコネクションが切れたという意味だ。
この場合をどうやって知ればいいのだろうか?
実は handle_input() は、リアクタへの read によって特定のイベントが
発生した場合やコネクションが切れた場合には呼び出されないのである。
しかし、もし切断済みのコネクションから読み出しを行った場合には
0バイトが返ることになる。
つまり、エラーや切断の場合には -1 が返ると覚えておけばよい。
これらの場合にはリアクタは正しい切断のために handle_close() を
呼び出してくれる。
今回は利用していないが、recv() 呼び出しには有用な
追加パラメータがいくつかある。
その一つは ACE_Time_Value で、recv() がブロックする時間を
限定することができる。
これはデータがまだ利用できない状態の時に用いる。
もちろん、handle_input() の呼び出しはデータが利用可能になってから
実施されるため、今回は冗長なパラメータである。
一方、recv_n() を利用した場合は確実に指定したバイト分だけ
読み出そうとするため、待ち時間を規定するために利用すると良い。
その他のパラメータは数値のフラグである。
これはそのまま OS の recv() コールへ渡される。
この詳細は sys/socket.h ヘッダや、マニュアルの recv(2) を
参照するようにしてほしい。
*/
ssize_t retval;
switch (retval = this->cli_stream_.recv (buf, BUFSIZ))
{
case -1:
ACE_ERROR_RETURN ((LM_ERROR,
"(%P|%t) %p bad read\n",
"client logger"),
-1);
case 0:
ACE_ERROR_RETURN ((LM_ERROR,
"(%P|%t) closing log daemon (fd = %d)\n",
this->get_handle ()),
-1);
default:
buf[retval] = '\0';
ACE_DEBUG ((LM_DEBUG,
"(%P|%t) from client: %s",
buf));
}
return 0;
}
/*
もし handle_input() が -1 を返した場合、この関数で終了処理を行う。
ここでは少しのハンドル処理を行っている。
*/
int handle_close (ACE_HANDLE,
ACE_Reactor_Mask _mask)
{
/*
リアクタから自身を抹消する。
handle_close() が再呼び出しされないように DONT_CALL を指定する。
*/
reactor_->remove_handler (this,
_mask | ACE_Event_Handler::DONT_CALL);
/*
クライアントへ接続しているソケットを閉じる。
*/
cli_stream_.close ();
/*
このオブジェクトは acceptor によって動的に作成されたので
自身を解放するのはここが丁度良い。
*/
delete this;
return 0;
}
protected:
/* クライアントとの接続 */
ACE_SOCK_Stream cli_stream_;
/* 登録先の(かつ acceptor も登録されている)リアクタ */
ACE_Reactor *reactor_;
};
#endif /* _CLIENT_HANDLER_H */
伝えるべきことはコメントに全て書いてある。 重要なのは handle_input() あたりである。 この関数以外は雑事にすぎない。 これらの雑事の大部分を処理してくれる ACE_Svc_Handler<> については後から学ぶことにしよう。