Boost::asio::streambuf通过https检索XML数据
boost::asio::streambuf retrieve xml data though https
我正在与亚洲的流媒体管理作斗争。我在ubuntu上使用boost 1.58。首先,下面是代码:
#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.1rn";
request_stream << "Host: " << "www.quandl.com" << "rn";
request_stream << "Accept: */*rn";
request_stream << "Connection: closernrn";
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_transfer是512个字符),streambuf就会损坏,下一次async_read调用将包含一些额外的字符。
我尝试了上面代码的许多变体,但没有成功:使用transfer_exactly(),调用streambuf.consume()来清除缓冲区,在检测到返回的字符数量不均匀时传递另一个缓冲区,等等。这些方法都不起作用。
我在这里错过了什么?Thx
在评论交换中确定,服务器正在使用分块传输编码:
分块传输编码是1.1版本中的数据传输机制HTTP (Hypertext Transfer Protocol,超文本传输协议),其中数据以一种方式发送一系列的"块"。它使用Transfer-Encoding HTTP报头
每个chunk以十六进制chunk长度和CRLF开头。如果你不熟悉块传输,它确实会出现有奇怪的字符破坏你的数据流。
在发送前不方便确定响应体的确切长度时,通常使用分块传输编码。由此可见,在处理最后的零长度数据块之前,接收方不知道数据体长度(注意,在最后的数据块之后可能会出现尾标"header",也就是"trailers")。
使用boost::asio,您可以使用async_read_until()
通过CRLF分隔符读取块头,解析长度,然后使用async_read()
和transfer_exactly
来获取块数据。注意,一旦开始使用streambuf
进行读取,就应该继续使用相同的streambuf
实例,因为它可能缓冲额外的数据(这里将讨论从streambuf
提取特定数量的数据)。还要注意,块数据以CRLF结束(不包括在长度中),您应该丢弃它。
使用boost::asio编写自己的HTTP客户端可能很有指导意义(如果你有时间和好奇心,甚至会很有趣),但要涵盖HTTP标准中的所有选项(例如压缩、预告、重定向)并不容易。您可能需要考虑像libcurl这样成熟的客户端库是否适合您的需求。
- Cppcheck生成xml转储文件
- 使用VerQueryValue检索应用程序的文件描述
- 是否可以从格式字符串中检索"width"
- 使用 pqxx 将 std::vector 存储在 postgresql 中,并从数据库中检索它
- 如何在pugixml中获取节点的内部XML
- 如何使用tinyxml2从XML加载父实体和子实体
- boost xml parsingl将xml的路径作为变量发送
- C++RapidXml-使用first_node()遍历以修改XML文件中节点的值
- 使用 Tinyxml 在 xml 中添加一个子子项
- 增强基于 XML class_id的反序列化
- 在 c++ 中使用 vtd-xml 时,如何摆脱 EOFException?
- 如何在QByteArray中放置和检索位字段而不会感到痛苦?
- 防止在C++中选择错误文件时提升 xml 解析器崩溃
- 如何在Qt中从数据库中检索二进制数据?
- 如何使用libxml2从C 中的XML文件中检索节点和特定元素字符串而不使用XPATH
- 如何使用XPath使用libxml2从C 中的XML文件中检索节点和特定元素字符串
- 使用GetOpenFileName检索xml文件名后,tinyxml加载失败
- 如何根据模式从XML文件中检索数据
- Qt c++ QDomDocument,递归迭代XML数据,检索没有子数据的文本元素
- Boost::asio::streambuf通过https检索XML数据