Stefano Mtangoo
Stefano Mtangoo

Reputation: 6534

Asio two way communication with peristence socket

I have this requirement where my app have to connect to another app via sockets and will have to maintain persistent connection for quiet long time. My app will be a TCP client and the other is a TCP server. My app will send commands and the server will respond accordingly.

The problem am facing right now is how to read the whole data from server a string and return for app which will issue the next command. Reading synchronously (with asio::read) looked like a good option up until I observed socket hanging up until I terminate the server. Looking at the documentation I found that the library is correctly working.

his function is used to read a certain number of bytes of data from a stream. The call will block until one of the following conditions is true: 1. The supplied buffers are full. That is, the bytes transferred is equal to the sum of the buffer sizes. 2. An error occurred.

The problem is I don't know correct buffer size as the response from the server varies. So If I put a too small buffer it returns fine but missing some data. If I put too big it will hang forever until server quits.

So I thought I would do the async reading. It works only once and I don't know how to make it fetch data until whole data it read.

here is the relevant async code

#define ASIO_STANDALONE 1

#include <iostream>
#include <asio.hpp>

int main()
{
    asio::io_context context;
    size_t reply_length;
    size_t length = 1024;
    std::vector<char> buffer;

    //create socket
    asio::ip::tcp::socket socket(context);
    socket.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), 8088));

    std::string dataOut = "list --files"; //some command to write
    std::error_code error;

    asio::write(socket, asio::buffer(dataOut), error);
    if (!error)
    {
        std::cout << "Receiving...!" << std::endl;
        buffer.resize(length);
        asio::async_read(socket, asio::buffer(buffer), [&buffer, &context](const asio::error_code &ec, std::size_t bytes_transferred) {
            std::copy(buffer.begin(), buffer.end(), std::ostream_iterator<char>(std::cout, ""));
            std::cout << "\nRead total of:" << bytes_transferred << "\n";
            context.run();
        });
    }
    else
    {
        std::cout << "send failed: " << error.message() << std::endl;
    }

    context.run();
}

Searching didn't help much solving my issue.

So my question is, how can I read all the data in a persistent socket with asio? Am not using boost.

Upvotes: 0

Views: 168

Answers (1)

rafix07
rafix07

Reputation: 20969

You need to loop async_read calls. If you don't want your client to hang on read operation you can define the smallest possible buffer i.e. 1 byte.

Define function which takes socket, buffer and two additional parameters according to async_read's handler signature, and this function calls itself with async_read to make the loop of async_read calls - it reads until some error occures:

void onRead (
    asio::ip::tcp::socket&    socket,
    std::array<char,1>&       buf,
    const system::error_code& ec, 
    std::size_t               bytes)
{
    if (ec)
    {
        if (ec == asio::error::eof && bytes == 1)
            std::cout << buf[0];
        return;
    }

    std::cout << buf[0];   
    asio::async_read(socket,asio::buffer(buf), 
        std::bind(onRead, std::ref(socket), std::ref(buf),
          std::placeholders::_1, // error code
          std::placeholders::_2)); // transferred bytes
}

and the changes in main:

std::array<char,1> buf;
asio::write(socket, asio::buffer(dataOut), error);

if (!error)
{
    std::cout << "Receiving...!" << std::endl;               

    asio::async_read(socket, asio::buffer(buf), 
        std::bind(onRead, std::ref(socket), std::ref(buf),
         std::placeholders::_1,
         std::placeholders::_2));

    context.run();
}
else
{
    std::cout << "send failed: " << error.message() << std::endl;
}

(I am using Boost, so you should replace system::error_code on asio::error_code).

Upvotes: 1

Related Questions