【问题标题】:Boost Asio - handling resolver and sockets with shared_ptrBoost Asio - 使用 shared_ptr 处理解析器和套接字
【发布时间】:2012-02-07 13:57:45
【问题描述】:

我有一个类能够通过 TCP 发送消息。这里是简化的界面:

class CommandScreenshot : public CameraCommand
{
public:
    CommandScreenshot();
    ~CommandScreenshot();
    void Dispatch(boost::shared_ptr<boost::asio::io_service> io_service);

 private:
     void resolve_handler(const boost::system::error_code& err,
          boost::asio::ip::tcp::resolver::iterator endpoint_iterator);

};

如你所见,我有一个函数Dispatch,它实际上只是为了启动异步操作:

void CommandScreenshot::Dispatch(boost::shared_ptr<boost::asio::io_service> io_service)
{
    boost::asio::ip::tcp::resolver resolver(*io_service);
    boost::asio::ip::tcp::resolver::query query(m_hostname,"http");
    resolver.async_resolve(query,boost::bind(&CommandScreenshot::resolve_handler,this,boost::asio::placeholders::error, boost::asio::placeholders::iterator));
    return;
}

其他一切都将在以下回调函数中完成。 io_serviceobject 以及对应的线程由另一个类管理(它有一个CommandScreenshot 的实例并调用Dispatch 函数)。

现在要使用 Boost 实现一个简单的 TCP 连接,您需要一个 resolver 和一个 socket 对象,两者都绑定到 io_service 对象。由于io_service对象只会在当时被传递,所以函数被调用,我无法在类构造函数中初始化它们。 也不可能将它们声明为类成员,然后在函数本身中初始化它们。

我的第一个想法是在函数调用时初始化它们并将它们传递给我的完成处理程序。这意味着我每次调用函数时都声明这两个对象并将它们绑定到io_service。然后在async_resolve,我通过boost::bind 将两者都添加为参数。这意味着我的 resolve_handler 会期待更多的论点 - 例如:

void resolve_handler(const boost::system::error_code& err,
          boost::asio::ip::tcp::resolver::iterator endpoint_iterator,
          boost::asio::ip::tcp::resolver resolver,
          boost::asio::ip::tcp::socket socket);

我实际上怀疑这是一个体面和公平的解决方案。通常这些对象应该作为成员保留,而不是被复制 - 所以我再想一想,我的想法把我带到了boost::shared_ptr

在我的标题中,它现在看起来像这样:

// Stuff above stays the same
private:
  boost::shared_ptr<boost::asio::ip::tcp::resolver> m_resolver;
  boost::shared_ptr<boost::asio::ip::tcp::socket> m_socket;
// Stuff below stays the same

而实现是:

void CommandScreenshot::Dispatch(boost::shared_ptr<boost::asio::io_service> io_service)
{
    m_resolver.reset(new boost::asio::ip::tcp::resolver(*io_service));
    m_socket.reset(new boost::asio::ip::tcp::socket(*io_service));
    boost::asio::ip::tcp::resolver::query query(m_hostname,"http");
    m_resolver->async_resolve(query,boost::bind(&CommandScreenshot::resolve_handler,this,boost::asio::placeholders::error, boost::asio::placeholders::iterator));
    return;
}

有了这个,我不需要复制 4 个(甚至更多)参数并将它们绑定在一起。当我需要套接字对象时,我可以通过指针访问它,它是一个类成员。

现在我的简单问题是 -> 这是正确的方法吗?该函数可以被多次调用,即使异步部分没有完成。 (我知道我应该用互斥锁保护套接字和解析器)。但这干净吗,我每次调用Dispatch 函数时都会创建一个新对象? reset 调用是否足以消除任何不需要的内存?

我知道这是针对特定短问题的长文本,而且本身甚至没有错误。但我总是想知道我要走的路是否不错,如果有更好的路,我会怎么做。

【问题讨论】:

    标签: c++ sockets boost boost-asio shared-ptr


    【解决方案1】:

    将 memebers 定义为 shared_ptr 到 asio 对象的想法是可以的。但是你不应该每次都销毁和创建它们:

    if (!m_resolver)
    {
      m_resolver.reset(...);
    }
    

    此外,如果您确保对 asio 对象的所有操作都发生在运行 io_service 的线程中(假设每个 io_service 有一个线程),您可以避免显式锁定。为此,只需分离接口函数的实现并使用 post()。当然,也可以使用 shared_from_this 来简化对象生命周期控制:

    void CommandScreenshot::someMethod(Arg1 arg1, Arg2 arg2)            
    {
      io_.post(bind(&CommandScreenshot::someMethodImpl, shared_from_this, arg1, arg2)); 
    }
    //...
    void CommandScreenshot::someMethodImpl(Arg1 arg1, Arg2 arg2)            
    {
      // do anything you want with m_resolver, m_socket etc.
    }
    

    【讨论】:

      猜你喜欢
      • 2014-01-24
      • 2012-01-16
      • 1970-01-01
      • 1970-01-01
      • 2012-10-26
      • 2011-07-05
      • 2011-10-15
      • 2011-12-05
      • 2020-06-17
      相关资源
      最近更新 更多