如何阻止boost :: asio异步读取混淆?

时间:2016-01-28 23:16:38

标签: c++ sockets boost boost-asio

我正在使用boost::asio::ip::tcp::socket来接收数据。我需要一个接口,允许我指定一个缓冲区,并在异步填充此缓冲区后调用完成处理程序。

从套接字读取时,我们可以使用async_read_some方法。 但是,async_read_some方法可能会读取少于请求的字节数,因此如果发生这种情况,它必须使用缓冲区的其余部分调用自身。这是我目前的做法:

template<typename CompletionHandler>
void read(boost::asio::ip::tcp::socket* sock, char* target, size_t size, CompletionHandler completionHandler){

   struct ReadHandler {
      boost::asio::ip::tcp::socket* sock;
      char* target;
      size_t size;
      CompletionHandler completionHandler;

      ReadHandler(ip::tcp::socket* sock, char* target, size_t size, CompletionHandler completionHandler)
      : sock(sock),target(target),size(size),completionHandler(completionHandler){}
      // either request the remaining bytes or call the completion handler
      void operator()(const boost::system::error_code& error, std::size_t bytes_transferred){
         if(error){
            return;
         }

        if(bytes_transferred  < size){
           // Read incomplete
           char* newTarg =target+bytes_transferred;
           size_t newSize = size-bytes_transferred;
           sock->async_read_some(boost::asio::buffer(newTarg, newSize), ReadHandler(sock,newTarg,newSize,completionHandler));
           return;
        } else {
           // Read complete, call handler
           completionHandler();
        }
      }
   };

   // start first read
   sock->async_read_some(boost::asio::buffer(target, size), ReadHandler(this,target,size,completionHandler));
}

基本上,我们调用async_read_some直到整个缓冲区被填满,然后我们调用完成处理程序。到现在为止还挺好。但是,一旦我在第一次调用完成接收之前多次调用此方法,我认为事情会变得混乱:

void thisMayFail(boost::asio::ip::tcp::socket* sock){
   char* buffer1 = new char[128];
   char* buffer2 = new char[128];
   read(sock, buffer1, 128,[](){std::cout << "Buffer 1 filled";});
   read(sock, buffer2, 128,[](){std::cout << "Buffer 2 filled";});
}

当然,前128个接收字节应该进入第一个缓冲区,第二个128应该进入第二个缓冲区。但根据我的理解,情况可能会发生在这里:

假设第一个async_read_some仅返回70个字节,那么它将发出第二个async_read_some,剩余的58个字节。但是,这个读取将在第二个128字节读取(!)后排队,因此第一个缓冲区将接收前70个字节,接下来的128个将进入第二个缓冲区,最后的50个进入第一个缓冲区。 即,在这种情况下,第二个缓冲区甚至会在第一个填充完全填充之前填充。这可能不会发生。

如何解决这个问题?我知道有async_read方法,但是它的文档说它只是通过多次调用async_read_some来实现,所以它与我的read实现基本相同,并且无法解决问题

1 个答案:

答案 0 :(得分:1)

您根本无法同时激活两个异步读取操作:这是未定义的行为。

你可以

  • 使用自由函数async_read_untilasync_read函数,它们已经具有更高级别的语义,循环调用套接字async_read_some,直到条件匹配或缓冲区已满。

  • 使用异步操作链接对第一个之后的下一个异步读取进行排序。简而言之,您在第一个完成处理程序中启动第二个boost::asio::async_read*调用。

    注意:

    • 让您有机会首先对传输错误采取行动。
    • 自由功能界面将提高代码的抽象级别 解决问题(问题是启动两个同时读取操作)
  • 在运行多个IO服务线程时使用strand;请参阅Why do I need strand per connection when using boost::asio?