使用 vcpkg 构建支持 HTTPS 的 libcurl 并解决常见链接错误

适用环境:Windows 10/11 + Visual Studio 2022 + CMake ≥ 3.20

目标读者:希望在 C++ 项目中轻松调用 HTTPS(GET/POST/PUT/DELETE),又被 LNK20xx 链接错误困扰的开发者


目录

  1. 为什么选 vcpkg 与 libcurl
  2. 用 vcpkg 安装带 SSL 的 libcurl
  3. CMake 最小工程模板
  4. HTTPS GET/POST/PUT/DELETE 封装示例
  5. 链接错误排查:__imp_CertOpenStore 等无法解析
  6. LNK4098 运行库冲突的本质与解决
  7. 小结与最佳实践

1. 为什么选 vcpkg 与 libcurl

  • libcurl 跨平台、成熟,支持 HTTP2、HTTP3、FTP、SFTP...;只想做简单 REST 调用也完全够用。
  • vcpkg 由微软维护,直接给 VS / CMake 注入正确的头文件、库路径、依赖链,省去 "到处找 *.lib" 的痛苦。
  • 对 Windows 而言,HTTPS 有两条后端:OpenSSL (跨平台一致)或 Schannel(调用系统 SSL)。文中使用 openssl 变体,切换只需改个 triplet 选项。

2. 用 vcpkg 安装带 SSL 的 libcurl

复制代码
# ① 克隆 vcpkg(已有可跳过)
git clone https://github.com/microsoft/vcpkg
.\vcpkg\bootstrap-vcpkg.bat

# ② 安装 libcurl + openssl
.\vcpkg\vcpkg install curl[openssl]        # 默认动态库 /MD

# ③ 将 vcpkg 集成到 VS/MSBuild(只需一次)
.\vcpkg\vcpkg integrate install

若偏好系统 Schannel:vcpkg install curl[schannel]


3. CMake 最小工程模板

复制代码
your-project/
 ├─ CMakeLists.txt
 └─ main.cpp

cmake_minimum_required(VERSION 3.20)
project(http_demo LANGUAGES CXX)

# 若没做 integrate,可取消注释下一行
# set(CMAKE_TOOLCHAIN_FILE "路径到/vcpkg/scripts/buildsystems/vcpkg.cmake")

find_package(CURL CONFIG REQUIRED)

add_executable(http_demo main.cpp)
target_link_libraries(http_demo PRIVATE CURL::libcurl)

set_target_properties(http_demo PROPERTIES
    CXX_STANDARD 17
    CXX_STANDARD_REQUIRED ON)

4. HTTPS GET/POST/PUT/DELETE 封装示例

复制代码
#include <curl/curl.h>
#include <iostream>
#include <vector>
#include <string>

static size_t write_cb(char* p, size_t s, size_t n, void* ud)
{
    auto* buf = static_cast<std::string*>(ud);
    buf->append(p, s * n);
    return s * n;
}

// 通用请求
CURLcode request(const std::string& url, const std::string& verb,
                 const std::string& body,
                 const std::vector<std::string>& headers,
                 bool ignore_cert, std::string& out, long& status)
{
    CURL* h = curl_easy_init();
    if (!h) return CURLE_FAILED_INIT;

    struct curl_slist* hs = nullptr;
    for (auto& it : headers) hs = curl_slist_append(hs, it.c_str());

    curl_easy_setopt(h, CURLOPT_URL, url.c_str());
    curl_easy_setopt(h, CURLOPT_CUSTOMREQUEST, verb.c_str());
    curl_easy_setopt(h, CURLOPT_HTTPHEADER, hs);
    curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, write_cb);
    curl_easy_setopt(h, CURLOPT_WRITEDATA, &out);
    if (verb == "POST" || verb == "PUT")
        curl_easy_setopt(h, CURLOPT_POSTFIELDS, body.c_str());

    if (ignore_cert) {
        curl_easy_setopt(h, CURLOPT_SSL_VERIFYPEER, 0L);
        curl_easy_setopt(h, CURLOPT_SSL_VERIFYHOST, 0L);
    }

    curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
    auto rc = curl_easy_perform(h);
    curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &status);

    curl_slist_free_all(hs);
    curl_easy_cleanup(h);
    return rc;
}

