Server

server

サーバー実装を見る。

sample/server は以下のような動作になっている:
  • 接続受付は一つで、受け付ける接続は多数
  • 全ての接続に対して、データが来ているかどうか確認する
  • データが来ていれば受信(消費)し、1byte ずつ char として出力する。

クラス宣言

class Server: public nine::TransceiverHandler
{
   public:
      Server();
      virtual ~Server();
      void init(int nTransceivers);
      bool listenLater(uint16_t port);
      void run();

   public:
      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);

   private:
      bool m_listen;
      nine::Transceiver* m_pTransceiver;
};

クラス定義

Client と同様である。

Transceiver の生成と TransceiverHandler の割り当て

これもおおよそは Client と同様である。

一つ大きな違いは、接続を待ち受けるために Transceiver::Create() の Acceptor 数を 1 に指定していることである。

void Server::init(int n)
{
   m_pTransceiver = nine::Transceiver::Create(1, n);
   m_pTransceiver->setHandler(this);
   m_listen = false;
}

TransceiverHandler の実装

Client は能動的に接続を行うので、Communicator 系のハンドラを使っていた。 対して Server は受動的に接続を待つので、Acceptor 系のハンドラを使う。

void Server::onAcceptorListen(int id, bool succeed)
{
   m_listen = succeed;
   printf("listen: %s(%d)\n", (succeed)? "succeed": "failed", id);
}

void Server::onCommunicatorAccept(int id, int cid)
{
   printf("accept: id=%d\n", cid);
   nine::Communicator* p = m_pTransceiver->getCommunicator(cid);
   if (p) {
//      p->dump(true);
   }
}

接続確立時に onCommunicatorAccept を使っているが、これは onAcceptorAccept を使ってもいい(両方呼ばれる)。

Transceiver::turn の呼び出し

これも Client と同じく、無限ループで呼んでいる。

void Server::run()
{
   int n = m_pTransceiver->getCommunicatorSize();
   while (true) {
      m_pTransceiver->turn();
      ...
      nine::msleep(1);
   }
}

データの受信

Communicator::getReceiveBuffer() で受信用 Buffer を取得し、Buffer の読み込み関数を使う。
   int n = m_pTransceiver->getCommunicatorSize();
         for (int i=0; i<n; ++i) {
            nine::Communicator* pComm = m_pTransceiver->getCommunicator(i);
            if (pComm) {

n は接続可能数なので、現時点での接続数とは異なる。 未接続の場合は getCommunicator() は NULL を返すので、確認する。

続いて受信バッファを得て、データを読み込んでいる。
               nine::Buffer* pBuf = pComm->getReceiveBuffer();
               int8_t ch;
               while (! pBuf->isEmpty()) {
                  pBuf->readInt8(&ch);
                  printf("%c", reinterpret_cast< char* >(ch));
               }
               fflush(0);

使用しているバッファ操作は isEmpty() と readInt8() の二つである。

isEmpty() は読み込み可能なデータがあるかどうかを真偽値で返す。 while 文によって、データが空になるまで繰り返すようになっている。

実際にデータを読むのは readInt8() である。 これは名のとおり、符号付き8bit整数を読み込む。つまり 1byte 読んでいる。

1byte ずつ読むたびに while を回しているのは効率が悪い。 実アプリケーションでは、readableSize() と readArray() や getReadBuffers() を使って一気に処理すべきであろう。

データの送信

Server サンプルではデータ受信は実装していないが、もちろんサーバーでもデータの受信は可能である。 実装方法は Client サンプルを参照されたい。