【问题标题】:issues about using async_write right after async_read_until在 async_read_until 之后使用 async_write 的问题
【发布时间】:2015-02-14 03:04:06
【问题描述】:

我的代码如下:

boost::asio::streambuf b1;

boost::asio::async_read_until(upstream_socket_, b1, '@',
           boost::bind(&bridge::handle_upstream_read, shared_from_this(),
           boost::asio::placeholders::error,
           boost::asio::placeholders::bytes_transferred));


void handle_upstream1_read(const boost::system::error_code& error,
                            const size_t& bytes_transferred)
  {
     if (!error)
     {

       async_write(downstream_socket_,
          b2,
          boost::bind(&bridge::handle_downstream_write,
          shared_from_this(),
          boost::asio::placeholders::error));  
     }
     else
        close();
  }

根据 async_read_until 的文档,http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference/async_read_until/overload1.html, 在成功执行 async_read_until 操作后,streambuf 可能包含超出分隔符的其他数据。应用程序通常会将该数据留在 streambuf 中,以供后续的 async_read_until 操作检查。

我知道 streambuf 可能包含超出分隔符的附加数据,但在我的情况下,它会在 async_write 操作中将这些附加数据(char'@' 之外的数据)写入下游套接字吗?或者 async_write 函数是否足够聪明,不会在下次调用 handle_upstream1_read 函数之前写入这些额外数据?

根据文档中的方法,streambuf 中的数据首先存储在 istream 中( std::istream response_stream(&streambuf); ) 然后使用 std::getline() 函数将其放入字符串中。

我真的需要先将streambuf存储在istream中,然后将其转换为字符串,然后再将其转换回char数组(这样我就可以将char数组发送到downstream_socket_),而不是仅仅使用async_write来写入数据(直到但不包括分隔符,'@')到下游_socket_?

我更喜欢第二种方法,这样我就不需要对数据进行多次转换。但是,当我尝试第二种方法时,似乎出了点问题。

我的理想情况是:

  1. upstream_socket_ 使用 async_read_until 收到 xxxx@yyyy
  2. xxxx@ 被写入到下游socket_
  3. upstream_socket_ 使用 async_read_until 收到 zzzz@kkkk
  4. yyyyzzzz@ 被写入下游套接字_

async_write 操作似乎仍然将超出分隔符的数据写入到下游套接字。 (但我对此不是 100% 确定)

如果有人能提供一点帮助,我将不胜感激!

