Boost Asio - обработка резолвера и сокетов с помощью shared_ptr - PullRequest
4 голосов
/ 07 февраля 2012

У меня есть класс, который сможет отправлять сообщения по 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_service, а также соответствующий поток управляются другим классом (который имеет экземпляр CommandScreenshot и вызывает функцию Dispatch).

Теперь нужно реализовать простое TCP-соединение с Boostвам нужен объект 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, чтобы избавиться от ненужной памяти?

Я знаю, что это длинный текст для конкретного короткого вопроса, и, кроме того, в нем даже нет ошибки.Но я всегда хотел бы знать, прилично ли я иду и если есть лучший способ, как я это сделаю.

1 Ответ

3 голосов
/ 22 февраля 2012

Идея определения членов как объектов 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.
}
...