簡體   English   中英

使用 boost::asio 從套接字讀取 JSON

[英]Reading JSON from a socket using boost::asio

我目前正在嘗試使用 boost-asio 的套接字 API 通過網絡將一些 JSON 數據從客戶端傳輸到服務器。 我的客戶基本上是這樣做的:

int from = 1, to = 2;

boost::asio::streambuf buf;
ostream str(&buf);

str << "{"
    << "\"purpose\" : \"request\"" << "," << endl
    << "\"from\" : " << from << "," << endl
    << "\"to\" : " << to << "," << endl
    << "}" << endl;

// Start an asynchronous operation to send the message.
boost::asio::async_write(socket_, buf,
    boost::bind(&client::handle_write, this, _1));

在服務器端,我可以在各種boost::asio::async_read*函數之間進行選擇。 我想使用 JsonCpp 來解析接收到的數據。 研究 JsonCpp API ( http://jsoncpp.sourceforge.net/class_json_1_1_reader.html ) 我發現 Reader 在std::string 、 char* 數組或std::istream之上運行,我可以從中進行操作boost::asio::streambuf傳遞給函數。

關鍵是,據我所知,不一定是一次傳輸整個內容,所以我需要某種確認緩沖區包含足夠的數據來使用 JsonCpp 處理整個文檔。 如何確保緩沖區包含足夠的數據?

這是應用層協議的區域

任何一個

  • 讀取直到流結束(發送方斷開連接); 這不適用於為多條消息保持活動狀態的連接
  • 提供一個像Content-Length: 12346\\r\\n這樣的標題來提前知道要讀取多少
  • 提供分隔符(有點像 mime 邊界,但您可以使用任何不允許/不支持的序列作為 JSON 有效負載的一部分)( async_read_until
  • 將有效負載視為“二進制樣式”(例如 BSON)並在文本傳輸之前提供(網絡順序)長度字段。

ASIO Http 服務器示例包含一個非常好的模式,用於解析您可以使用的 HTTP 請求/標頭。 這假設您的解析器可以檢測完整性並且只是“軟失敗”,直到所有信息都存在。

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

我已經提供了一個使用 Boost Spirit 解析 JSON 的答案,之前使用 QJsonDocument 將子字符串解析為 JSON 您可以使用它來檢測正確 JSON 文檔的結尾(如果不完整,結尾將與開頭重合)

這里有2個問題:1)告訴服務器要讀取多少字節; 2)讀取JSON

1)您可以制定自己的簡單協議

300#my message here

發送 300 字節大小的消息; # 是大小和消息之間的分隔符

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

在服務器上閱讀

//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;
    }
  }

要讀取 JSON,您可以使用

https://github.com/nlohmann/json

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM