Search code examples
c++socketstcpboost-asioasio

Boost asio tcp data lost between packet when sending large data


I try to send a large data (1mb+) from a server to a client using the boost's asio library.

I split the data into chunks of size 16000 and use async_write_some to send them. I read the data with async_read or async_read_some in the client.

When both the server and the client are my local machine, everything works fine and I can send 10mb+ data without any problems.

When I use a different machine to send the data it is ok with data less than 1mb. but when the data size is 1mb+ there is a data loss between chunks when the client reads it. I am using an ethernet cable to connect the server and the client (the network is really reliable).

When I put a timeout between sending chunks like:

std::this_thread::sleep_for(30ms)

The client could get the whole data without any loss.

Could somebody give me some hints what is happening and how can I correct mistakes? TCP requires that no data lost must be occurred in any situation.

Edit: Here is the send logic

void SendData(const char* data, int size) {
    const int tr_size = std::min(CHUNK_SIZE, size);
    socket_->async_write_some(asio::buffer(data, tr_size),
    [this, data = data + tr_size, size = size - tr_size]
    (const std::error_code &error, size_t bytes_transferred){
        if(!error && size > 0) {
            SendData(data, size);
        } else if(socket_ && socket_->is_open()) {
            socket_->close();
        }
    });
}

Here is the receive logic

    void ReceiveData(char* data, size_t size)
{
    socket_.async_read_some(asio::buffer(data, size), [this, data, size]
    (const std::error_code &error, size_t bytes_transferred) mutable{
        if(!error) {
            assert(size >= bytes_transferred);
            size -= bytes_transferred;
            data += bytes_transferred;
        }
        if(error) {
            return;
        }
        if(size > 0) {
            ReceiveData(data, size);
        }
    });

Solution

  • The commenters already spot the problem: you should be using write_some repeatedly until the full buffer has been sent.

    Of course, this task is so common, why not use asio::async_write which does just that?

    void SendData(const char* data, int size) {
        const int tr_size = std::min(CHUNK_SIZE, size);
        async_write(*socket_, asio::buffer(data, tr_size),
            [this, data = data + tr_size, size = size - tr_size]
            (std::error_code error, size_t /*bytes_transferred*/){
                if (error) {
                    std::cerr << "async_write: " << error.message() << std::endl;
                }
                socket_->close();
            });
    }