C++ Boost Asio Windows文件句柄异步读取直到无限循环 - 无EOF

13

我正在使用 boost 1.50 和 VS2010,在使用 Windows 文件句柄 读取(与使用套接字的 asio 相比,这似乎相对不常见)。

问题

handle_read 回调到达第 8 行并返回第一行的所有位以及所有第 1 行附加的位; 后续回调循环从第 2 行开始,反复无常:

  • 打开一个短文本文件(如下所示)
  • 获取内容正确的行1至7的预期handle_read回调
  • 下一个回调具有比预期更长的字节数length参数
  • 尽管没有使用length,但getline从asio流缓冲区中提取了一行相应长度的较长行
  • 提取的内容在中途切换,重复输入文件中的第一行
  • 进一步的handle_read回调循环从第2到7行,然后出现“长豪华混合”行问题
  • 反复无常

输入

LINE 1 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
LINE 2 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
...3--E similarly...
LINE F abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789

输出

这里是输出的前15行(它会一直继续):

line #1, length 70, getline() [69] 'LINE 1 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
line #2, length 70, getline() [69] 'LINE 2 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
...line #3 through #6 are fine too...
line #7, length 70, getline() [69] 'LINE 7 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
line #8, length 92, getline() [91] 'LINE 8 abcdefghijklmnoLINE 1 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
line #9, length 70, getline() [69] 'LINE 2 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
...line #10 through #13 are fine...
line #14, length 70, getline() [69] 'LINE 7 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
line #15, length 92, getline() [91] 'LINE 8 abcdefghijklmnoLINE 1 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
...

请注意输出的第8行和第15行是输入的第8行和第1行的混合。

代码

#include "stdafx.h"

#include <cassert>
#include <iostream>
#include <string>

#include <boost/asio.hpp>
#include <boost/bind.hpp>

#include <Windows.h>
#include <WinBase.h>

class AsyncReader
{
  public:
    AsyncReader(boost::asio::io_service& io_service, HANDLE handle)
      : io_service_(io_service),
        input_buffer(/*size*/ 8192),
        input_handle(io_service, handle)
    {
        start_read();
    }

