使用asio发起HTTPS请求,响应体为不可打印Unicode字符求助
问题分析与解决
核心问题
你的代码存在两个关键问题导致响应体显示为不可打印字符:
- 错误操作底层TCP Socket:SSL连接建立后,所有数据交互必须通过
ssl_socket对象完成,直接读取底层socket会拿到加密后的SSL数据,而非解密后的明文。你用sock.lowest_layer().available()获取的是加密字节数,并非实际明文长度,后续读取逻辑也因此出错。 - 响应内容被Gzip压缩:Yahoo Finance API默认返回gzip压缩的响应体,即使解密后也是二进制压缩数据,需要解压才能得到可读的JSON文本。
修复后的代码
以下是修正后的完整代码,包含gzip解压逻辑(需要链接zlib库):
#include <string> #include <iostream> #include <vector> #include <sstream> #include <zlib.h> #include "asio.hpp" #include "asio/ssl.hpp" using namespace std; // Gzip解压函数 string gzip_decompress(const vector<char>& data) { z_stream zs; memset(&zs, 0, sizeof(zs)); if (inflateInit2(&zs, 16 + MAX_WBITS) != Z_OK) { throw runtime_error("inflateInit2 failed"); } zs.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data.data())); zs.avail_in = data.size(); int ret; char out_buffer[32768]; string result; do { zs.next_out = reinterpret_cast<Bytef*>(out_buffer); zs.avail_out = sizeof(out_buffer); ret = inflate(&zs, Z_NO_FLUSH); if (result.size() < zs.total_out) { result.append(out_buffer, zs.total_out - result.size()); } } while (ret == Z_OK); inflateEnd(&zs); if (ret != Z_STREAM_END) { throw runtime_error("gzip decompression failed: " + to_string(ret)); } return result; } float getTickerValue(const string& ticker) { float tickerPrice = 0.0f; const string host = "query1.finance.yahoo.com"; typedef asio::ssl::stream<asio::ip::tcp::socket> ssl_socket; // 创建SSL上下文 asio::ssl::context ctx(asio::ssl::context::sslv23_client); ctx.set_default_verify_paths(); asio::io_service io_service; ssl_socket sock(io_service, ctx); // 解析域名并连接 asio::ip::tcp::resolver resolver(io_service); asio::ip::tcp::resolver::query query(host, "https"); asio::connect(sock.lowest_layer(), resolver.resolve(query)); sock.lowest_layer().set_option(asio::ip::tcp::no_delay(true)); // SSL握手并验证证书 sock.set_verify_mode(asio::ssl::verify_peer); sock.set_verify_callback(asio::ssl::rfc2818_verification(host)); sock.handshake(ssl_socket::client); // 构造GET请求,添加Accept-Encoding支持gzip string request = "GET /v11/finance/quoteSummary/" + ticker + "?modules=financialData HTTP/1.1\r\n" "Host: " + host + "\r\n" "Connection: close\r\n" "Accept-Encoding: gzip\r\n\r\n"; // 发送请求 asio::write(sock, asio::buffer(request)); // 读取响应,分离响应头和响应体 vector<char> buffer(4096); string response; bool header_complete = false; string header; vector<char> body; while (true) { size_t bytes_read = sock.read_some(asio::buffer(buffer)); if (bytes_read == 0) break; if (!header_complete) { // 查找响应头结束标记\r\n\r\n string chunk(buffer.data(), bytes_read); size_t header_end = chunk.find("\r\n\r\n"); if (header_end != string::npos) { header += chunk.substr(0, header_end + 4); body.insert(body.end(), chunk.begin() + header_end + 4, chunk.end()); header_complete = true; } else { header += chunk; } } else { body.insert(body.end(), buffer.begin(), buffer.begin() + bytes_read); } } // 输出响应头 cout << "响应头:\n" << header << endl; // 检查是否需要解压 bool is_gzip = (header.find("Content-Encoding: gzip") != string::npos); string body_text; if (is_gzip) { body_text = gzip_decompress(body); } else { body_text = string(body.data(), body.size()); } // 输出解压后的响应体 cout << "\n响应体:\n" << body_text << endl; // 这里可以解析JSON获取tickerPrice,示例省略解析逻辑 // 建议使用JSON库如nlohmann/json来解析 return tickerPrice; } int main() { getTickerValue("AAPL"); return 0; }
关键修改说明
- 移除底层Socket操作:改用
ssl_socket::read_some循环读取所有响应数据,直到连接关闭,确保读取的是解密后的明文。 - 添加Gzip解压逻辑:实现
gzip_decompress函数,使用zlib库对压缩的响应体进行解压。 - 分离响应头与响应体:解析响应头判断是否需要解压,避免将响应头混入解压逻辑。
- 完善请求头:添加
Accept-Encoding: gzip明确告知服务器支持压缩响应。 - 移除不可靠的sleep:循环读取替代休眠,确保完整获取所有响应数据。
编译说明
编译时需要链接asio和zlib库,例如GCC命令:
g++ your_code.cpp -o ticker -lasio -lz
内容的提问来源于stack exchange,提问作者soey_sause
相关产品推荐
相关产品推荐

