2017-04-20 13 views
1

私のシステムでは、私はジャグルのTCPクライアントの束を持っていると私はちょっと混乱している[私の経験のほとんどはCで、したがって不安です] 。私はブーストASIOを使用して接続を管理しています。これらは私が持っている要素であるC++デザイン:複数のTCPクライアント、ブーストasioとオブザーバー

  • A TCPStreamクラス:ブースト上に薄いラッパーASIO
  • TCP上のプロトコルを実装するIPCプロトコルは、: は基本的に各メッセージはタイプと長さフィールド ので、私達ができるで始まりますストリームから個々のメッセージを読み込みます。
  • 私は簡潔にするために、擬似C++のコードを書いています接続

を監視し、メッセージ

  • Observerクラスを扱う接続クラス。私はあなたが

    class TCPStream { 
        boost::asio::socket socket_; 
    public: 
    
        template <typename F> 
        void connect (F f) 
        { 
         socket_.connect(f); 
        } 
    
        template <typename F> 
        void read (F f) 
        { 
         socket_.read(f); 
        } 
    }; 
    
    class IpcProtocol : public TCPStream { 
    public: 
        template <typename F 
        void read (F f) 
        { 
         TCPStream::read(
           [f] (buffer, err) { 
    
           while (msg = read_indvidual_message(buffer)) { 
             // **** this is a violation of how this pattern is 
             // supposed to work. Ideally there should a callback 
             // for individual message. Here the same callback 
             // is called for N no. of messages. But in our case 
             // its the same callback everytime so this should be  
             // fine - just avoids some function calls. 
             f(msg); 
           }; 
           }; 
         ) 
        } 
    }; 
    

    私はTCPコネクションの束を持っていると、接続ごとにハンドラクラス があるとしましょうアイデアを得ると思います。名前をConnection1、Connection2 ...

    class Connection { 
        virtual int type() = 0; 
    }; 
    
    class Connection1 : public Connection { 
    
        shared_ptr<IpcProtocol> ipc_; 
    
        int type() 
        { 
         return 1; 
        } 
    
        void start() 
        { 
         ipc_.connect([self = shared_from_this()](){ self->connected(); }); 
    
         ipc_.read(
          [self = shared_from_this()](msg, err) { 
    
           if (!err) 
            self->process(msg); 
           } else { 
            self->error(); 
           } 
          }); 
        } 
    
        void connected() 
        { 
         observer.notify_connected(shared_from_this()); 
        } 
    
        void error() 
        { 
         observer.notify_error(shared_from_this()); 
        } 
    }; 
    

    このパターンは、一方向などのすべての接続で繰り返されます。 メッセージは接続クラス自体によって処理されます。しかし、それは 他のイベント[接続、エラー]をオブザーバに知らせるでしょう。理由 -

    1. 再起動接続は、毎回それはみんなの
    2. バンチは、彼らが サーバーへの最初の要求/ confgurationを送ることができるように、接続が確立されるかどうかを知る必要があります切断します。
    3. muliple接続の接続ステータス 例:に基づいて行うことが必要なものがあります。接続1と接続2が確立されている場合は、connection3など

    を開始私はミドルObserverクラスを追加したように、そこにオブザーバーであります再起動するたびに接続に直接接続する必要があります。接続が切断されるたびに、接続クラスが削除され、新しい接続クラスが作成されます。

    class Listeners { 
    public: 
        virtual void notify_error(shared_ptr<Connection>) = 0; 
        virtual void notify_connect(shared_ptr<Connection>) = 0; 
        virtual void interested(int type) = 0; 
    }; 
    
    
    class Observer { 
        std::vector<Listeners *> listeners_; 
    public: 
    
        void notify_connect(shared_ptr<Connection> connection) 
        { 
         for (listener : listeners_) { 
          if (listener->interested(connection->type())) { 
           listener->notify_error(connection); 
          } 
         }  
        } 
    }; 
    

    ここでは、原型の試作品です。しかし、私はこのクラスのデザイン 良いかどうか疑問に思っていた。連続して状態を生成し、それを私のモジュールに送信して状態をh/wでプログラムする複数のストリーミングサーバがあります。これは将来的に多くのクライアントが追加されるため、拡張可能でなければなりません。レガシーコード

    スレ

    は、TCPコネクションごとに1つのスレッドを持っていたし、これがうまく働きました。ここでは、同じスレッドで複数の接続を処理しようとしています。それでも、ioserviceを呼び出す複数のスレッドがあります。したがって、オブザーバは複数のスレッドで実行されます。リスナーごとに複数のイベントを同時に取得しないように、リスナーごとにミューテックスを用意する予定です。

  • 答えて

    0

    HTTP HTTPサーバーasio examplesは、特にHTTP Server 2,HTTP Server 3およびHTTP Server 4のような、設計にとっては適切な出発点です。

    注:特に、クラスメンバー関数をハンドラとして使用する予定であるため、接続の有効期間が問題になる可能性があります。How to design proper release of a boost::asio socket or wrapper thereofの質問と回答を参照してください。

    +0

    HTTPの例を見ると助けになりました。ポインタありがとう。生涯については。非同期補完ハンドラが呼び出されるまで、非同期操作を実行しているときには常に、shared-ptr自体が共有されます。あなたはこれに穴が見えますか? – MGH

    +0

    はい@MGH私は穴、特にメモリとリソースのリークがあります。私はshared-ptrを所有する*サーバー*クライアントを好んでおり、 'non-amember'(または 'static')関数コールバックを使用して接続にweak-ptrを渡します。たとえば、接続クラス[here](https://github.com/kenba/via-httplib/tree/master/include/via/comms)を参照してください。 – kenba

    +0

    私は 'class Connection'を見ました。私は何をしているのか分かりません。 1.共有ポインタを作成する静的 'create'ルーチンです。私はそのアイデアは接続を常に共有ポインタとして作成することを強制することだと思いますか? ( 'make()'の中でなぜ 'make_shared'が使われているのか分かりません) - 大丈夫です。しかし、私はクラスの関係を示す疑似コードです。詳細をスキップしました。 – MGH

    関連する問題