Guido Winkelmann
2011-10-05 13:44:01 UTC
Hi,
I'm having some weird problems trying to read an incoming file from an SSL
stream via a boost::asio::streambuf using async_read. I'm using asio is
shipped with Boost 1.46
The incoming file is of a known size (which has been signalled by the peer
before sending the file), so I was going to use async_read with
transfer_at_least() as a completion handler. Since I don't want the entire
incoming file in memory before writing it to the disk, I decided I would read
at most 16000 bytes in one async_read call, write that to the disk and then
iteratively read the rest of the file. The code originally looked like this:
size_t bytes_to_read = file->bytesleft() < 16000 ? file->bytesleft() : 16000;
boost::asio::async_read(m_conn->get_socket(),
m_readbuffer,
boost::asio::transfer_at_least(bytes_to_read),
boost::bind(&Order::handle_read_file_block, this,
boost::asio::placeholders::error, file,
handler));
The problem is that Order::handle_read_file_block() never gets called. (I made
sure that the peer is actually sending the number of expected bytes.) The
entire programs appears to just hang after that, although other asynchronous
operations on other sockets, but on the same io_service, are not affected.
m_conn->get_socket() returns
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>&; m_readbuffer is of
type boost::asio::streambuf.
I've experimented with replacing boost::asio::transfer_at_least() with my own
completion_condition, which was easier to debug for me:
class completion_condition {
public:
completion_condition(std::size_t filesize) : m_filesize(filesize) {}
~completion_condition() {}
std::size_t operator()(const boost::system::error_code& error,
std::size_t bytes_transferred) {
std::cerr << "In completion_condition::operator(), bytes_transferred: " <<
bytes_transferred << ", returning " << m_filesize - bytes_transferred <<
std::endl;
return m_filesize - bytes_transferred;
}
std::size_t m_filesize;};
The output of that looked like this
In completion_condition::operator(), bytes_transferred: 0, returning 1672
In completion_condition::operator(), bytes_transferred: 512, returning 1160
In completion_condition::operator(), bytes_transferred: 1024, returning 648
In completion_condition::operator(), bytes_transferred: 1211, returning 461
In completion_condition::operator(), bytes_transferred: 1249, returning 423
... and then nothing.
Meaning async_read() read five blocks of 1249 bytes and then just stopped, as
if there was nothing more to read on the socket.
The interesting part is what happens when I reduce the block size to 1000,
i.e. call transfer_at_least (or my own completion_condition) with a value of
no more than 1000. In that case, the file gets transferred successfully and
without any corruption (MD5 sum checks out) in several smaller steps (2 in the
case of this 1672 byte file).
BTW, the incoming file data on the socket is immediately followed by some more
bytes belonging to the protocol, but not to the file being transferred, so it
can't be just an off-by-one error somewhere, either. (I.e. asking async_read to
read just one more byte than there is available)
So, does anyone have any idea why async_read might just refuse to read 1672
bytes from a socket in one go, but will happily read all of it when called
twice for a smaller amount of data?
Regards,
Guido
I'm having some weird problems trying to read an incoming file from an SSL
stream via a boost::asio::streambuf using async_read. I'm using asio is
shipped with Boost 1.46
The incoming file is of a known size (which has been signalled by the peer
before sending the file), so I was going to use async_read with
transfer_at_least() as a completion handler. Since I don't want the entire
incoming file in memory before writing it to the disk, I decided I would read
at most 16000 bytes in one async_read call, write that to the disk and then
iteratively read the rest of the file. The code originally looked like this:
size_t bytes_to_read = file->bytesleft() < 16000 ? file->bytesleft() : 16000;
boost::asio::async_read(m_conn->get_socket(),
m_readbuffer,
boost::asio::transfer_at_least(bytes_to_read),
boost::bind(&Order::handle_read_file_block, this,
boost::asio::placeholders::error, file,
handler));
The problem is that Order::handle_read_file_block() never gets called. (I made
sure that the peer is actually sending the number of expected bytes.) The
entire programs appears to just hang after that, although other asynchronous
operations on other sockets, but on the same io_service, are not affected.
m_conn->get_socket() returns
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>&; m_readbuffer is of
type boost::asio::streambuf.
I've experimented with replacing boost::asio::transfer_at_least() with my own
completion_condition, which was easier to debug for me:
class completion_condition {
public:
completion_condition(std::size_t filesize) : m_filesize(filesize) {}
~completion_condition() {}
std::size_t operator()(const boost::system::error_code& error,
std::size_t bytes_transferred) {
std::cerr << "In completion_condition::operator(), bytes_transferred: " <<
bytes_transferred << ", returning " << m_filesize - bytes_transferred <<
std::endl;
return m_filesize - bytes_transferred;
}
std::size_t m_filesize;};
The output of that looked like this
In completion_condition::operator(), bytes_transferred: 0, returning 1672
In completion_condition::operator(), bytes_transferred: 512, returning 1160
In completion_condition::operator(), bytes_transferred: 1024, returning 648
In completion_condition::operator(), bytes_transferred: 1211, returning 461
In completion_condition::operator(), bytes_transferred: 1249, returning 423
... and then nothing.
Meaning async_read() read five blocks of 1249 bytes and then just stopped, as
if there was nothing more to read on the socket.
The interesting part is what happens when I reduce the block size to 1000,
i.e. call transfer_at_least (or my own completion_condition) with a value of
no more than 1000. In that case, the file gets transferred successfully and
without any corruption (MD5 sum checks out) in several smaller steps (2 in the
case of this 1672 byte file).
BTW, the incoming file data on the socket is immediately followed by some more
bytes belonging to the protocol, but not to the file being transferred, so it
can't be just an off-by-one error somewhere, either. (I.e. asking async_read to
read just one more byte than there is available)
So, does anyone have any idea why async_read might just refuse to read 1672
bytes from a socket in one go, but will happily read all of it when called
twice for a smaller amount of data?
Regards,
Guido