基于BOOST :: ASIO-慢慢http客户端 - (转移块)

slow http client based on boost::asio - (Chunked Transfer)

本文关键字:转移 http BOOST ASIO- 慢慢 基于 客户端      更新时间:2023-10-16

我正在使用以下代码(从Boost教程中获取)从服务器中获取JSON字符串。

问题在于执行需要一些时间,即完成超过2秒钟才能完成,并且客户端和服务器都在Localhost上。如果我删除程序的最后2行,则为此处:

while(boost :: asio :: read(套接字,响应, boost :: asio :: trasse_at_least(1),错误))

该程序执行非常快。问题可能是什么?

        boost::asio::streambuf response;
        boost::asio::read_until(socket, response, "rn");
        std::istream response_stream(&response);
        std::string http_version;
        response_stream >> http_version;
        unsigned int status_code;
        response_stream >> status_code;
        std::string status_message;
        std::getline(response_stream, status_message);
        if (!response_stream || http_version.substr(0, 5) != "HTTP/")
        {
          std::cout << "Invalid responsen";
          return 1;
        }
        if (status_code != 200)
        {
          std::cout << "Response returned with status code " << status_code << "n";
          return 1;
        }

        boost::asio::read_until(socket, response, "rnrn");
        // Process the response headers.
        std::string header;
        while (std::getline(response_stream, header) && header != "r");

        if (response.size() > 0)
          std::cout << &response;
        // Read until EOF, writing data to output as we go.
        boost::system::error_code error;
        while (boost::asio::read(socket, response,
              boost::asio::transfer_at_least(1), error))
          std::cout << &response;
        if (error != boost::asio::error::eof)
          throw boost::system::system_error(error);

tcpdump显示从服务器显示一些数据

HTTP/1.1 200 OK
Connection: close
Content-Length: 42
Server: C/1.1
Date: Thu, 24 Nov 2016 07:47:27 GMT
{"Out":[1],"In":[1,2,3,4,5,6]}

从评论中的讨论中可以理解,主要问题是阅读块数据。对于HTTP块编码的数据,在块数据开始之前,大小在十六进制中前缀。因此,必须读取大小是该块的内容长度。

      asio::streambuf response;
      // Get till all the headers
      asio::read_until(socket, response, "rnrn");
      // Check that response is OK. 
      std::istream response_stream(&response);
      std::string http_version;
      response_stream >> http_version;
      std::cout << "Version : " << http_version << std::endl;
      unsigned int status_code;
      response_stream >> status_code;
      std::string status_message;
      std::getline(response_stream, status_message);
      if (!response_stream || http_version.substr(0, 5) != "HTTP/") {
        std::cerr << "invalid response";
        return -1; 
      }
      if (status_code != 200) {
        std::cerr << "response did not returned 200 but " << status_code;
        return -1; 
      }
      //read the headers.
      std::string header;
      while (std::getline(response_stream, header) && header != "r") {
        std::cout << "H: " << header << std::endl;
      }
      bool chunk_size_present = false;
      std::string chunk_siz_str;
      // Ignore the remaining additional 'rn' after the header
      std::getline(response_stream, header);
      // Read the Chunk size
      asio::read_until(socket, response, "rn");
      std::getline(response_stream, chunk_siz_str);
      std::cout << "CS : " << chunk_siz_str << std::endl;
      size_t chunk_size = (int)strtol(chunk_siz_str.c_str(), nullptr, 16);

      // Now how many bytes yet to read from the socket ?
      // response might have some additional data still with it
      // after the last `read_until`
      auto chunk_bytes_to_read = chunk_size - response.size();
      std::cout << "Chunk Length = " << chunk_size << std::endl;
      std::cout << "Additional bytes to read: " << response_stream.gcount() << std::endl;
      std::error_code error;
      size_t n = asio::read(socket, response, asio::transfer_exactly(chunk_bytes_to_read), error);
      if (error) {
        return -1; //throw boost::system::system_error(error);
      }
      std::ostringstream ostringstream_content;
      ostringstream_content << &response;
      auto str_response = ostringstream_content.str();
      std::cout << str_response << std::endl;

有点棘手的部分是asio::read_until保证它可以将数据读取到提供的模式,但也可以将更多数据读取到缓冲区中。

http中唯一的" eof"是关闭TCP连接时。在这种情况下,您很幸运,服务器在关闭连接之前仅在2秒钟后就开始了 - 否则,您的应用程序将坐着更长的时间。

您需要使用Content-Length值来了解要读取多少数据,而不是寻找EOF条件。

Google" HTTP管道"为了理解为什么当您期望它时服务器未关闭TCP连接。