// 便捷包装
#define DECL(NAME, VERB)                                                \
    std::string NAME(const std::string& url, const std::string& body = "",\
                     const std::vector<std::string>& hdr = {}, bool nocert = false)\
    { std::string r; long st; request(url, VERB, body, hdr, nocert, r, st);\
      std::cout << '[' << VERB << "] " << st << '\n'; return r; }

DECL(get_,   "GET")
DECL(del_,   "DELETE")
DECL(post_,  "POST")
DECL(put_,   "PUT")

int main()
{
    curl_global_init(CURL_GLOBAL_DEFAULT);

    std::cout << get_("https://self-signed.badssl.com/", "", {}, true).substr(0, 80) << "...\n";

    std::vector<std::string> h = { "Content-Type: application/json" };
    post_("https://httpbin.org/post", R"({"k":"v"})", h, true);

    curl_global_cleanup();
}

5. 链接错误排查

症状

复制代码
error LNK2019: 无法解析的外部符号 __imp_CertOpenStore
warning LNK4098: 默认库 "LIBCMT" 与其他库的使用冲突

根因

  1. 静态版 libcurl / openssl 依赖 Crypt32.lib / Secur32.lib / Ws2_32.lib ...,未加入链接器。
  2. 你的工程用 /MT 静态 CRT,而 vcpkg 发行包是 /MD 动态 CRT,导致 LIBCMTMSVCRT 冲突。

解决

首选办法------交给 vcpkg & CMake:

复制代码
find_package(CURL CONFIG REQUIRED)
target_link_libraries(<exe> PRIVATE CURL::libcurl)

vcpkg 自动把系统库也传给链接器;且其 triplet 与你的运行库设置匹配,无需手工维护依赖表。

若手动指定库:

复制代码
libcurl.lib;libssl.lib;libcrypto.lib;zlib.lib;
ws2_32.lib;wldap32.lib;crypt32.lib;secur32.lib;normaliz.lib;bcrypt.lib;
advapi32.lib;version.lib

统一运行库:

VS ‣ 项目属性 ‣ C/C++ ‣ 代码生成 ‣ 运行库 → /MD(Debug 选 /MDd)。

保持全部第三方库一致即可消除 LNK4098。


6. CRT 冲突深挖(可选阅读)

  • /MD 链接到 vcruntime140.dll, ucrtbase.dll,执行时动态加载;
  • /MT 把 CRT 代码打包进目标程序。
    二者 ABI 完全一样,但把两套实现塞进同一个进程会让链接器难以解析同名符号,最终触发 LNK4098。
    一条规则:所有目标 + 第三方库保持同一运行库选项。

7. 小结与最佳实践

  1. 使用 vcpkg integrate install + CURL::libcurl,可省去繁琐的手动依赖管理。
  2. HTTPS 后端任选 openssl / schannel,只需更改 vcpkg feature。
  3. 遇到 __imp_Cert* 未解析 ⇒ 缺 Crypt32.lib 等系统库;
    遇到 LNK4098 ⇒ 运行库 /MD vs /MT 不一致。
  4. 示例中的封装函数已支持 GET/POST/PUT/DELETE;
    生产环境请提供 CA 链而非 ignore_cert=true
  5. 建议把 vcpkg triplet 与项目运行库 写进 vcpkg.json + CMakePresets.json 以保持团队一致性。

至此,你就拥有了一套可直接在 Windows 上进行 HTTPS 调用的干净工程,并掌握了排雷思路。祝编码愉快!

附录:

更靠谱的类封装

