使用 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 PROPERTIESCXX_STANDARD 17CXX_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(); }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/76377.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ISO26262-浅谈用例导出方法和测试方法

目录 1 摘要2 测试方法3 测试用例导出方法4 测试方法与用例导出方法的差异和联系5 结论 1 摘要 ISO26262定义了测试方法和用例导出方法&#xff0c;共同保证产品的开发质量。但在刚开始学习ISO26262的时候&#xff0c;又不是非常清晰地理解它俩的区别和联系。本文主要对它俩的…

RoBoflow数据集的介绍

https://public.roboflow.com/object-detection&#xff08;该数据集的网址&#xff09; 可以看到一些基本情况 如果我们想要下载&#xff0c;直接点击 点击图像可以看到一些基本情况 可以点击红色箭头所指&#xff0c;右边是可供选择的一些yolo模型的格式 如果你想下载…

基于CFSSL构建高可用ETCD集群全指南(含TLS证书管理)

基于CFSSL构建高可用ETCD集群全指南&#xff08;含TLS证书管理&#xff09; 摘要&#xff1a;本文深入讲解使用CFSSL工具签发TLS证书&#xff0c;并部署生产级高可用ETCD集群的完整流程。涵盖证书全生命周期管理、集群配置优化及安全加固方案&#xff0c;适用于Kubernetes、分…

【设计模式】适配器模式:让不兼容的接口和谐共处

引言 在软件开发中&#xff0c;我们经常会遇到这样的情况&#xff1a;两个已经存在的接口无法直接协同工作&#xff0c;但我们又希望它们能够无缝对接。这时&#xff0c;适配器模式就派上用场了。适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&…

doris/clickhouse常用sql

一、doris常用SQL 1、doris统计数据库的总大小&#xff08;单位&#xff1a;MB&#xff09; SELECT table_schema AS database_name,ROUND(SUM(data_length) / 1024 / 1024, 2) AS database_size_MB FROM information_schema.tables WHERE table_schema NOT IN (information…

软件架构分层策略对比及Go项目实践

一、水平分层 vs 功能划分 vs 组件划分 维度水平分层功能划分组件划分核心思想按垂直层次划分职责&#xff08;如表示层、业务层、数据层&#xff09;按业务功能模块划分&#xff08;如用户管理、订单服务、支付模块&#xff09;按技术或业务能力划分独立组件&#xff08;如数…

Linux进程地址空间、写时拷贝

1.进程地址空间 感知进程地址空间 C/C有内存的概念&#xff0c;内存空间包括栈、堆、代码段等等&#xff0c;下面是32位下的内存分布图&#xff0c;自底向上(由0x00000000至0xFFFFFFFF); 下面通过程序来验证各个数据在该空间的地址&#xff0c;由此感知整个地址空间的分布情…

python成功解决AttributeError: can‘t set attribute ‘lines‘

文章目录 报错信息与原因分析解决方法示例代码代码解释总结 报错信息与原因分析 在使用 matplotlib绘图时&#xff0c;若尝试使用 ax.lines []来清除图表中的线条&#xff0c;会遇到AttributeError: can’t set attribute错误。这是因为 ax.lines是一个只读属性&#xff0c;不…

从零搭建微服务项目Pro(第6-2章——微服务鉴权模块SpringSecurity+JWT)

前言&#xff1a; 在上一章已经实现了SpringBoot单服务的鉴权&#xff0c;在导入SpringSecurity的相关依赖,以及使用JWT生成的accessToken和refreshToken能够实现不同Controller乃至同一Controller中不同接口的权限单独校验。上一章链接如下&#xff1a; 从零搭建微服务项目Pr…

win安装软件

win安装软件 jdk安装 jdk安装 首先去官网下载适合系统版本的JDK&#xff0c;下载地址&#xff1a; http://www.oracle.com/technetwork/java/javase/downloads/index.html进入下载页面&#xff0c;如下图&#xff1a; 首先选择&#xff1a;Accept License Agreement单选按钮&…

