使用 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.pswp.cn/web/76377.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/76377.shtml
英文地址,請注明出處:http://en.pswp.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…