class CurlEasy {

public:

CurlEasy() {

handle_ = curl_easy_init();

if (!handle_) throw std::runtime_error("curl_easy_init failed");

// 通用选项

curl_easy_setopt(handle_, CURLOPT_FOLLOWLOCATION, 1L);

curl_easy_setopt(handle_, CURLOPT_ACCEPT_ENCODING, ""); // 自动解压 gzip/deflate

curl_easy_setopt(handle_, CURLOPT_NOPROGRESS, 1L);

}

~CurlEasy() { if (handle_) curl_easy_cleanup(handle_); }

// 禁止拷贝,允许移动

CurlEasy(const CurlEasy&) = delete;

CurlEasy& operator=(const CurlEasy&) = delete;

CurlEasy(CurlEasy&&) = default;

CurlEasy& operator=(CurlEasy&&) = default;

struct Response {

long status{};

std::vector<byte> body; // 原始字节,不会被空字符截断

std::string headers; // 原样拼接的请求头,可自行解析

};

Response request(const std::string& url,

const std::string& verb = "GET",

const std::vector<byte>& body = {},

const std::vector<std::string>& hdr = {},

bool ignore_cert = false)

{

set_basic(url, verb, body, hdr, ignore_cert);

resp_.body.clear();

resp_.headers.clear();

curl_easy_setopt(handle_, CURLOPT_WRITEDATA, &resp_.body);

curl_easy_setopt(handle_, CURLOPT_WRITEFUNCTION, &CurlEasy::write_body);

curl_easy_setopt(handle_, CURLOPT_HEADERDATA, &resp_.headers);

curl_easy_setopt(handle_, CURLOPT_HEADERFUNCTION, &CurlEasy::write_header);

auto code = curl_easy_perform(handle_);

if (code != CURLE_OK)

throw std::runtime_error(curl_easy_strerror(code));

curl_easy_getinfo(handle_, CURLINFO_RESPONSE_CODE, &resp_.status);

return resp_;

}

private:

static size_t write_body(char* p, size_t sz, size_t nm, void* ud) {

auto* vec = static_cast<std::vector<byte>*>(ud);

vec->insert(vec->end(),

reinterpret_cast<byte*>(p),

reinterpret_cast<byte*>(p) + sz * nm);

return sz * nm;

}

static size_t write_header(char* p, size_t sz, size_t nm, void* ud) {

auto* s = static_cast<std::string*>(ud);

s->append(p, sz * nm);

return sz * nm;

}

void set_basic(const std::string& url, const std::string& verb,

const std::vector<byte>& body,

const std::vector<std::string>& hdr,

bool ignore_cert)

{

curl_easy_setopt(handle_, CURLOPT_URL, url.c_str());

if (verb == "GET")

curl_easy_setopt(handle_, CURLOPT_HTTPGET, 1L);

else if (verb == "POST" || verb == "PUT") {

curl_easy_setopt(handle_, CURLOPT_CUSTOMREQUEST, verb.c_str());

curl_easy_setopt(handle_, CURLOPT_POSTFIELDS, body.data());

curl_easy_setopt(handle_, CURLOPT_POSTFIELDSIZE, body.size());

}

else

curl_easy_setopt(handle_, CURLOPT_CUSTOMREQUEST, verb.c_str());

// 头

struct curl_slist* list = nullptr;

for (auto& h : hdr) list = curl_slist_append(list, h.c_str());

curl_easy_setopt(handle_, CURLOPT_HTTPHEADER, list);

// 证书

if (ignore_cert) {

curl_easy_setopt(handle_, CURLOPT_SSL_VERIFYPEER, 0L);

curl_easy_setopt(handle_, CURLOPT_SSL_VERIFYHOST, 0L);

}

}

CURL* handle_{};

Response resp_{};

};

int main() { curl_global_init(CURL_GLOBAL_DEFAULT); try { CurlEasy curl; auto res = curl.request("https://httpbin.org/gzip"); // gzip 压缩 JSON std::cout << "HTTP " << res.status << " , body bytes = " << res.body.size() << '\n'; // 若确定是文本,可安全构造字符串 std::string text(reinterpret_cast<char*>(res.body.data()), res.body.size()); std::cout << text.substr(0, 120) << "...\n"; } catch (const std::exception& e) { std::cerr << "ERROR: " << e.what() << '\n'; } curl_global_cleanup(); }

相关推荐
紫火桑葚4 个月前
windows openssl编译x64版libssl.lib,编译x64版本libcurl.lib,支持https,vs2015编译器
windows·网络协议·https·静态库·openssl·libcurl
arbboter5 个月前
libcurl.net入门使用
c#·.net·curl·webapi·libcurl·libcurl.net
qq762118228 个月前
libcurl8.9.1 上传mp4文件
c++·libcurl
咖喱年糕1 年前
【智能家居】七、人脸识别 & 翔云平台编程使用(编译openSSL支持libcurl的https访问、安装SSL依赖库openSSL)
https·智能家居·bmp·base64·openssl·libcurl·rgb
uxlike2 年前
aarch64-linux交叉编译libcurl带zlib和openssl
linux·cmake·openssl·交叉编译·libcurl·aarch64-linux·zlib