1
votes

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);
        }
    });
1
tcp is just a byte stream -- no packets as such. So there's no guarantee a write at one end will correspond to exactly one read at the other end. - G.M.
I added the functions that I wrote to send and read the data. - asmbaty
I'm guessing here but your SendData implementation seems to assume that async_write_some always writes the specified number of bytes (tr_size in this case) before the handler is called. - G.M.
You never use bytes_transferred in your send lambda. How do you know how many bytes were sent? Also see the remark at the bottom of the async_write_some documentation. - rustyx
async_write_some was the problem. Replacing it with async_write solved the problem. Thank you for noticing it. - asmbaty

1 Answers

1
votes

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();
        });
}