2016-06-26 4 views
1

私はAsioのstreambuf管理に苦労しています。私はubuntuで1.58のboostを使用しています。まず、ここでのコードは次のとおりです。ここでboost :: asio :: streambuf httpsを通してxmlデータを取得

#include <iostream> 

#include <boost/bind.hpp> 
#include <boost/asio.hpp> 
#include <boost/asio/ssl.hpp> 
#include <boost/asio/buffer.hpp> 
#include <boost/asio/completion_condition.hpp> 

class example 
{ 
private: 
    // asio components 
    boost::asio::io_service service; 
    boost::asio::ssl::context context; 
    boost::asio::ip::tcp::resolver::query query; 
    boost::asio::ip::tcp::resolver resolver; 
    boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket; 
    boost::asio::streambuf requestBuf, responseBuf; 

    // callbacks 
    void handle_resolve(const boost::system::error_code& err, 
          boost::asio::ip::tcp::resolver::iterator endpoint_iterator) 
    { 
     if (!err) 
     { 
      boost::asio::async_connect(socket.lowest_layer(), endpoint_iterator, 
       boost::bind(&example::handle_connect, this, 
        boost::asio::placeholders::error)); 
     } 
    } 
    void handle_connect(const boost::system::error_code& err) 
    { 
     if (!err) 
     { 
      socket.async_handshake(boost::asio::ssl::stream_base::client, 
       boost::bind(&example::handle_handshake, this, 
       boost::asio::placeholders::error)); 
     } 
    } 
    void handle_handshake(const boost::system::error_code& err) 
    { 
     if (!err) 
     { 
      boost::asio::async_write(socket, requestBuf, 
       boost::bind(&example::handle_write_request, this, 
        boost::asio::placeholders::error, 
        boost::asio::placeholders::bytes_transferred)); 
     } 
    } 

    void handle_write_request(const boost::system::error_code& err, size_t bytes_transferred) 
     { 
      if (!err) 
      { 
       boost::asio::async_read(socket, responseBuf, 
        boost::asio::transfer_at_least(1), 
        boost::bind(&example::handle_read, this, 
         boost::asio::placeholders::error, 
         boost::asio::placeholders::bytes_transferred)); 
      } 
     } 

    void handle_read(const boost::system::error_code& err, 
          size_t bytes_transferred) 
    { 
     if (!err) 
     { 
      boost::asio::async_read(socket, responseBuf, 
       boost::asio::transfer_at_least(1), 
       boost::bind(&example::handle_read, this, 
        boost::asio::placeholders::error, 
        boost::asio::placeholders::bytes_transferred)); 
     } 
    } 
public: 
    example() : context(boost::asio::ssl::context::sslv23), 
       resolver(service), 
       socket(service, context), 
       query("www.quandl.com", "443") {} 

    void work() 
    { 
     // set security 
     context.set_default_verify_paths(); 
     socket.set_verify_mode(boost::asio::ssl::verify_peer); 

     // in case this no longer works, generate a new key from https://www.quandl.com/ 
     std::string api_key = "4jufXHL8S4XxyM6gzbA_"; 

     // build the query 
     std::stringstream ss; 

     ss << "api/v3/datasets/"; 
     ss << "RBA" << "/" << "FXRUKPS" << "."; 
     ss << "xml" << "?sort_order=asc"; 
     ss << "?api_key=" << api_key; 
     ss << "&start_date=" << "2000-01-01"; 
     ss << "&end_date=" << "2003-01-01"; 

     std::ostream request_stream(&requestBuf); 
     request_stream << "GET /"; 
     request_stream << ss.str(); 
     request_stream << " HTTP/1.1\r\n"; 
     request_stream << "Host: " << "www.quandl.com" << "\r\n"; 
     request_stream << "Accept: */*\r\n"; 
     request_stream << "Connection: close\r\n\r\n"; 

     resolver.async_resolve(query, 
      boost::bind(&example::handle_resolve, this, 
       boost::asio::placeholders::error, 
       boost::asio::placeholders::iterator)); 

     service.run(); 

     std::cout << &responseBuf; 
    } 
}; 

int main(int argc, char * argv[]) 
{ 
    // this is a test 
    int retVal; try 
    { 
     example f; f.work(); 
     retVal = 0; 

    } 
    catch (std::exception & ex) 
    { 
     std::cout << "an error occured:" << ex.what() << std::endl; 
     retVal = 1; 
    } 

    return retVal; 

} 

は私の問題である。その結果得られたデータは、(数千文字)長すぎるされていない場合の例では、完璧に動作します。 しかし、async_readが不均等な文字数を返すと(デフォルトbytes_transferredは512文字です)、streambufが壊れてしまい、次のasync_read呼び出しにいくつかの余分な文字が含まれます。

transfer_exactly()を使用して、バッファをクリアするためにstreambuf.consume()を呼び出し、返された文字の数が不均一であることを検出するとすぐに別のバッファを渡すなど、多くのバリエーションを試してみました。ソリューションが機能しました。

私はここで何が欠けていますか? THX

+1

あなたのコードは私の仕事です。私の疑問はあなたが[チャンク転送エンコーディング](https://en.wikipedia.org/wiki/Chunked_transfer_encoding)に精通しておらず、あなたのストリーム内の「少数の余分な文字」が実際にチャンクヘッダであるということです。 – rhashimoto

+0

こんにちは、間違いなく、私はこの点を見逃していました。あなたの発言のおかげで、私はchunckデリミタを追跡するためにコードを変更し始めましたが、asioメッセージはサーバーchuncks(区切り文字はバッファの中央にあるかもしれません)に関連していないことに気付きました。だから私は自分の戦略を変え、最初はバッファーにメッセージ全体をロードし、そこからストリームストリングを埋めました。 –

答えて

2

コメント交換で決定されるように、サーバはchunked transfer encodingを使用している:

チャンク転送符号化は、データがに送信されるハイパーテキスト転送プロトコル(HTTP)のバージョン1.1 のデータ転送機構であります 一連の「チャンク」。これは、Content-Lengthヘッダの代わり に転送エンコーディングHTTPヘッダーを使用し、...

各チャンクは、16進数のチャンクの長さとCRLFで始まります。チャンク転送に慣れていない場合は、データストリームに奇妙な文字が壊れているように見えます。

チャンク転送符号化は、送信される前に応答本体の正確な長さを決定するのが便利でない場合に、一般的に使用されます。最終的なゼロ長チャンクを処理するまで、受信者はボディの長さを知らないことになります(末尾の "ヘッダー"、別名 "トレーラー"が最後のチャンクに続く場合があります)。ブースト

:: ASIO、あなたは、CRLF区切り文字によってチャンクヘッダーを読み取るためにasync_read_until()を使用して長さを解析して、チャンクデータを取得するためにtransfer_exactlyasync_read()を使用することができます。読み込みにstreambufを使用し始めると、追加のデータをバッファリングする可能性があるため、同じstreambufインスタンスを引き続き使用する必要があります(streambufから特定の量のデータを抽出することについては、hereを参照してください)。また、チャンクデータは、破棄すべきCRLF(長さには含まれない)で終わることにも注意してください。

boost :: asioで独自のHTTPクライアントを書くのは時間と好奇心があれば面白いかもしれませんが、すべてのオプション(圧縮、予告編など)を扱うのは容易ではありません。リダイレクト)。 libcurlのような成熟したクライアントライブラリがあなたのニーズに合っているかどうかを検討したいかもしれません。

関連する問題