Prompt-Tuning 提示词微调

1. Hard Prompt 定义&#xff1a; Hard prompt 是一种更为具体和明确的提示&#xff0c;要求模型按照给定的信息生成精确的结果&#xff0c;通常用于需要模型提供准确答案的任务. 原理&#xff1a; Prompt Tuning原理如下图所示&#xff1a;冻结主模型全部参数&#xff0c;在…

【Vue生命周期的演变:从Vue 2到Vue 3的深度剖析】

Vue生命周期的演变&#xff1a;从Vue 2到Vue 3的深度剖析 1. 生命周期钩子的概念与意义 Vue框架通过生命周期钩子函数使开发者可以在组件不同阶段执行自定义逻辑。这些钩子函数是Vue组件生命周期中的关键切入点&#xff0c;对于控制组件行为至关重要。 2. Vue 2中的生命周期…

java ai 图像处理

Java AI 图像处理 图像处理是人工智能&#xff08;AI&#xff09;领域中非常重要的一个应用方向。通过使用Java编程语言和相应的库&#xff0c;我们可以实现各种图像处理任务&#xff0c;如图像识别、图像分类、图像分割等。本文将介绍一些常见的图像处理算法&#xff0c;并通过…

从 0~1 保姆级 详细版 PostgreSQL 数据库安装教程

PostgreSQL数据库安装 PostgreSQL官网 【PostgreSQL官网】 | 【PostgreSQL安装官网_Windows】 安装步骤 step1&#xff1a; 选择与电脑相对应的PostgreSQL版本进行下载。 step2&#xff1a; 双击打开刚才下载好的文件。 step3&#xff1a; 在弹出的setup窗口中点击 …

Keil MDK中禁用半主机(No Semihosting)

在 ARM 编译器&#xff08;如 Keil MDK&#xff09; 中禁用半主机&#xff08;Semihosting&#xff09;并实现标准库的基本功能&#xff0c;需要以下步骤&#xff1a; 1. 禁用半主机 #pragma import(__use_no_semihosting) // 禁用半主机模式作用&#xff1a;防止标准库函数&…

github | 仓库权限管理 | 开权限

省流版总结&#xff1a; github 给别人开权限&#xff1a;仓库 -> Setting -> Cllaborate -> Add people GitHub中 将公开仓库改为私有&#xff1a;仓库 -> Setting -> Danger Zone&#xff08;危险区&#xff09; ->Change repository visibility( 更改仓…

快速部署大模型 Openwebui + Ollama + deepSeek-R1模型

背景 本文主要快速部署一个带有web可交互界面的大模型的应用&#xff0c;主要用于开发测试节点&#xff0c;其中涉及到的三个组件为 open-webui Ollama deepSeek开放平台 首先 Ollama 是一个开源的本地化大模型部署工具,提供与OpenAI兼容的Api接口&#xff0c;可以快速的运…

极狐GitLab 项目导入导出设置介绍?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 导入导出设置 (BASIC SELF) 导入和导出相关功能的设置。 配置允许的导入源 在从其他系统导入项目之前&#xff0c;必须为该…

信奥还能考吗?未来三年科技特长生政策变化

近年来&#xff0c;科技特长生已成为名校录取的“黄金敲门砖”。 从CSP-J/S到NOI&#xff0c;编程竞赛成绩直接关联升学优势。 未来三年&#xff0c;政策将如何调整&#xff1f;家长该如何提前布局&#xff1f; 一、科技特长生政策趋势&#xff1a;2025-2027关键变化 1. 竞…

AI测试用例生成平台

AI测试用例生成平台 项目背景技术栈业务描述项目展示项目重难点 项目背景 针对传统接口测试用例设计高度依赖人工经验、重复工作量大、覆盖场景有限等行业痛点&#xff0c;基于大语言模型技术实现接口测试用例智能生成系统。 技术栈 LangChain框架GLM-4模型Prompt Engineeri…