Issue with boost serialization not dearchiving when data length is over a specific value

Viewed 138

Okay, so I am trying to send a struct with boost asio. The send on the client-side works fine and the read_until also seems fine. However, when it tries to deserialize the data back to the struct it won't work when the size of the archive is greater than about 475 in length. The rest of the struct gets ignored for some reason and only the data field gets printed. I also added screenshots of the output. Basically, when the whole struct is not received there is an input stream error on the line ba >> frame. I also tested both with a larger file and get the same error. I even tried serializing a vector as well so not sure where my error is.

EDIT:

I figured out the issue. When I was reading from the socket I had something like this...

boost::asio::read_until(socket, buf, "\0");

This was causing weird issues reading in all the data from the boost binary archive. To fix this issue I made a custom delimiter that I appended to the archive I was sending over the socket like...

boost::asio::read_until(socket, buf, "StopReadingHere");

This fixed the weird issue of the entire boost archive string not being read into the streambuf.

1 Answers
  1. First Issue

    ostringstream oss;
    boost::archive::text_oarchive ba(oss);
    ba << frame;
    string archived_data = oss.str();
    

    Here you take the string without ensuring that the archive is complete. Fix:

    ostringstream oss;
    {
        boost::archive::text_oarchive ba(oss);
        ba << frame;
    }
    string archived_data = oss.str();
    
  2. Second issue:

    boost::asio::read_until(socket, buf, "\0");
    string s((istreambuf_iterator<char>(&buf)), istreambuf_iterator<char>());
    

    Here you potentially read too much into s - buf may contain additional data after the '\0'. Use the return value from read_until and e.g. std::copy_n, following buf.consume(n).

    If you then keep the buf instance for subsequent reads you will still have the previously read remaining data in the the buffer. If you discard it, that will lead to problems deserializing the next message.

  3. Risky Code?

    void write(tcp::socket& socket, string data, int timeout) {
        auto time = std::chrono::seconds(timeout);
        async_write(socket, boost::asio::buffer(data), transfer_all(), [&] (error_code error, size_t bytes_transferred) {   
        });
        service.await_operation(time, socket);
    }
    

    You're using async operation, but passing local variables (data) as buffer.The risk is that data becomes invalid as soon as write returns.

    Are you making sure that async_write is always completed before exiting from write? (It is possible that await_operation achieves this for you.

    Perhaps you are even using await_operation from my own old answer here How to simulate boost::asio::write with a timeout . It's possible since things were added that some assumptions no longer hold. I can always review some larger piece of code to check.

Related