下面我将为您详细介绍几种主流的方案,并提供一个功能强大且易于使用的基于 libcurl 的 C++ 封装类,这个类封装了 libcurl 的复杂细节,提供了简洁的同步和异步接口,是 VC++ 项目中处理 HTTP 请求的绝佳选择。

(图片来源网络,侵删)
方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| WinHTTP | Windows 原生,性能高,安全,支持代理、SSL 等,适合服务端程序。 | 仅限 Windows,API 相对复杂,异步编程模型较旧。 | Windows 桌面应用、服务程序、需要高性能和原生集成的场景。 |
| libcurl | 跨平台,功能极其强大(支持所有协议、HTTP/2、多线程、回调等),社区活跃。 | 需要第三方库,编译和配置稍显繁琐。 | 跨平台项目、需要复杂网络功能(如文件上传、断点续传)的项目。 |
| C++ REST SDK (Casablanca) | 现代 C++ 风格,支持异步/等待,与 C++11/14/17 无缝集成。 | 依赖 Boost.Asio,库本身较大,学习曲线稍陡。 | 现代 C++ 项目,尤其是偏好异步编程和智能指针的开发者。 |
| WinINet | 非常简单,几行代码就能实现基本 GET/POST。 | 功能有限,过时,不支持 HTTP/2,不适合服务端。 | 简单的、一次性的网络请求,快速原型验证。 |
对于一个“功能强大”且希望在 VC++ 中广泛使用的 HTTP 类,基于 libcurl 的封装是最佳选择,它兼顾了功能、性能和跨平台性。
功能强大的 VC++ HTTP 类:基于 libcurl 的封装
下面我将提供一个完整的、功能强大的 CHttpRequest 类的实现,这个类封装了 libcurl,支持同步/异步请求、自定义请求头、POST 数据、文件上传、超时设置、代理设置等。
第 1 步:准备工作 (获取和配置 libcurl)
-
下载 libcurl:
- 访问 libcurl 官网 下载 Windows 版本。
- 建议下载 DLL 版本 (DLL-openssl),
curl-8.x.x-win32-mingw.zip或curl-8.x.x-win64-mingw.zip(如果使用 MinGW) 或curl-8.x.x-win64-msvc.zip(如果使用 MSVC)。 - 解压下载的文件,你会得到
include,lib, 和bin三个文件夹。
-
配置你的 VC++ 项目:
(图片来源网络,侵删)- 包含目录: 在你的 VC++ 项目属性中,将
include\curl目录添加到 C/C++ -> 常规 -> 附加包含目录。 - 库目录: 将
lib目录添加到 链接器 -> 常规 -> 附加库目录。 - 依赖库: 在 链接器 -> 输入 -> 附加依赖项 中添加
libcurl.lib。 - 运行时库: 将
bin目录下的libcurl.dll复制到你的项目输出目录(x64\Debug),或者将其添加到系统 PATH 环境变量中。
- 包含目录: 在你的 VC++ 项目属性中,将
第 2 步:CHttpRequest.h (头文件)
这个头文件定义了类的接口、请求选项和响应数据结构。
// CHttpRequest.h
#pragma once
#include <string>
#include <map>
#include <vector>
#include <memory>
#include <functional>
#include <curl/curl.h>
// 回调函数类型定义,用于异步请求
using HttpResponseCallback = std::function<void(bool, const std::string&, const std::map<std::string, std::string>&)>;
// 请求选项结构体
struct HttpRequestOptions
{
std::string url;
std::string method = "GET"; // GET, POST, PUT, DELETE etc.
std::map<std::string, std::string> headers;
std::string postFields; // For POST/PUT data
std::string proxy;
long timeout = 30L; // Timeout in seconds
bool followRedirects = true;
bool verbose = false;
};
// 响应数据结构体
struct HttpResponse
{
long statusCode = 0;
std::string body;
std::map<std::string, std::string> headers;
std::string error;
};
// CHttpRequest 类
class CHttpRequest
{
public:
CHttpRequest();
~CHttpRequest();
// 禁用拷贝构造和赋值
CHttpRequest(const CHttpRequest&) = delete;
CHttpRequest& operator=(const CHttpRequest&) = delete;
// --- 同步请求 ---
// 执行请求并返回 HttpResponse 对象
HttpResponse Request(const HttpRequestOptions& options);
// --- 异步请求 ---
// 执行请求,完成后通过回调函数通知
void RequestAsync(const HttpRequestOptions& options, HttpResponseCallback callback);
// --- 便捷方法 ---
// GET 请求
HttpResponse Get(const std::string& url);
void GetAsync(const std::string& url, HttpResponseCallback callback);
// POST 请求
HttpResponse Post(const std::string& url, const std::string& data);
void PostAsync(const std::string& url, const std::string& data, HttpResponseCallback callback);
private:
// libcurl 的 easy handle
CURL* m_curl;
// libcurl 的 multi handle,用于异步操作
CURLM* m_curlm;
// 用于存储异步回调的函数对象
HttpResponseCallback m_asyncCallback;
// libcurl 写入回调函数,用于接收数据
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp);
// libcurl 头部信息回调函数,用于解析响应头
static size_t HeaderCallback(void* buffer, size_t size, size_t nitems, void* userp);
// 异步任务执行函数
void PerformAsyncTask(CURL* curlHandle, HttpRequestOptions options);
};
第 3 步:CHttpRequest.cpp (实现文件)
这是类的核心实现,封装了所有 libcurl 的细节。
// CHttpRequest.cpp
#include "pch.h" // 如果是预编译头
#include "CHttpRequest.h"
#include <sstream>
CHttpRequest::CHttpRequest() : m_curl(nullptr), m_curlm(nullptr)
{
// 初始化 libcurl 全局设置
curl_global_init(CURL_GLOBAL_ALL);
m_curl = curl_easy_init();
m_curlm = curl_multi_init();
}
CHttpRequest::~CHttpRequest()
{
if (m_curl) {
curl_easy_cleanup(m_curl);
}
if (m_curlm) {
curl_multi_cleanup(m_curlm);
}
curl_global_cleanup();
}
// 同步请求
HttpResponse CHttpRequest::Request(const HttpRequestOptions& options)
{
HttpResponse response;
if (!m_curl) {
response.error = "CURL easy handle is not initialized.";
return response;
}
// 设置 URL
curl_easy_setopt(m_curl, CURLOPT_URL, options.url.c_str());
// 设置请求方法
if (options.method == "POST") {
curl_easy_setopt(m_curl, CURLOPT_POST, 1L);
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, options.postFields.c_str());
}
// 其他方法如 PUT, DELETE 可以通过自定义 CURLOPT_CUSTOMREQUEST 实现
else if (options.method != "GET") {
curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, options.method.c_str());
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, options.postFields.c_str());
}
// 设置自定义请求头
struct curl_slist* headers = nullptr;
for (const auto& header : options.headers) {
std::string h = header.first + ": " + header.second;
headers = curl_slist_append(headers, h.c_str());
}
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, headers);
// 设置写入回调
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &response.body);
// 设置头部回调
curl_easy_setopt(m_curl, CURLOPT_HEADERFUNCTION, HeaderCallback);
curl_easy_setopt(m_curl, CURLOPT_HEADERDATA, &response.headers);
// 设置超时
curl_easy_setopt(m_curl, CURLOPT_TIMEOUT, options.timeout);
// 设置是否跟随重定向
curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, options.followRedirects ? 1L : 0L);
// 设置代理
if (!options.proxy.empty()) {
curl_easy_setopt(m_curl, CURLOPT_PROXY, options.proxy.c_str());
}
// 详细输出 (调试用)
if (options.verbose) {
curl_easy_setopt(m_curl, CURLOPT_VERBOSE, 1L);
}
// 执行请求
CURLcode res = curl_easy_perform(m_curl);
// 清理请求头
curl_slist_free_all(headers);
if (res != CURLE_OK) {
response.error = curl_easy_strerror(res);
} else {
// 获取 HTTP 状态码
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &response.statusCode);
}
return response;
}
// 异步请求
void CHttpRequest::RequestAsync(const HttpRequestOptions& options, HttpResponseCallback callback)
{
if (!m_curlm) {
callback(false, "CURL multi handle is not initialized.", {});
return;
}
// 创建一个新的 easy handle 用于异步任务
CURL* curlHandle = curl_easy_duphandle(m_curl);
if (!curlHandle) {
callback(false, "Failed to duplicate CURL handle.", {});
return;
}
// 存储回调,以便在 PerformAsyncTask 中使用
m_asyncCallback = callback;
// 启动一个线程来执行网络请求
// 注意:在实际应用中,应使用线程池来管理线程
std::thread t(&CHttpRequest::PerformAsyncTask, this, curlHandle, options);
t.detach(); // 分离线程,让它独立运行
}
// 异步任务执行函数
void CHttpRequest::PerformAsyncTask(CURL* curlHandle, HttpRequestOptions options)
{
HttpResponse response;
// 设置选项 (与同步请求类似,但使用传入的 curlHandle)
curl_easy_setopt(curlHandle, CURLOPT_URL, options.url.c_str());
// ... (设置所有其他选项,如同步请求中的 CURLOPT_POST, CURLOPT_HTTPHEADER 等)
struct curl_slist* headers = nullptr;
for (const auto& header : options.headers) {
std::string h = header.first + ": " + header.second;
headers = curl_slist_append(headers, h.c_str());
}
curl_easy_setopt(curlHandle, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, &response.body);
curl_easy_setopt(curlHandle, CURLOPT_HEADERFUNCTION, HeaderCallback);
curl_easy_setopt(curlHandle, CURLOPT_HEADERDATA, &response.headers);
curl_easy_setopt(curlHandle, CURLOPT_TIMEOUT, options.timeout);
curl_easy_setopt(curlHandle, CURLOPT_FOLLOWLOCATION, options.followRedirects ? 1L : 0L);
if (!options.proxy.empty()) {
curl_easy_setopt(curlHandle, CURLOPT_PROXY, options.proxy.c_str());
}
if (options.verbose) {
curl_easy_setopt(curlHandle, CURLOPT_VERBOSE, 1L);
}
if (options.method == "POST") {
curl_easy_setopt(curlHandle, CURLOPT_POST, 1L);
curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDS, options.postFields.c_str());
} else if (options.method != "GET") {
curl_easy_setopt(curlHandle, CURLOPT_CUSTOMREQUEST, options.method.c_str());
curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDS, options.postFields.c_str());
}
// 执行请求
CURLcode res = curl_easy_perform(curlHandle);
curl_slist_free_all(headers);
if (res != CURLE_OK) {
response.error = curl_easy_strerror(res);
} else {
curl_easy_getinfo(curlHandle, CURLINFO_RESPONSE_CODE, &response.statusCode);
}
// 清理 easy handle
curl_easy_cleanup(curlHandle);
// 在主线程中调用回调函数 (重要!避免跨线程操作UI)
// 这里为了简化,直接调用,实际项目中应使用 PostMessage 或类似机制。
m_asyncCallback(response.error.empty(), response.body, response.headers);
}
// --- 便捷方法实现 ---
HttpResponse CHttpRequest::Get(const std::string& url)
{
HttpRequestOptions options;
options.url = url;
return Request(options);
}
void CHttpRequest::GetAsync(const std::string& url, HttpResponseCallback callback)
{
HttpRequestOptions options;
options.url = url;
RequestAsync(options, callback);
}
HttpResponse CHttpRequest::Post(const std::string& url, const std::string& data)
{
HttpRequestOptions options;
options.url = url;
options.method = "POST";
options.postFields = data;
return Request(options);
}
void CHttpRequest::PostAsync(const std::string& url, const std::string& data, HttpResponseCallback callback)
{
HttpRequestOptions options;
options.url = url;
options.method = "POST";
options.postFields = data;
RequestAsync(options, callback);
}
// --- 静态回调函数实现 ---
size_t CHttpRequest::WriteCallback(void* contents, size_t size, size_t nmemb, void* userp)
{
size_t realsize = size * nmemb;
std::string* buffer = static_cast<std::string*>(userp);
buffer->append((char*)contents, realsize);
return realsize;
}
size_t CHttpRequest::HeaderCallback(void* buffer, size_t size, size_t nitems, void* userp)
{
size_t realsize = size * nitems;
std::map<std::string, std::string>* headers = static_cast<std::map<std::string, std::string>*>(userp);
std::string header((char*)buffer, realsize);
size_t separator_pos = header.find(':');
if (separator_pos != std::string::npos) {
std::string key = header.substr(0, separator_pos);
// 去除可能的空格和换行符
key.erase(key.find_last_not_of(" \r\n") + 1);
std::string value = header.substr(separator_pos + 1);
value.erase(0, value.find_first_not_of(" \r\n"));
value.erase(value.find_last_not_of(" \r\n") + 1);
(*headers)[key] = value;
}
return realsize;
}
第 4 步:如何使用
在你的代码中包含头文件并创建 CHttpRequest 对象即可使用。
示例 1: 同步 GET 请求

