Home > Software engineering >  asio socket, split incoming data at a delimitator?
asio socket, split incoming data at a delimitator?

Time:02-25

I am reading data from a asio socket in c .

I need to parse the incoming data as json. To do this, i need to get a single json string entry. I am adding a character ';' at the end of the json string, now i need to split at that character on read. i am trying this:

int main()
{
    asio::io_service service;
    asio::ip::tcp::endpoint endpoint(asio::ip::address::from_string("127.0.0.1"), 4444);
    asio::ip::tcp::socket socket(service);
    std::cout << "[Client] Connecting to server..." << std::endl;
    socket.connect(endpoint);
    std::cout << "[Client] Connection successful" << std::endl;


    while (true)
    {

        std::string str;
        str.resize(2048);
        asio::read(socket, asio::buffer(str));

        std::string parsed;
        std::stringstream input_stringstream(str);


        if (std::getline(input_stringstream, parsed, ';'))
        {
                            
            std::cout << parsed << std::endl;
            std::cout<<std::endl;
        }

    

    }
}

But it gives me random sections of the string.

The full message is: (for testing, not json formatted)

this is the message in full, no more no less ;

and I get:

 full, no more no less

this is the message in full, no more no less

ull, no more no less

is is the message in full, no more no less

l, no more no less

 is the message in full, no more no less

 no more no less

Where am i going wrong here?

Thanks!

CodePudding user response:

I'd use read_until:

#include <boost/asio.hpp>
#include <iostream>
#include <iomanip>
namespace asio = boost::asio;
using asio::ip::tcp;

int main()
{
    asio::io_service service;
    tcp::socket      socket(service);
    socket.connect({{}, 4444});

    std::string str;
    while (auto n = asio::read_until(socket, asio::dynamic_buffer(str), ';')) {
        std::cout << std::quoted(std::string_view(str).substr(0, n - 1)) << std::endl;
        str.erase(0, n);
    }
}

For example with a sample server:

paste -sd\; /etc/dictionaries-common/words  | netcat -l -p 4444

Output is:

"A"
"A's"
"AMD"
"AMD's"
"AOL"
"AOL's"
"Aachen"
"Aachen's"
"Aaliyah"
"Aaliyah's"
"Aaron"
"Aaron's"
"Abbas"
"Abbas's"
"Aberdeen's"
...
"zucchinis"
"zwieback"
"zwieback's"
"zygote"
"zygote's"
"zygotes"
"Ångström"
"Ångström's"
"éclair"
"éclair's"
"éclairs"
"éclat"
"éclat's"
"élan"
"élan's"
"émigré"
"émigré's"
"émigrés"
"épée"
"épée's"
"épées"
"étude"
"étude's"

Additional hints

You can use any dynamic buffer. Here's streambuf:

for (asio::streambuf buf; auto n = asio::read_until(socket, buf, ';');) {
    std::cout << std::string_view(
                     asio::buffer_cast<char const*>(buf.data()), n)
              << std::endl;
    buf.consume(n);
}

Or, hybrid, showing that dynamic_string_buffer models the same concept as streambuf:

std::string str;
for (auto buf = asio::dynamic_buffer(str);
     auto n   = asio::read_until(socket, buf, ';');) {
    std::cout << std::string_view(
                     asio::buffer_cast<char const*>(buf.data()), n)
              << std::endl;
    buf.consume(n);
}

Or also:

std::vector<unsigned char> vec;
for (auto buf = asio::dynamic_buffer(vec);
     auto n   = asio::read_until(socket, buf, ';');) {
    std::cout << std::string_view(
                     asio::buffer_cast<char const*>(buf.data()), n)
              << std::endl;
    buf.consume(n);
}
  • Related