[英]C++ boost asio Windows file handle async_read_until infinite loop - no eof
I'm using boost 1.50 with VS2010, reading using a Windows file HANDLE (which seems to be relatively uncommon compared to asio use with sockets). 我正在使用VS2010的boost 1.50,使用Windows文件HANDLE读取(与使用套接字的asio相比,这似乎相对不常见)。
The
handle_read
callback gets to line 8 and returns the first bit with all of line 1 appended;handle_read
回调到达第8行并返回第一位,并附加第1行; further callbacks cycle through from line 2 again, ad nauseum:进一步的回调再次从第2行循环,令人作呕:
handle_read
callbacks with correct content for lines 1 through 7 handle_read
回调,其中包含第1行到第7行的正确内容 length
parameter length
参数长于预期 length
, getline
extracts a correspondingly longer line from the asio stream buffer length
,但getline
从asio流缓冲区中提取相应较长的行 handle_read
callbacks recycle lines 2 through 7, then the "long hybrid" line problem happens handle_read
回调回收第2行到第7行,然后发生“长混合”线问题 LINE 1 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
LINE 2 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
...3--E similarly...
LINE F abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
Here's the first 15 lines of output (it continues forever): 这是前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'
...
Please note how output lines #8 and #15 are a mix of input LINE 8 and LINE 1. 请注意输出线#8和#15是输入LINE 8和LINE 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
options - it didn't work at all until I switched to FILE_FLAG_OVERLAPPED
- not sure if there are other requirements that don't even manifest as errors...? CreateFile
选项中的一些东西 - 在我切换到FILE_FLAG_OVERLAPPED
之前它根本不起作用 - 不确定是否还有其他要求甚至不表现为错误......? input_buffer.commit
and even .consume
- not sure if there's something like that I'm supposed to do, even though all the example code I could find (for sockets) suggests getline
takes care of that... input_buffer.commit
甚至.consume
- 不确定是否有类似我应该做的事情,即使我能找到的所有示例代码(对于套接字)都建议getline
处理... A stream_handle
will always read at offset zero. stream_handle
将始终在偏移零处读取。 I think it's meant for sockets handles and useless for regular files. 我认为它适用于套接字句柄,对常规文件无用。
Calling async_read_until() gets 512 bytes if the streambuf doesn't already contain a newline. 如果streambuf尚未包含换行符,则调用async_read_until()将获得512个字节。 The first call reads a bit more than 7 lines.
第一个调用读取超过7行。 When seven lines are extracted the remainig characters ("LINE 8 abcdefghijklmno") don't have a newline and (the same) 512 bytes are appended.
当提取七行时,剩余字符(“LINE 8 abcdefghijklmno”)没有换行符,并且(相同)附加512字节。
To solve the problem I'd suggest to use a random_access_handle
. 为了解决这个问题,我建议使用
random_access_handle
。 You have to track the file position manually and replace async_read_until
with async_read_at
. 您必须手动跟踪文件位置,并使用
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)
{
...
This mailing list post describes the same problem. 此邮件列表帖子描述了同样的问题。 While
CreateFile
with FILE_FLAG_OVERLAPPED
allows for asynchronous I/O, it does not establish it as a stream in the context of Boost.Asio. 虽然具有
FILE_FLAG_OVERLAPPED
CreateFile
允许异步I / O,但它不会在Boost.Asio的上下文中将其建立为流。 For streams, Boost.Asio implements read_some
as read_some_at
with the offset always being 0
. 对于流,Boost.Asio的实现
read_some
作为read_some_at
与胶印始终是0
。 This is the source of the problem, as the ReadFile()
documentation states: 这是问题的根源,因为
ReadFile()
文档指出:
For files that support byte offsets, you must specify a byte offset at which to start reading from the file.
对于支持字节偏移的文件,必须指定从文件开始读取的字节偏移量。
Boost.Asio is written very generically, often requiring arguments to meet a certain type requirement rather than be a specific type. Boost.Asio是非常通用的,通常需要参数来满足某种类型的要求而不是特定的类型。 Therefore, it is often possible to adapt either the I/O object or its service to obtain the desired behavior.
因此,通常可以调整I / O对象或其服务以获得所需的行为。 First, one must identify what the adapted interface needs to support.
首先,必须确定适应的接口需要支持的内容。 In this case,
async_read_until
accepts any type fulfilling the type requirements of AsyncReadStream
. 在这种情况下,
async_read_until
接受满足AsyncReadStream
类型要求的任何类型。 AsyncReadStream
's requirements are fairly basic, requiring a void async_read_some(MutableBufferSequence, ReadHandler)
member function. AsyncReadStream
的要求相当基本,需要一个void async_read_some(MutableBufferSequence, ReadHandler)
成员函数。
As the offset value will need to be tracked throughout the composed async_read_until
operation, a simple type meeting the requirements of ReadHandler can be introduced that will wrap an application's ReadHandler, and update the offset accordingly. 由于需要在组合的
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
The asio_handler_invoke
hook will be found through ADL to support invoking user handlers in the proper context. 可以通过ADL找到
asio_handler_invoke
挂钩,以支持在适当的上下文中调用用户处理程序。 This is critical for tread safety when a composed operation is being invoked within a strand
. 当在
strand
内调用组合操作时,这对于胎面安全是至关重要的。 For more details on composed operations and strands, see this answer. 有关组合操作和链的更多详细信息,请参阅此答案。
The following class will adapt boost::asio::windows::random_access_handle
to meet the type requirements of AsyncReadStream
. 下面的类会适应
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_;
};
Alternatively, boost::asio::windows::basic_stream_handle
can be provided a custom type meeting the requirements of StreamHandleService types, and implement async_read_some
in terms of async_read_some_at
. 另外,
boost::asio::windows::basic_stream_handle
可以提供一个自定义类型的会议要求StreamHandleService类型,并实施async_read_some
来讲async_read_some_at
。
/// @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 have opted for simplicity in the example code, but the same service will be used by multiple I/O objects. 我在示例代码中选择了简单性,但多个I / O对象将使用相同的服务。 Thus, the
offset_stream_handle_service
would need to manage an offset per handler to function properly when multiple I/O objects use the service. 因此,当多个I / O对象使用该服务时,
offset_stream_handle_service
将需要管理每个处理程序的偏移量以正常运行。
To use the adapted types, modify the AsyncReader::input_handle
member variable to be either a basic_adapted_stream<boost::asio::windows::random_access_handle>
(adapted I/O object) or boost::asio::windows::basic_stream_handle<offset_stream_handle_service>
(adapted 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>
(改编服务)。
Here is the complete example based on the original code, only modifying the AsyncReader::input_handler
's type: 这是基于原始代码的完整示例,仅修改
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;
}
Which produces the following output when using the input from the original question: 使用原始问题的输入时,会产生以下输出:
line #1, length 70, getline() [69] 'LINE 1 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #2, length 70, getline() [69] 'LINE 2 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #3, length 70, getline() [69] 'LINE 3 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #4, length 70, getline() [69] 'LINE 4 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #5, length 70, getline() [69] 'LINE 5 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #6, length 70, getline() [69] 'LINE 6 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #7, length 70, getline() [69] 'LINE 7 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #8, length 70, getline() [69] 'LINE 8 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #9, length 70, getline() [69] 'LINE 9 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #10, length 70, getline() [69] 'LINE 0 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #11, length 70, getline() [69] 'LINE A abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #12, length 70, getline() [69] 'LINE B abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #13, length 70, getline() [69] 'LINE C abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #14, length 70, getline() [69] 'LINE D abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #15, length 70, getline() [69] 'LINE E abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' Misc error during read! Normal termination
My input file did not have a \\n
character at the end of LINE F. Thus, AsyncReader::handle_read()
gets invoked with an error of boost::asio::error::eof
and input_buffer
's contents contain LINE F. After modifying the final else case to print more information: 我的输入文件在LINE F的末尾没有
\\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";
}
}
I get the following output: 我得到以下输出:
line #1, length 70, getline() [69] 'LINE 1 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #2, length 70, getline() [69] 'LINE 2 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #3, length 70, getline() [69] 'LINE 3 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #4, length 70, getline() [69] 'LINE 4 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #5, length 70, getline() [69] 'LINE 5 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #6, length 70, getline() [69] 'LINE 6 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #7, length 70, getline() [69] 'LINE 7 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #8, length 70, getline() [69] 'LINE 8 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #9, length 70, getline() [69] 'LINE 9 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #10, length 70, getline() [69] 'LINE 0 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #11, length 70, getline() [69] 'LINE A abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #12, length 70, getline() [69] 'LINE B abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #13, length 70, getline() [69] 'LINE C abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #14, length 70, getline() [69] 'LINE D abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' line #15, length 70, getline() [69] 'LINE E abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' Error: End of file stream contents: 'LINE F abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' Normal termination
One option is to fseek()
the file to the next position before the user's ReadHandler is called. 一种选择是在
fseek()
用户的ReadHandler之前将文件fseek()
调到下一个位置。 Then async_read_some()
can be implemented as async_read_at(ftell())
. 然后
async_read_some()
可以实现为async_read_at(ftell())
。
The AsyncReader can use ReadUntilHandle instead of the stream_handle: 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));
}
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.