2017-04-20 113 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