Reading JSON from a socket using boost::asio Reading JSON from a socket using boost::asio json json

Reading JSON from a socket using boost::asio


This is an area for application level protocol

Either

  • read until the stream end (the sender disconnects); this doesn't work with connections that are kept alive for more than a single message
  • supply a header like Content-Length: 12346\r\n to know in advance how much to read
  • supply a delimiter (a bit like mime boundaries, but you could use any sequence that is not allowed/supported as part of the JSON payload) (async_read_until)
  • Treat the payload as "binary-style" (BSON e.g.) and supply a (network-order) length field before the text transmission.

The ASIO Http server example contains a pretty nice pattern for parsing HTTP request/headers that you could use. This assumes that your parser can detect completeness and just 'soft-fails' until all information is present.

void connection::handle_read(const boost::system::error_code& e,    std::size_t bytes_transferred){  if (!e)  {    boost::tribool result;    boost::tie(result, boost::tuples::ignore) = request_parser_.parse(        request_, buffer_.data(), buffer_.data() + bytes_transferred);    if (result)    {      request_handler_.handle_request(request_, reply_);      boost::asio::async_write(socket_, reply_.to_buffers(),          boost::bind(&connection::handle_write, shared_from_this(),            boost::asio::placeholders::error));    }    else if (!result)    {      reply_ = reply::stock_reply(reply::bad_request);      boost::asio::async_write(socket_, reply_.to_buffers(),          boost::bind(&connection::handle_write, shared_from_this(),            boost::asio::placeholders::error));    }    else    {      socket_.async_read_some(boost::asio::buffer(buffer_),          boost::bind(&connection::handle_read, shared_from_this(),            boost::asio::placeholders::error,            boost::asio::placeholders::bytes_transferred));    }  }  else if (e != boost::asio::error::operation_aborted)  {    connection_manager_.stop(shared_from_this());  }}

I've provided an answer that parses JSON using Boost Spirit earlier Parse a substring as JSON using QJsonDocument; you could use this to detect the end of a proper JSON document (and if it's incomplete, the end will coincide with the start)


2 problems here : 1) tell the server how many bytes to read; 2) read the JSON

for 1) you can make your own simple protocol

300#my message here

sends a 300 byte sized message; # is the delimiter between size and message

int write_request(socket_t &socket, const char* buf_json){  std::string buf;  size_t size_json = strlen(buf_json);  buf = std::to_string(static_cast<long long unsigned int>(size_json));  buf += "#";  buf += std::string(buf_json);  return (socket.write_all(buf.data(), buf.size()));}

to read on the server

//parse header, one character at a time and look for for separator #  //assume size header lenght less than 20 digits  for (size_t idx = 0; idx < 20; idx++)  {    char c;    if ((recv_size = ::recv(socket.m_sockfd, &c, 1, 0)) == -1)    {      std::cout << "recv error: " << strerror(errno) << std::endl;      return str;    }    if (c == '#')    {      break;    }    else    {      str_header += c;    }  }

to read JSON, you can use

https://github.com/nlohmann/json