class Server: public nine::MessageServer
{
public:
virtual bool initialize(nine::MessageServerConfig*);
void run();
virtual bool handleMessage(nine::Message* pmsg);
void handleTest(TestMessage*);
virtual void onAcceptorListen(int id, bool succeed);
virtual void onAcceptorAccept(int id, int cid);
virtual void onAcceptorClose(int id);
virtual void onAcceptorError(int id);
inline virtual void onCommunicatorConnect(int id, bool succeed);
inline virtual void onCommunicatorAccept(int id, int cid);
inline virtual void onCommunicatorClose(int id);
inline virtual void onCommunicatorError(int id);
inline virtual void onCommunicatorReceiveTimeout(int id);
inline virtual void onCommunicatorSendTimeout(int id);
メッセージサーバーの実装は、MessageServer クラスを継承して実装するのが基本である。
この MessageServer は TransceiverHandler も継承している。
MessageServer は、初期化のために MessageServer::initialize(const MessageServerConfig*) 関数を提供している。
class MessageServer {
...
virtual bool initialize(const MessageServerConfig* pCfg);
}
これを直接呼び出しても良いが、実際のアプリケーションでは独自の初期化処理も行いたいであろう。 具象クラスでも初期化関数を提供し、内部で MessageServer の initialize() を呼ぶようにするとよい。
bool Server::initialize(nine::MessageServerConfig* p)
{
if (! super::initialize(p)) { return false; }
m_listen = false;
return true;
}
サンプルの initialize() では、まずベースクラスの initialize() を呼んでからユーザークラス用の初期化処理を行っている。
MessageServer#initialize() では、自動的に Transceiver に自身を TransceiverHandler として登録する。このため、TransceiverHandler 登録処理を記述する必要はない。
MessageServer::initialize() へ渡す引数の型は MessageServerConfig として定義されている。
struct MessageServerConfig
{
MessageServerConfig();
MessageServerConfig(const MessageServerConfig&);
MessageServerConfig& operator=(const MessageServerConfig&);
int nAcceptors; //!< Acceptor の数。デフォルトは 0.
int nCommunicators; //!< Communicator の数。デフォルトは 0.
// int nHttpCommunicators; //!< HttpCommunicator の数。デフォルトは 0.
int nConcurrency; //!< turn() の並列数。0の場合は並列化しない。デフォルトは 0.
int sockSendBufferSize; //!< ソケットの送信バッファサイズ。負だとOS設定値を使う。デフォルトは -1.
int sockRecvBufferSize; //!< ソケットの受信バッファサイズ。負だとOS設定値を使う。デフォルトは -1.
int nineSendBufferSize; //!< 送信バッファのサイズ。デフォルトは 10000.
int nineRecvBufferSize; //!< 受信バッファのサイズ。デフォルトは 10000.
int turnTrafficKB; //!< 一回の turn() で送受信する最大データサイズ。
bool enableWinsockAio; //!< nine1.2.1 より。1.2以降導入のWindows非同期I/O を使うなら真。デフォルトは偽。
};
TransceiverConfig と同名のメンバは、TransceiverConfig と同じ意味である。
サーバー側は接続を待ち受ける必要がある。
MessageServer は内部に持つ Transceiver の listenLater を呼ぶだけの関数を提供しているので、これを使う。
int main(int argc, char** argv)
{
...
server.listenLater(opt.host, opt.port);
...
}
メッセージを受信すると、ハンドラと呼ばれる関数がコールされる。 ユーザーはハンドラ関数を実装することで、メッセージの処理を実装することができる。
ハンドラ関数には、個別のメッセージを処理するハンドラ関数と、全てのメッセージを受け取るディスパッチ関数がある。
ディスパッチ関数で全てのメッセージを受け取り、そのメッセージIDを識別して個別のハンドラ関数へ渡すという処理になる。
bool Server::handleMessage(nine::Message* pMsg)
bool Server::handleMessage(nine::Message* pMsg)
{
if (pMsg->getMessageId() == TestMessage::_MESSAGE_ID) {
return handleTest( static_cast< TestMessage* >(pMsg) );
} else {
return super::handleMessage(pMsg);
}
}
template <typename T, typename M> bool tryDispatch(void(T::*pf)(M*), Message* pMsg)
bool Server::handleMessage(nine::Message* pMsg)
{
if (tryDispatch(&Server::handleTest, pMsg)) {
return true;
} else {
return super::handleMessage(pMsg);
}
}
void handleTest(TestMessage*);
void Server::run()
{
while (true) {
turn();
nine::msleep(1);
}
}
MessageServer::turn() は内部で Transceiver::turn() を呼んでいるので、Transceiver::turn() を明示的に呼ぶ必要はない。