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

功能强大的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)

  1. 下载 libcurl:

    • 访问 libcurl 官网 下载 Windows 版本。
    • 建议下载 DLL 版本 (DLL-openssl)curl-8.x.x-win32-mingw.zipcurl-8.x.x-win64-mingw.zip (如果使用 MinGW) 或 curl-8.x.x-win64-msvc.zip (如果使用 MSVC)。
    • 解压下载的文件,你会得到 include, lib, 和 bin 三个文件夹。
  2. 配置你的 VC++ 项目:

    功能强大的vc访问http网页类
    (图片来源网络,侵删)
    • 包含目录: 在你的 VC++ 项目属性中,将 include\curl 目录添加到 C/C++ -> 常规 -> 附加包含目录
    • 库目录: 将 lib 目录添加到 链接器 -> 常规 -> 附加库目录
    • 依赖库: 在 链接器 -> 输入 -> 附加依赖项 中添加 libcurl.lib
    • 运行时库: 将 bin 目录下的 libcurl.dll 复制到你的项目输出目录(x64\Debug),或者将其添加到系统 PATH 环境变量中。

第 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 请求

功能强大的vc访问http网页类
(图片来源网络,侵删)
#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 请求解决方案。

它的优点:

  1. 功能强大: 基于 libcurl,支持几乎所有 HTTP 特性。
  2. 接口简洁: 封装了底层复杂性,提供了易于使用的同步和异步 API。
  3. 可扩展性: 你可以轻松地在 HttpRequestOptions 中添加更多选项(如 SSL 证书设置、Cookie 等),并在 Request 方法中实现它们。
  4. 结构化数据: 响应头被解析成 std::map,方便访问。

可以进一步改进的地方:

  • 更完善的错误处理: 可以定义一个枚举类型的错误码,而不是简单的字符串。
  • 文件上传: 可以添加 UploadFile 方法,使用 curl_easy_setopt(..., CURLOPT_POSTFIELDS, ...)curl_easy_setopt(..., CURLOPT_READFUNCTION, ...) 来实现。
  • 超时控制: CURLOPT_TIMEOUT 是总传输时间,可以增加 CURLOPT_CONNECTTIMEOUT 来控制连接超时。
  • 线程安全: 当前的异步实现每次都会创建一个新线程,对于高并发的应用,应该使用一个线程池来管理这些任务,避免频繁创建和销毁线程的开销。
  • 支持 C++17: 可以将 std::functionstd::map 等替换为更现代的类型,或者使用 std::optional 来处理可能为空的响应。

希望这个详细的实现能帮助你在 VC++ 项目中轻松搞定 HTTP 访问!