    void start_read()
    {
        boost::asio::async_read_until(input_handle, input_buffer, '\n',
            boost::bind(&AsyncReader::handle_read, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
    }

    void handle_read(const boost::system::error_code& error, std::size_t length);
    // void handle_write(const boost::system::error_code& error);

  private:
    boost::asio::io_service& io_service_;
    boost::asio::streambuf input_buffer;
    boost::asio::windows::stream_handle input_handle;
};

void AsyncReader::handle_read(const boost::system::error_code& error, std::size_t length)
{
    if (!error)
    {
        static int count = 0;
        ++count;

        // method 1: (same problem)
        // const char* pStart = boost::asio::buffer_cast<const char*>(input_buffer.data());
        // std::string s(pStart, length);
        // input_buffer.consume(length);

        // method 2:
        std::istream is(&input_buffer);
        std::string s;
        assert(std::getline(is, s));

        std::cout << "line #" << count << ", length " << length << ", getline() [" << s.size() << "] '" << s << "'\n";

        start_read();
    }
    else if (error == boost::asio::error::not_found)
        std::cerr << "Did not receive ending character!\n";
    else
        std::cerr << "Misc error during read!\n";
}
int _tmain(int argc, _TCHAR* argv[])
{
    boost::asio::io_service io_service;

    HANDLE handle = ::CreateFile(TEXT("c:/temp/input.txt"),
                                 GENERIC_READ,
                                 0, // share mode
                                 NULL, // security attribute: NULL = default
                                 OPEN_EXISTING, // creation disposition
                                 FILE_FLAG_OVERLAPPED,
                                 NULL // template file
                                );

    AsyncReader obj(io_service, handle);

    io_service.run();

    std::cout << "Normal termination\n";
    getchar();
    return 0;
}

我的想法

  • 可能是在CreateFile选项中有问题 - 直到我切换到FILE_FLAG_OVERLAPPED,它才开始工作 - 不确定是否还有其他要求没有表现为错误...?
  • 我已经尝试过input_buffer.commit甚至.consume - 不确定是否有类似的操作我应该做,尽管我能找到的所有示例代码(针对套接字)都建议getline会处理这个...
  • 恼怒 / 我想念Linux....

1
你有没有尝试过使用boost::asio::windows::random_access_handle,而不是stream_handle - Dave S
@DaveS:你好,Dave。我尝试了一下,但是在 async_read_until(...调用行中出现了编译错误:asio\impl\read_until.hpp(409): error C2039: 'async_read_some' : is not a member of 'boost::asio::windows::basic_random_access_handle<>'。我看不出明显的解决方法…… - Tony Delroy
3个回答

7
这篇邮件列表中的帖子描述了相同的问题。虽然使用FILE_FLAG_OVERLAPPED的CreateFile允许执行异步I/O,但它不能在Boost.Asio上下文中建立流。对于流,Boost.Asio将read_some实现为read_some_at,其中偏移始终为0。这是问题的来源,因为ReadFile()的文档说明如下:
对于支持字节偏移量的文件,必须指定从文件中开始读取的字节偏移量。

适应类型要求

Boost.Asio非常通用,通常要求参数满足某些类型要求而不是特定类型。因此,通过自适应I/O对象或其服务来获得所需行为通常是可能的。首先,必须确定适配接口需要支持什么。在本例中,async_read_until接受任何满足AsyncReadStream类型要求的类型。AsyncReadStream的要求相当基本,要求有一个void async_read_some(MutableBufferSequence, ReadHandler)成员函数。
由于偏移值将需要在组合的async_read_until操作中进行跟踪,因此可以引入一个简单的类型,满足ReadHandler的要求,将包装应用程序的ReadHandler,并相应地更新偏移量。
namespace detail {
/// @brief Handler to wrap asynchronous read_some_at operations.
template <typename Handler>
class read_some_offset_handler
{
public:
  read_some_offset_handler(Handler handler, boost::uint64_t& offset)
    : handler_(handler),
      offset_(offset)
  {}

  void operator()(
    const boost::system::error_code& error,
    std::size_t bytes_transferred)
  {
    offset_ += bytes_transferred;

    // If bytes were transferred, then set the error code as success.
    // EOF will be detected on next read.  This is to account for
    // the read_until algorithm behavior.
    const boost::system::error_code result_ec =
      (error && bytes_transferred)
      ? make_error_code(boost::system::errc::success) : error;

    handler_(result_ec, bytes_transferred);
  }

//private:
  Handler handler_;
  boost::uint64_t& offset_;
};

/// @brief Hook that allows the wrapped handler to be invoked
///        within specific context.  This is critical to support
///        composed operations being invoked within a strand.
template <typename Function,
          typename Handler>
void asio_handler_invoke(
  Function function,
  detail::read_some_offset_handler<Handler>* handler)
{
  boost_asio_handler_invoke_helpers::invoke(
    function, handler->handler_);
}

} // namespace detail
asio_handler_invoke钩子通过ADL被发现,以支持在正确的上下文中调用用户处理程序。这对于在strand中调用组合操作时的线程安全至关重要。有关组合操作和串行的更多详细信息,请参见答案。
以下类将适配boost::asio::windows::random_access_handle以满足AsyncReadStream的类型要求。
/// @brief Adapts AsyncRandomAccessReadDevice to support AsyncReadStream.
template <typename AsyncRandomAccessReadDevice>
class basic_adapted_stream
  : public AsyncRandomAccessReadDevice
{
public:
  basic_adapted_stream(
    boost::asio::io_service& io_service,
    HANDLE handle
  )
    : AsyncRandomAccessReadDevice(io_service, handle),
      offset_(0)
  {}

  template<typename MutableBufferSequence,
           typename ReadHandler>
  void async_read_some(
    const MutableBufferSequence& buffers,
    ReadHandler handler)
  {
    async_read_at(*this, offset_, buffers, 
      detail::read_some_offset_handler<ReadHandler>(handler, offset_));
  }

private:
  boost::uint64_t offset_;
};

另外,boost::asio::windows::basic_stream_handle 可以提供符合 StreamHandleService 类型要求的自定义类型,并利用 async_read_some_at 来实现 async_read_some

/// @brief Service that implements async_read_some with async_read_some_at.
class offset_stream_handle_service
  : public boost::asio::windows::stream_handle_service
{
private:
  // The type of the platform-specific implementation.
  typedef boost::asio::detail::win_iocp_handle_service service_impl_type;
public:

  /// The unique service identifier.
  static boost::asio::io_service::id id;

  /// Construct a new stream handle service for the specified io_service.
  explicit offset_stream_handle_service(boost::asio::io_service& io_service)
    : boost::asio::windows::stream_handle_service(io_service),
      service_impl_(io_service),
      offset_(0)
  {}

  /// Start an asynchronous read.
  template <typename MutableBufferSequence,
            typename ReadHandler>
  void
  async_read_some(
    implementation_type& impl,
    const MutableBufferSequence& buffers,
    ReadHandler handler)
  {
    // Implement async_read_some in terms of async_read_some_at.  The provided
    // ReadHandler will be hoisted in an internal handler so that offset_ can
    // be properly updated.
    service_impl_.async_read_some_at(impl, offset_, buffers, 
      detail::read_some_offset_handler<ReadHandler>(handler, offset_));
  }
private:
  // The platform-specific implementation.
  service_impl_type service_impl_;
  boost::uint64_t offset_;
};

boost::asio::io_service::id offset_stream_handle_service::id;

我在示例代码中选择了简单性,但同一个服务将被多个I/O对象使用。因此,当多个I/O对象使用该服务时,offset_stream_handle_service需要管理每个处理程序的偏移量才能正常工作。
要使用改编后的类型,请修改AsyncReader::input_handle成员变量为basic_adapted_stream<boost::asio::windows::random_access_handle>(改编后的I/O对象)或boost::asio::windows::basic_stream_handle<offset_stream_handle_service>(改编后的服务)。

示例

以下是基于原始代码的完整示例,仅修改AsyncReader::input_handler的类型:
#include "stdafx.h"

#include <cassert>
#include <iostream>
#include <string>

#include <boost/asio.hpp>
#include <boost/bind.hpp>

#include <Windows.h>
#include <WinBase.h>


namespace detail {
/// @brief Handler to wrap asynchronous read_some_at operations.
template <typename Handler>
class read_some_offset_handler
{
public:
  read_some_offset_handler(Handler handler, boost::uint64_t& offset)
    : handler_(handler),
      offset_(offset)
  {}

  void operator()(
    const boost::system::error_code& error,
    std::size_t bytes_transferred)
  {
    offset_ += bytes_transferred;

    // If bytes were transferred, then set the error code as success.
    // EOF will be detected on next read.  This is to account for
    // the read_until algorithm behavior.
    const boost::system::error_code result_ec =
      (error && bytes_transferred)
      ? make_error_code(boost::system::errc::success) : error;

    handler_(result_ec, bytes_transferred);
  }

//private:
  Handler handler_;
  boost::uint64_t& offset_;
};

/// @brief Hook that allows the wrapped handler to be invoked
///        within specific context.  This is critical to support
///        composed operations being invoked within a strand.
template <typename Function,
          typename Handler>
void asio_handler_invoke(
  Function function,
  detail::read_some_offset_handler<Handler>* handler)
{
  boost_asio_handler_invoke_helpers::invoke(
    function, handler->handler_);
}

} // namespace detail

/// @brief Adapts AsyncRandomAccessReadDevice to support AsyncReadStream.
template <typename AsyncRandomAccessReadDevice>
class basic_adapted_stream
  : public AsyncRandomAccessReadDevice
{
public:
  basic_adapted_stream(
    boost::asio::io_service& io_service,
    HANDLE handle
  )
    : AsyncRandomAccessReadDevice(io_service, handle),
      offset_(0)
  {}

  template<typename MutableBufferSequence,
           typename ReadHandler>
  void async_read_some(
    const MutableBufferSequence& buffers,
    ReadHandler handler)
  {
    async_read_at(*this, offset_, buffers, 
      detail::read_some_offset_handler<ReadHandler>(handler, offset_));
  }

private:
  boost::uint64_t offset_;
};

/// @brief Service that implements async_read_some with async_read_some_at.
class offset_stream_handle_service
  : public boost::asio::windows::stream_handle_service
{
private:
  // The type of the platform-specific implementation.
  typedef boost::asio::detail::win_iocp_handle_service service_impl_type;
public:

  /// The unique service identifier.
  static boost::asio::io_service::id id;

  /// Construct a new stream handle service for the specified io_service.
  explicit offset_stream_handle_service(boost::asio::io_service& io_service)
    : boost::asio::windows::stream_handle_service(io_service),
      service_impl_(io_service),
      offset_(0)
  {}

  /// Start an asynchronous read.
  template <typename MutableBufferSequence,
            typename ReadHandler>
  void
  async_read_some(
    implementation_type& impl,
    const MutableBufferSequence& buffers,
    ReadHandler handler)
  {
    // Implement async_read_some in terms of async_read_some_at.  The provided
    // ReadHandler will be hoisted in an internal handler so that offset_ can
    // be properly updated.
    service_impl_.async_read_some_at(impl, offset_, buffers, 
      detail::read_some_offset_handler<ReadHandler>(handler, offset_));
  }
private:
  // The platform-specific implementation.
  service_impl_type service_impl_;
  boost::uint64_t offset_;
};

boost::asio::io_service::id offset_stream_handle_service::id;

#ifndef ADAPT_IO_SERVICE
typedef basic_adapted_stream<
    boost::asio::windows::random_access_handle> adapted_stream;
#else
typedef boost::asio::windows::basic_stream_handle<
    offset_stream_handle_service> adapted_stream;
#endif

class AsyncReader
{
  public:
    AsyncReader(boost::asio::io_service& io_service, HANDLE handle)
      : io_service_(io_service),
        input_buffer(/*size*/ 8192),
        input_handle(io_service, handle)
    {
        start_read();
    }

    void start_read()
    {
        boost::asio::async_read_until(input_handle, input_buffer, '\n',
            boost::bind(&AsyncReader::handle_read, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
    }

    void handle_read(const boost::system::error_code& error, std::size_t length);
    // void handle_write(const boost::system::error_code& error);

  private:
    boost::asio::io_service& io_service_;
    boost::asio::streambuf input_buffer;
    adapted_stream input_handle;
};

void AsyncReader::handle_read(const boost::system::error_code& error, std::size_t length)
{
    if (!error)
    {
        static int count = 0;
        ++count;

        // method 1: (same problem)
        // const char* pStart = boost::asio::buffer_cast<const char*>(input_buffer.data());
        // std::string s(pStart, length);
        // input_buffer.consume(length);

        // method 2:
        std::istream is(&input_buffer);
        std::string s;
        assert(std::getline(is, s));

        std::cout << "line #" << count << ", length " << length << ", getline() [" << s.size() << "] '" << s << "'\n";

        start_read();
    }
    else if (error == boost::asio::error::not_found)
        std::cerr << "Did not receive ending character!\n";
    else
        std::cerr << "Misc error during read!\n";
}
int _tmain(int argc, _TCHAR* argv[])
{
    boost::asio::io_service io_service;

    HANDLE handle = ::CreateFile(TEXT("c:/temp/input.txt"),
                                 GENERIC_READ,
                                 0, // share mode
                                 NULL, // security attribute: NULL = default
                                 OPEN_EXISTING, // creation disposition
                                 FILE_FLAG_OVERLAPPED,
                                 NULL // template file
                                );

    AsyncReader obj(io_service, handle);

    io_service.run();

    std::cout << "Normal termination\n";
    getchar();
    return 0;
}

以下是使用原始问题中提供的输入时产生的输出:
第1行,长度为70,getline() [69] 'LINE 1 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第2行,长度为70,getline() [69] 'LINE 2 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第3行,长度为70,getline() [69] 'LINE 3 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第4行,长度为70,getline() [69] 'LINE 4 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第5行,长度为70,getline() [69] 'LINE 5 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第6行,长度为70,getline() [69] 'LINE 6 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第7行,长度为70,getline() [69] 'LINE 7 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第8行,长度为70,getline() [69] 'LINE 8 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第9行,长度为70,getline() [69] 'LINE 9 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第10行,长度为70,getline() [69] 'LINE 0 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第11行,长度为70,getline() [69] 'LINE A abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第12行,长度为70,getline() [69] 'LINE B abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第13行,长度为70,getline() [69] 'LINE C abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第14行,长度为70,getline() [69] 'LINE D abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第15行,长度为70,getline() [69] 'LINE E abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
读取过程中发生错误!
正常终止
我的输入文件末尾没有\n字符。因此,AsyncReader::handle_read()被调用时会出现boost::asio::error::eof的错误,并且input_buffer的内容包含了LINE F。在修改最终的else语句以打印更多信息后:
...
else
{
    std::cerr << "Error: " << error.message() << "\n";

    if (std::size_t buffer_size = input_buffer.size())
    {
        boost::asio::streambuf::const_buffers_type bufs = input_buffer.data();
        std::string contents(boost::asio::buffers_begin(bufs),
                             boost::asio::buffers_begin(bufs) + buffer_size);
        std::cerr << "stream contents: '" << contents << "'\n";
    }
}

我得到了以下输出:
第1行,长度为70,getline() [69] 'LINE 1 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第2行,长度为70,getline() [69] 'LINE 2 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第3行,长度为70,getline() [69] 'LINE 3 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第4行,长度为70,getline() [69] 'LINE 4 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第5行,长度为70,getline() [69] 'LINE 5 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第6行,长度为70,getline() [69] 'LINE 6 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第7行,长度为70,getline() [69] 'LINE 7 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第8行,长度为70,getline() [69] 'LINE 8 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第9行,长度为70,getline() [69] 'LINE 9 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第10行,长度为70,getline() [69] 'LINE 0 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第11行,长度为70,getline() [69] 'LINE A abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第12行,长度为70,getline() [69] 'LINE B abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第13行,长度为70,getline() [69] 'LINE C abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第14行,长度为70,getline() [69] 'LINE D abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
第15行,长度为70,getline() [69] 'LINE E abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
错误: 文件结束
流内容:'LINE F abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
正常终止

我认为offset_stream_handle_service是最好的方法,因为即使(adapted_)stream_handle被销毁,偏移量变量可能仍在使用中。 - kunysch
+1(来自之前的评论)- 看起来很棒,谢谢Tanner!我会在有机会的时候尝试这种方法 - 目前非常忙碌。干杯。 - Tony Delroy
@TonyD:我稍微扩展了一下最初的答案,包括对strands的支持。我完全忘记了在原始问题中发布的伟大的sscce可能已经消除了真实应用程序中的潜在线程问题。 - Tanner Sansbury

5
一个 stream_handle 总是从偏移量0开始读取。我认为它适用于套接字句柄,对于常规文件则无用。
调用 async_read_until() 如果 streambuf 中没有换行符,则获取512个字节。第一次调用读取了超过7行的少量内容。当提取七行时,剩余的字符(“LINE 8 abcdefghijklmno”)没有换行符,并且(相同的)512个字节被附加。
为解决这个问题,我建议使用一个 random_access_handle。您需要手动跟踪文件位置并将 async_read_until 替换为 async_read_at
class AsyncReader
{
  ...
  void start_read()
  {
    async_read_at(input_handle, input_offset, input_buffer, ...);
  }
private:
  boost::asio::windows::random_access_handle input_handle;
  boost::uint64_t input_offset;
};

void AsyncReader::handle_read(const boost::system::error_code& error,
                              std::size_t length)
{
  input_offset += length;
  if (!error || error == boost::asio::error::eof)
  {
    ...

谢谢kunysch…该替代方法的确可以正确读取文件,只是有一个相对较小的不便之处,无法指定“直到”条件——尽管我特别关注正则表达式的支持,但可以单独实现它。我暂时不会将其标记为答案——看看是否有人建议一种获取“直到”版本的方法,即使这意味着对boost代码进行补丁……还是要感谢您! - Tony Delroy

2

一种选择是在用户的ReadHandler被调用之前,使用fseek()将文件定位到下一个位置。然后可以将async_read_some()实现为async_read_at(ftell())

AsyncReader可以使用ReadUntilHandle而不是stream_handle:

class ReadUntilHandle : public boost::asio::windows::random_access_handle
{
  public:
    ReadUntilHandle(boost::asio::io_service& ios, HANDLE handle)
      : boost::asio::windows::random_access_handle(ios, handle)
    {}

    template <typename MutableBufferSequence, typename Handler>
    void async_read_some(const MutableBufferSequence& buffers, Handler& handler)
    {
      LARGE_INTEGER offset;
      offset.QuadPart = 0;
      if (::SetFilePointerEx(native_handle(), offset, &offset, FILE_CURRENT)) {
        async_read_some_at(offset.QuadPart, buffers,
                           std::bind(&on_read_complete<Handler>, handler,
                                     native_handle(), std::ref(get_io_service()),
                                     std::placeholders::_1, std::placeholders::_2));
      } else {
        boost::system::error_code error(::GetLastError(), boost::asio::error::get_system_category());
        get_io_service().post(boost::asio::detail::bind_handler(handler, error, 0));
      }
    }
  private:
    template <typename Handler> static void
    on_read_complete(Handler& handler, HANDLE native_handle, boost::asio::io_service& ios,
                   boost::system::error_code error, std::size_t length)
    {
      if (0 != length) { // update file position
        LARGE_INTEGER offset;
        offset.QuadPart = length;
        if (!::SetFilePointerEx(native_handle, offset, NULL, FILE_CURRENT) && !error) {
          error.assign(::GetLastError(),  boost::asio::error::get_system_category());
        }
      }
      ios.dispatch(boost::asio::detail::bind_handler(handler, error, length));
    }
};

当包装现有处理程序时,引入新类型可能是值得的。这使得更容易添加asio_handler_invoke函数以允许正确的处理程序调用上下文。例如,如果使用从strand::wrap()返回的处理程序调用ReadUntilHandle::async_read_some,则所有中间操作都将在strand之外调用。 - Tanner Sansbury
@TannerSansbury 我相信detail::bind_handler()调用将正确地包装原始处理程序并在线程中执行。当然,使用新类型可能是一个好主意,以避免这个内部函数。另一方面(如果后果可以接受),该处理程序可以直接被调用 …… 无需post / dispatch调用和asio_handler_ ……包装器。 - kunysch
糟糕,我的错误。我将async_read_some_at()成员函数误读为async_read_at()自由函数。由于没有潜在的中间处理程序,因此std::bind的未指定类型和bind_handler的包装应该是可以的。 - Tanner Sansbury

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接