Запуск записи с помощью Boost :: asio - PullRequest
3 голосов
/ 25 февраля 2011

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

Оба потока имеют ссылку на объект boost :: asio :: io_service и объект Session, который инкапсулирует объект сокета.Объект sesson выглядит примерно так:

  class Session
  {
  public:

  Session(
    boost::asio::io_service & io_service,
    std::string const & ip_address,
    std::string const & port)
  : io_service_(io_service),
    resolver_(io_service),
    socket_(io_service),
    ip_address_(ip_address),
    port_(port),
  {}

  virtual void start();
  virtual ~Session();
  virtual void stop();
  void write(std::string const & msg);

  void handle_resolve(
    const boost::system::error_code & error,
    boost::asio::ip::tcp::resolver::iterator endpoint_itr);

  void handle_connect(
    const boost::system::error_code & error,
    boost::asio::ip::tcp::resolver::iterator endpoint_itr);

  void handle_close();
  void handle_write(const boost::system::error_code & error);

private:
  boost::asio::io_service & io_service_;
  boost::asio::ip::tcp::resolver resolver_;
  boost::asio::ip::tcp::socket socket_;
  std::string ip_address_;
  std::string port_;
};

В цикле выполнения потока ввода-вывода вызывается метод start () объекта сеанса, который подключается к серверу.(Это работает, кстати).Затем поток находится в цикле, вызывающем метод run () для объекта службы ввода-вывода [io_service_.run ()] для запуска событий.

Основной поток вызывает метод write () сеансакогда он хочет отправить данные, и объект сеанса вызывает boost :: async_write с данными для записи, а затем метод обратного вызова, который является членом объекта сеанса (handle_write).

Пока у меня есть I /О поток, соединяющийся с сервером, я не могу получить метод handle_write для запуска.Я проверил, что основной поток вызывает объект сеанса и выполняет async_write () в сокете.Просто обратный вызов никогда не срабатывает.Я также не вижу никаких данных на стороне сервера или по проводам с помощью tcpdump.

Есть идеи, где может быть моя проблема?Есть ли лучший способ организовать архитектуру?Более того, я не хочу блокировать основной поток, выполняя ввод / вывод.

Вот код, который порождает поток io из основного потока (извинения за интервал):

    boost::asio::io_service io_service;
    boost::shared_ptr<Session> session_ptr;
    boost::thread io_thread;
    ....
    session_ptr.reset(
      new Session::Session(
                io_service,
                std::string("127.0.0.1"),
                std::string("17001")));

    // spawn new thread for the network I/O endpoint
    io_thread = boost::thread(
      boost::bind(
        &Session::start,
        session_ptr_.get()));

Код метода start () выглядит следующим образом:

    void Session::start()
    {
      typedef boost::asio::ip::tcp tcp;

      tcp::resolver::query query(
          tcp::v4(),
          ip_address_,
          port_);  

      resolver_.async_resolve(
          query,
          boost::bind(
              &Session::handle_resolve,
              this,
              boost::asio::placeholders::error,
              boost::asio::placeholders::iterator));

      while(1){ // improve this later
        io_service_.run();
      }
    }

Обратный вызов для распознавателя:

    void Session::handle_resolve(
        const boost::system::error_code & error,
        boost::asio::ip::tcp::resolver::iterator endpoint_itr)
    {
      if (!error)
      {
        boost::asio::ip::tcp::endpoint endpoint = *endpoint_itr;
        socket_.async_connect(
            endpoint,
            boost::bind(
              &Session::handle_connect,
              this,
              boost::asio::placeholders::error,
              ++endpoint_itr));
      }
      else
      {
        std::cerr << "Failed to resolve\n";
        std::cerr << "Error: " << error.message() << std::endl;
      }
    }

Обратный вызов для соединения:

    void Session::handle_connect(
        const boost::system::error_code & error,
        boost::asio::ip::tcp::resolver::iterator endpoint_itr)
    {
      typedef boost::asio::ip::tcp tcp;

      if (!error)
      {
        std::cerr << "Connected to the server!\n";
      }
      else if (endpoint_itr != tcp::resolver::iterator())
      {
        socket_.close();
        socket_.async_connect(
            *endpoint_itr,
            boost::bind(
              &Session::handle_connect,
              this,
              boost::asio::placeholders::error,
              ++endpoint_itr));
      }
      else
      {
        std::cerr << "Failed to connect\n";
      }

    }

Метод write (), который основной поток может вызвать для отправки поста с асинхронной записью.

    void Session::write(
        std::string const & msg)
    {
      std::cout << "Write: " << msg << std::endl;
      boost::asio::async_write(
          socket_,
          boost::asio::buffer(
              msg.c_str(),
              msg.length()),
          boost::bind(
              &Session::handle_write,
              this,
              boost::asio::placeholders::error));     
    }

И, наконец, обратный вызов завершения записи:

    void Session::handle_write(
        const boost::system::error_code & error)
    {
      if (error)
      {
         std::cout << "Write complete with errors !!!\n";
      }
      else
      {
         std::cout << "Write complete with no errors\n";
      }        
    }

Ответы [ 2 ]

3 голосов
/ 25 февраля 2011

Похоже, что ваша служба io не будет работать после подключения, после чего вы просто вызываете io_service :: run снова?Похоже, что run вызывается в цикле while, однако я нигде не вижу вызова для сброса.Вам нужно вызвать io :: service :: reset, прежде чем снова вызывать run на том же io_service.

Конструктивно было бы лучше добавить work к io_service, тогда вам не нужно вызывать его в цикле, и запуск завершится, как только вы вызовете io_service :: stop.

2 голосов
/ 25 февраля 2011

эта часть вашего кода

boost::asio::io_service io_service;
boost::shared_ptr<Session> session_ptr;
boost::thread io_thread;
....
session_ptr.reset(
  new Session::Session(
            io_service,
            std::string("127.0.0.1"),
            std::string("17001")));

// spawn new thread for the network I/O endpoint
io_thread = boost::thread(
  boost::bind(
    &Session::start,
    session_ptr_.get()));

для меня красный флаг .Ваш io_service объект, возможно, выходит из области видимости и вызывает странное поведение.io_service не подлежит копированию, поэтому его передача в Session в качестве неконстантной ссылки, вероятно, не то, чего вы надеетесь достичь. HTTP-клиент пример, вы должны заметить, что io_service находится в области действия все время внутри main().Как отметил Ральф , вашему io_service также, вероятно, не хватает работы после обработчика соединения, поэтому вы включили его в команду run() внутри цикла

while(1){ // improve this later
  io_service_.run();
}

еще раз, обратите внимание, что пример HTTP client не делает этого.Вам нужно запустить еще одну асинхронную операцию внутри обработчика соединения, либо чтение, либо запись, в зависимости от того, что нужно вашему приложению.

...