2016-06-26 2 views
1

Asio에서 streambuf 관리에 어려움을 겪고 있습니다. 우분투에서 1.58을 사용하고 있습니다. 첫째, 여기에 코드입니다 :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()를 사용하여 bufferbuf.consume()을 호출하여 버퍼를 지우고, 고르지 않은 수의 문자가 반환되는 즉시 다른 버퍼를 전달하는 등 많은 변형을 시도해 보았습니다. 솔루션이 작동했습니다.

무엇이 여기에 있습니까? THX

주석 교환 결정된
+1

코드가 작동합니다. 제 생각에 당신은 [chunked transfer encoding] (https://en.wikipedia.org/wiki/Chunked_transfer_encoding)에 익숙하지 않고 스트림에있는 "몇 개의 추가 문자"는 실제로 청크 헤더입니다. – rhashimoto

+0

안녕하세요, 저는 분명히이 지점을 놓치고있었습니다. 귀하의 발언 덕분에 나는 chunck 구분 기호를 추적하기 위해 코드를 변경하기 시작했지만 asio 메시지는 서버 chunck와 관련이 없다는 것을 알았습니다 (구분 기호는 버퍼 중간에있을 수 있음). 그래서 전략을 바꿨습니다. 처음에는 전체 메시지를 버퍼에로드하고 거기에서 스트림 스트링을 채웠습니다. –

답변

2

, 서버 chunked transfer encoding를 사용 :

청크 전송 코딩 데이터가 전송되는 하이퍼 텍스트 전송 프로토콜 (HTTP) 1.1 버전 에서 데이터 전송 메커니즘 "청크"시리즈. 그것은 각각의 덩어리 16 진수 청크 길이와 CRLF로 시작

... 장소 콘텐츠 길이 헤더의 에 전송 인코딩 HTTP 헤더를 사용합니다. 청크 분할 전송에 익숙하지 않은 경우 실제로 데이터 스트림을 손상시키는 이상한 문자가있는 것으로 보입니다.

청크 분할 전송 인코딩은 응답 본문을 전송하기 전에 응답 본문의 정확한 길이를 결정하는 것이 편리하지 않을 때 일반적으로 사용됩니다. 수신기는 최종 길이가 0 인 청크를 처리 할 때까지 본체 길이를 알지 못합니다 (마지막 "헤더", 일명 "예고편"이 최종 청크를 따라갈 수 있습니다). 부스트와

:: ASIO, 당신은 CRLF 구분 기호를 통해 청크 헤더를 읽을 수 길이를 분석 한 다음 청크 데이터를 얻을 수있는 transfer_exactlyasync_read()를 사용하는 async_read_until()를 사용할 수 있습니다. 읽음에 streambuf을 사용하기 시작하면 추가 streambuf에서 특정 양의 데이터를 추출하는 것은 here에 설명되어 있으므로 추가 데이터를 버퍼링 할 수 있으므로 동일한 streambuf 인스턴스를 계속 사용해야합니다. 또한 청크 데이터는 폐기해야하는 CRLF (길이에 포함되지 않음)로 끝납니다.

boost :: asio로 자신의 HTTP 클라이언트를 작성하는 것은 시간과 호기심이 있다면 재미있게 보일 수 있지만 모든 옵션 (예 : 압축, 예고편, 리다이렉션). libcurl과 같은 성숙한 클라이언트 라이브러리가 사용자의 필요에 맞는지 여부를 고려할 수 있습니다.

관련 문제