2016-11-24 9 views
2

私はブーストチュートリアルから次のコードを使用して、サーバからjson文字列を取得しています。boost :: asioに基づいた低速のhttpクライアント - (チャンク転送)

問題は、実行には時間がかかります。つまり、クライアントとサーバーの両方がローカルホスト上にあるためです。私はプログラムの最後の2行を削除する場合は、この中に、つまり:

しばらく(後押し:: ASIO ::読み(ソケット、応答、 ブースト:: ASIO :: transfer_at_least(1)、エラーを))

プログラムは非常に高速に実行されます。何が問題なのでしょうか?

 boost::asio::streambuf response; 
     boost::asio::read_until(socket, response, "\r\n"); 

     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 response\n"; 
      return 1; 
     } 
     if (status_code != 200) 
     { 
      std::cout << "Response returned with status code " << status_code << "\n"; 
      return 1; 
     } 


     boost::asio::read_until(socket, response, "\r\n\r\n"); 

     // 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]} 
+0

はい、本当に遅いです。問題はコンテンツの長さに関係していると私は信じています。応答でコンテンツの長さのサイズを正確に読み取るようにコードを修正すると、プログラムは高速に実行されます。 – cateof

+0

@Arunmuサーバーコードにアクセスできません。コンテンツの長さは送信されず、ヘッダーからメッセージがチャンクされていることがわかります。しかし、上記のコードは、接続クローズとコンテンツ長を送信するサーバーに対してもうまく機能しません。 – cateof

+2

ここでは、応答がチャンクされた重要な部分について言及しています:)すべてのチャンクの開始前にチャンクサイズが書かれています。あなたはそれをデコードしてどれくらい読むべきかを知ることができます。あなたはそれについて言及するだけで、多くの時間を節約することができました。 – Arunmu

答えて

2

コメントの議論から、主な問題はチャンクされたデータを読むことであることが理解されました。 HTTPチャンクエンコードされたデータの場合、チャンクデータが始まる前にサイズが16進数でプレフィックスされます。したがって、そのチャンクのコンテンツ長であるサイズを読み取る必要があります。

 asio::streambuf response; 
     // Get till all the headers 
     asio::read_until(socket, response, "\r\n\r\n"); 

     // 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 '\r\n' after the header 
     std::getline(response_stream, header); 

     // Read the Chunk size 
     asio::read_until(socket, response, "\r\n"); 
     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保証が提供されたパターン件までデータを読み出して、それはまた、バッファに多くのデータを読み取ることができるということです。

1

TCP接続が閉じられたときにHTTPで唯一の "EOF" です。この場合、サーバーは接続を閉じる前にわずか2秒後にタイムアウトしていることが幸運です。そうしないと、アプリはもっと​​長く座ってしまうでしょう。

EOF条件を検索するのではなく、読み取るデータの量を知るには、Content-Lengthの値を使用する必要があります。

の理解のためのGoogleの "HTTPパイプライン化"なぜあなたが期待したときにサーバーがTCP接続を閉じていません。

+0

実際にコンテンツの長さを読むことは仕事をしました。しかし、サーバーがContent-Lenghで応答せず、チャンクされたメッセージがある場合はどうなりますか?これでブーストはソリューションを提供しますか? – cateof

+0

Boost ASIOはHTTPよりも低いレベルで動作しません。したがって、あなたは 'Transfer-Encoding'ヘッダとチャンク化されたコンテンツフォーマットを自分で処理するか、または[非公式Boost.Http](https://boostgsoc14.github.io/boost.http/) –

関連する問題