3

Recently I have found an annoying problem with boost:asio::async_read. When I transfer large amounts of data, say a file of about 9GB, boost:asio::async_read works well in most of the time, but sometimes the completion of async_read is executed with bytes_transferred=0 and ec=0! And sometimes the completion of async_read is executed with bytes_transferred!=sp_recv_data->size() and ec still equal to 0. My code just like below:

void CNetProactorClientImpl::async_recv()
{
    auto sp_vec_buf = make_shared<std::vector<unsigned char>>(1 * 1024 * 1024);

    /*
    boost::asio::async_read
    this function is used to asynchronously read a certain number of bytes of data from a stream.
    the function call always returns immediately. the asynchronous operation will continue until one of the following conditions is true:
        *the supplied buffers are full. that is, the bytes transferred is equal to the sum of the buffer sizes.
        *an error occurred.
    */
    boost::asio::async_read(*sp_socket,
        boost::asio::buffer(sp_vec_buf),
        bind(&CNetProactorClientImpl::async_recv_complete_handler, this,
            sp_vec_buf,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
}
void CNetProactorClientImpl::async_recv_complete_handler(shared_ptr<std::vector<unsigned char>> sp_recv_data, const boost::system::error_code& ec, size_t bytes_transferred)
{
    if (!ec)
    {
        //no error
        if (bytes_transferred == 0)
        {
            //Sometimes it trigger here, ec is 0 but also bytes_transferred! Why???
            assert(bytes_transferred == sp_recv_data->size());
        }
        if (bytes_transferred != sp_recv_data->size())
        {
            /*
            Sometimes it trigger here, it is also a strange behavior that bytes_transferred NOT equal to sp_recv_data->size() because the Boost document say:
            The asynchronous operation will continue until one of the following conditions is true:
                *The supplied buffers are full. That is, the bytes transferred is equal to the sum of the buffer sizes.
                *An error occurred.
            */
            assert(bytes_transferred == sp_recv_data->size());
        }
        //do normal action
    }
    else
    {
        //error handling
        disconnect();
    }
}

My boost version is 1.61. Testing environment: win10 pro +VS2015 Update3.

Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169
superos
  • 53
  • 6
  • Did you get to the end of the file? (Or if it's a socket, did the other end close the socket?) – user253751 Jul 17 '16 at 10:07
  • This is described in the documentation, and is fixed with `asio::transfer_at_least(1)`. – ildjarn Jul 17 '16 at 10:12
  • @ildjarn Can you please provide a link to the documentation that describes this behavior? As far as I know, the [`async_read()` overload](http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/async_read/overload1.html) being used is the equivalent of providing the `boost::asio::transfer_all()` completion condition, where if the operation does not fail with an error, then `bytes_transferred` should be equal to the size of the read buffer. – Tanner Sansbury Jul 17 '16 at 15:49
  • 2
    Could a [mcve] please be provided? Also, is there anything different with this socket that would cause it to use custom driver or network stack? I often observe this behavior when custom drivers poorly handle the `ReadFile` API contract. – Tanner Sansbury Jul 17 '16 at 16:00
  • @immibis The socket is keep connecting ,not closed. And when the annoying problem happened,there always still a lots of file data need to be transferred. – superos Jul 19 '16 at 01:21
  • 2
    @Tanner Sansbury After a lot of debugging and googling,I think I have found the reason: If multiple threads are processing the io_service,then boost::asio::async_read and boost::asio::async_write MUST NOT invoked at the same time WHEN using with OpenSSL! General speaking: OpenSSL+multiple thread process io_service.run+boost::asio::async_read and boost::asio::async_write will trigger bytes_transferred=0 and ec=0. – superos Jul 26 '16 at 02:08
  • @Tanner Sansbury We should use strand to protect IO,just like what you say [here](http://stackoverflow.com/questions/12794107/why-do-i-need-strand-per-connection-when-using-boostasio/12801042#12801042) – superos Jul 26 '16 at 02:51
  • @superos How did you fixed the issue? You added one strand for each `boost::asio::ssl::stream` and one strand for the `boost::asio::ssl::context`? – Victor Mezrin Nov 21 '16 at 20:52

0 Answers0