Question

I am building a client server communication. The server sends Header+Data (using async_write and a seperate IO Thread), Client receives Header of fixed size and knows how much data it has to read.

The Problem: Sometimes the client receives wrong data. It seems like the server sends the wrong data.

void Session::do_write(std::shared_ptr<DataItem> data)
{
    std::lock_guard<std::mutex> lk(doWrite_mutex);

    std::vector<boost::asio::const_buffer> buffers;
    buffers.push_back(boost::asio::buffer(&data->length, sizeof(uint32_t)));
    buffers.push_back(boost::asio::buffer(&data->callbackID, sizeof(uint8_t)));
    buffers.push_back(boost::asio::buffer(&data->isString, sizeof(bool)));
    //Get the data to send into the buffer and make sure the given shared ptr to the data item keeps living until this function is finished.
    buffers.push_back(boost::asio::buffer(data->getData(), data->length));

    boost::asio::async_write(*socket_, buffers, boost::bind(&Session::onSend, this, data, _1,_2));
}

    void Session::onSend(std::shared_ptr<DataItem> data,const boost::system::error_code ec, std::size_t length)
{ //Some logging, nothing special here 
}

The data item is a polymorphic class to handle different kinds of data (vectors, strings,...). The getData() method returns a const void* to the actual data (e.g. myData->data() in case of vector). The data is stored as a shared_ptr inside the DataItem (to keep it from being destroyed).

In most cases the data is transmitted correctly.

I don't know where to debug or what I am doing wrong.

Was it helpful?

Solution

Invoking a write operation on a stream that has an outstanding async_write() operation fails to meet a requirement of async_write(), which can result in interwoven data. Additionally, if multiple threads are servicing the io_service event loop or Session::do_write() is invoked from a thread that is not processing the event loop, then the use of a mutex will fail to meet the thread safety requirement of the stream. This answer demonstrates using a queue to serialize multiple async_write() operations, and processing the queue with an asynchronous call chain within a strand, fulfilling both the requirements of async_write() and the stream's thread safety.

For further details, the async_write() function is a composed operations, resulting in zero or more calls to the stream's async_write_some() function. Therefore, if the program does not ensure that the stream performs no other write operations until the outstanding operation completes, the intermediate write operations can be mixed between other write operations, resulting in interwoven data. Furthermore, these intermediate operations invoke async_write_some() on the stream without having acquired doWrite_mutex, potentially violating the thread safety requirement for the stream. For more information about composed operations and strand usage, consider reading this answer.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top