メッセージクライアント

サーバーとの違い

nine では connect による接続も accept による接続も、接続が完了した後の区別は無い。 よって、クライアント側のコードも、サーバーとほぼ同様の形になる。

server が accept しているところを、クライアントは connect するところが違う点である。

initialize

サンプルのクライアントはコマンドラインオプションを解析しているので複雑になっているが、 要は接続数を指定しているだけである。 不要な部分を省いて引用する:

   nine::MessageServer::Config cfg;
   cfg.nAcceptors = 0;
   cfg.nConnector = 1;
   client.initialize(&cfg);

connectLater

サーバーは受動的に接続を生成するが、クライアントは明示的に接続を行う必要がある。 サーバー側で MessageServer#listenLater を呼んだように、クライアント側では MessageServer#connectLater を呼べばよい。

void Client::connect()
{
   for (int i=0; i<m_cfg.nCommunicators; ++i) {
      m_id[i] = connectLater(host, port);
      if (m_id[i] < 0) {
	 return false;
      }
   }
   ...
複数の接続を扱うために複雑になっているが、結局は connectLater を呼んでいるだけである。

post

メッセージ送信には Messenger::post を使う。 Messenger は MessageServer の親クラスなので、post関数は MessageServer でも使うことができる。

サンプルでは post は run() 関数の中で呼び出している。 不要な部分を省いて引用する:
   TestMessage msg;
   msg.setString("hello, world.");
   int n = 0;
   while (true) {
      this->turn();
      post(m_id[0], &msg);
      nine::msleep(1);
   }

while 〜 turn 〜 msleep の流れはサーバー側の run() と同じである。 違いは、post() を行っている部分と、その post で使うためのメッセージオブジェクトを用意している部分である。

post() 内部で、送信用データのコピーが取られる。 このため、post() に与えたメッセージオブジェクトは実際の送信を待たずに廃棄や再利用をすることができる。 例えば次のように実装して問題はない:
  • 送信メッセージオブジェクトをスタック上に確保する。
  • 複数回の post で、同じメッセージオブジェクトを内容を変えて使いまわす。

post() 関数では最初に Communicator の id を渡し、二番目に送信するメッセージオブジェクトを渡す。 実際に送信するのは、以降に呼ばれた turn() である。