2017-04-20 110 views
1

在我的系統中,我有一堆TCP客戶端,我對如何設計它有點困惑[我的大部分經驗都在C中,因此不安全] 。我正在使用boost ASIO來管理連接。這些是分量I具有C++設計:多個TCP客戶端,提升asio和觀察者

  • 甲TCPStream類:瘦包裝過大加力ASIO
  • 的IPC協議,通過TCP實現協議: 基本上每個消息具有類型和長度字段 所以我們可以開始從流中讀取單個消息。其處理消息
  • 連接類
  • 其監視連接

我寫僞C++代碼觀察員類要簡潔。我想你會得到的想法

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建立,然後開始連接3等

我增加了一箇中間觀測類是存在的,這樣的觀察員每次重新啓動時都必須直接連接到連接。每次連接斷開時,連接類都會被刪除,並創建一個新連接。

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連接一個線程,這工作得很好。在這裏,我試圖處理同一個線程上的多個連接。仍然會有多個線程調用ioservice。所以觀察者將在多個線程上運行。我計劃每個Listener都有一個互斥體,以便聽衆不會同時獲得多個事件。

回答

0

HTTP通過TCP實現協議,因此HTTP服務器asio examples是您設計的良好起點,特別是:HTTP Server 2,HTTP Server 3HTTP Server 4

注意:該連接的生命週期可能是一個問題,尤其是因爲您打算使用類成員函數作爲處理程序,請參閱此處的問題和答案:How to design proper release of a boost::asio socket or wrapper thereof

+0

查看HTTP示例確實有幫助。感謝指針。至於生活的時間。 Connection在每次執行異步操作時都會將shared-ptr傳遞給自己,以保持它處於活動狀態,直到調用異步完成處理程序。你有沒有看到這個漏洞? – MGH

+0

是@MGH我確實看到了漏洞,特別是:內存和資源泄漏。我更喜歡服務器或客戶端*擁有* shared-ptr,並使用'non-'member'(或'static')函數回調函數將weak-ptr傳遞給連接。例如,參見連接類[here](https://github.com/kenba/via-httplib/tree/master/include/via/comms)。 – kenba

+0

我看了一下'class Connection'。我只看到與我在做什麼不同的幾點 1.創建共享指針的靜態'create'例程。我認爲這個想法是強制連接始終創建爲共享指針? (不知道爲什麼在'create()'中使用'make_shared') - 沒關係。但我的是一個僞代碼來顯示階級關係,我跳過了細節。 – MGH