【问题讨论】:

    标签: boost-asio asyncsocket


    【解决方案1】:

    streambuf 的所有数据(其输入序列)已写入WriteStream(套接字)时,正在使用的async_write() 重载被认为是完整的。相当于调用:

    boost::asio::async_write(stream, streambuf,
        boost::asio::transfer_all(), handler);
    

    可以通过使用boost::asio::transfer_exactly 完成条件调用此async_write() 重载来限制从streambuf 对象写入和消耗的字节数:

    boost::asio::async_write(stream, streambuf, 
        boost::asio::transfer_exactly(n), handler);
    

    或者,可以直接从流缓冲区的输入序列中写入。但是,需要从 streambuf 中显式使用。

    boost::asio::async_write(stream,
        boost::asio::buffer(streambuf.data(), n), handler);
    // Within the completion handler...
    streambuf.consume(n);
    

    请注意,当async_read_until() 操作完成时,完成处理程序的bytes_transferred 参数包含streambuf 输入序列中直到并包括分隔符的字节数,如果发生错误,则包含0


    这是一个使用这两种方法的完整示例demonstrating。该示例是使用同步操作编写的,旨在简化流程:

    #include <iostream>
    #include <boost/asio.hpp>
    #include <boost/bind.hpp>
    
    // This example is not interested in the handlers, so provide a noop function
    // that will be passed to bind to meet the handler concept requirements.
    void noop() {}
    
    /// @brief Helper function that extracts a string from a streambuf.
    std::string make_string(
      boost::asio::streambuf& streambuf,
      std::size_t n)
    {
      return std::string(
          boost::asio::buffers_begin(streambuf.data()),
          boost::asio::buffers_begin(streambuf.data()) + n);
    }
    
    int main()
    {
      using boost::asio::ip::tcp;
      boost::asio::io_service io_service;
    
      // Create all I/O objects.
      tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 0));
      tcp::socket server_socket(io_service);
      tcp::socket client_socket(io_service);
    
      // Connect client and server sockets.
      acceptor.async_accept(server_socket, boost::bind(&noop));
      client_socket.async_connect(acceptor.local_endpoint(), boost::bind(&noop));
      io_service.run();
    
      // Mockup write_buffer as if it read "xxxx@yyyy" with read_until()
      // using '@' as a delimiter.
      boost::asio::streambuf write_buffer;
      std::ostream output(&write_buffer);
      output << "xxxx@yyyy";
      assert(write_buffer.size() == 9);
      auto bytes_transferred = 5;
    
      // Write to server.
      boost::asio::write(server_socket, write_buffer,
          boost::asio::transfer_exactly(bytes_transferred));
       // Verify write operation consumed part of the input sequence.
      assert(write_buffer.size() == 4);
    
      // Read from client.
      boost::asio::streambuf read_buffer;
      bytes_transferred = boost::asio::read(
          client_socket, read_buffer.prepare(bytes_transferred));
      read_buffer.commit(bytes_transferred);
    
      // Copy from the read buffers input sequence.
      std::cout << "Read: " << 
                   make_string(read_buffer, bytes_transferred) << std::endl;
      read_buffer.consume(bytes_transferred);
    
      // Mockup write_buffer as if it read "zzzz@kkkk" with read_until()
      // using '@' as a delimiter.
      output << "zzzz@kkkk";
      assert(write_buffer.size() == 13); 
      bytes_transferred = 9; // yyyyzzzz@
    
      // Write to server.
      boost::asio::write(server_socket, buffer(write_buffer.data(),
          bytes_transferred));
      // Verify write operation did not consume the input sequence.
      assert(write_buffer.size() == 13);
      write_buffer.consume(bytes_transferred);
    
      // Read from client.
      bytes_transferred = boost::asio::read(
          client_socket, read_buffer.prepare(bytes_transferred));
      read_buffer.commit(bytes_transferred);
    
      // Copy from the read buffers input sequence.
      std::cout << "Read: " << 
                   make_string(read_buffer, bytes_transferred) << std::endl;
      read_buffer.consume(bytes_transferred);
    }
    

    输出:

    Read: xxxx@
    Read: yyyyzzzz@
    

    其他几点说明:

    • streambuf 拥有内存,std::istreamstd::ostream 使用内存。当需要提取格式化输入或插入格式化输出时,使用流可能是一个好主意。例如,当人们希望将字符串 "123" 读取为整数 123
    • 可以直接访问 streambuf 的输入序列并对其进行迭代。在上面的示例中,我使用 boost::asio::buffers_begin() 通过迭代流缓冲区的输入序列来帮助构造 std::string

      std::string(
          boost::asio::buffers_begin(streambuf.data()),
          boost::asio::buffers_begin(streambuf.data()) + n);
      
    • 正在使用基于流的传输协议,因此将传入数据作为流处理。请注意,即使中间服务器重构消息并在一次写入操作中发送"xxxx@",在后续写入操作中发送"yyyyzzzz@",下游也可能在一次读取操作中读取"xxxx@yyyy"

    【讨论】:

    • 感谢您详细而全面的解释,Tanner。 transfer_exactly(n) 和 streambuf.consume(n) 正是我想要的!我坚持了这么长时间的原因是我尝试使用单个'@'字符作为我的视频流数据中的结束标记符号,这完全是一个转储想法
    • 该示例使用 write(),我假设它是同步 boost::asio::write(),对吗?
    • @Dronz 是的。 Argument-dependent lookup 导致编译器选择 boost::asio::read()boost::asio::write()boost::asio::buffers_begin(),因为它们的参数之一也在 boost::asio 命名空间中。无论如何,我已将示例修改为更明确。
    • 谢谢!我注意到即使不使用 boost::asio::ip::tcp 也可以编译这些行;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多