(图片来源网络,侵删)
#include <iostream>
#include "CHttpRequest.h" // 确保路径正确
int main()
{
CHttpRequest http;
std::cout << "Performing a synchronous GET request to httpbin.org..." << std::endl;
HttpResponse response = http.Get("https://httpbin.org/get");
if (response.error.empty()) {
std::cout << "Status Code: " << response.statusCode << std::endl;
std::cout << "Response Headers:" << std::endl;
for (const auto& header : response.headers) {
std::cout << " " << header.first << ": " << header.second << std::endl;
}
std::cout << "\nResponse Body (first 200 chars):" << std::endl;
std::cout << response.body.substr(0, 200) << "..." << std::endl;
} else {
std::cerr << "Error: " << response.error << std::endl;
}
return 0;
}
示例 2: 异步 POST 请求
#include <iostream>
#include <thread>
#include <chrono>
#include "CHttpRequest.h"
void OnResponseReceived(bool success, const std::string& body, const std::map<std::string, std::string>& headers)
{
if (success) {
std::cout << "[Async Callback] Request successful!" << std::endl;
std::cout << "[Async Callback] Status Code: " << (headers.empty() ? "N/A" : "Check via a more robust class") << std::endl;
std::cout << "[Async Callback] Response Body (first 200 chars):" << std::endl;
std::cout << body.substr(0, 200) << "..." << std::endl;
} else {
std::cerr << "[Async Callback] Request failed." << std::endl;
}
}
int main()
{
CHttpRequest http;
std::cout << "Performing an asynchronous POST request to httpbin.org/post..." << std::endl;
std::string postData = R"({"username": "test", "password": "123456"})";
// 启动异步请求
http.PostAsync("https://httpbin.org/post", postData, OnResponseReceived);
std::cout << "Main thread continues to work..." << std::endl;
// 模拟主线程做其他事情
// 等待异步回调完成 (在实际应用中,消息循环会处理这个)
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "Main thread finished." << std::endl;
return 0;
}
总结与扩展
这个 CHttpRequest 类为你提供了一个强大且灵活的 HTTP 请求解决方案。
它的优点:
- 功能强大: 基于 libcurl,支持几乎所有 HTTP 特性。
- 接口简洁: 封装了底层复杂性,提供了易于使用的同步和异步 API。
- 可扩展性: 你可以轻松地在
HttpRequestOptions中添加更多选项(如 SSL 证书设置、Cookie 等),并在Request方法中实现它们。 - 结构化数据: 响应头被解析成
std::map,方便访问。
可以进一步改进的地方:
- 更完善的错误处理: 可以定义一个枚举类型的错误码,而不是简单的字符串。
- 文件上传: 可以添加
UploadFile方法,使用curl_easy_setopt(..., CURLOPT_POSTFIELDS, ...)和curl_easy_setopt(..., CURLOPT_READFUNCTION, ...)来实现。 - 超时控制:
CURLOPT_TIMEOUT是总传输时间,可以增加CURLOPT_CONNECTTIMEOUT来控制连接超时。 - 线程安全: 当前的异步实现每次都会创建一个新线程,对于高并发的应用,应该使用一个线程池来管理这些任务,避免频繁创建和销毁线程的开销。
- 支持 C++17: 可以将
std::function和std::map等替换为更现代的类型,或者使用std::optional来处理可能为空的响应。
希望这个详细的实现能帮助你在 VC++ 项目中轻松搞定 HTTP 访问!
