1. 为什么需要自行编译 curl 8.15.0
在开发过程中,我们经常需要使用 curl 这个强大的网络传输工具库。虽然官方提供了源码,但直接使用预编译版本可以节省大量配置时间。最近我在一个需要 HTTPS 通信的项目中就遇到了这个问题——网上能找到的预编译版本要么太老(很多还停留在 7.x 版本),要么只有源码需要自己编译。
经过几天的搜索无果后,我决定自己动手编译 curl 8.15.0 版本。这个版本有几个重要优势:
- 修复了多个安全漏洞,特别是与 TLS 握手相关的几个关键问题
- 改进了 HTTP/2 的支持
- 可能是最后一个使用传统 autotools 构建系统的版本(后续版本可能会改用 CMake)
提示:如果你也遇到类似情况,建议直接使用官方源码编译,这样能确保获得最新安全补丁,同时避免第三方编译版本可能引入的兼容性问题。
2. 编译环境准备
2.1 硬件和软件要求
在开始编译前,需要准备以下环境:
- Windows 10/11 64位系统
- Visual Studio 2026(社区版或专业版均可)
- Git for Windows(用于获取源码)
- NASM 汇编器(用于编译某些加密组件)
- Perl(用于生成某些头文件)
我使用的是以下具体版本:
- Visual Studio 2026 17.6.5 社区版
- Git 2.42.0.windows.2
- NASM 2.16.01
- Strawberry Perl 5.32.1.1
2.2 源码获取与验证
从官方仓库获取源码:
bash复制git clone https://github.com/curl/curl.git
cd curl
git checkout curl-8_15_0
获取源码后,强烈建议验证其完整性:
bash复制# 验证签名
gpg --verify curl-8.15.0.tar.gz.asc curl-8.15.0.tar.gz
# 计算SHA256校验和
certutil -hashfile curl-8.15.0.tar.gz SHA256
3. 详细编译步骤
3.1 依赖项准备
curl 编译需要几个关键依赖项:
- OpenSSL(提供 TLS 支持)
- zlib(用于压缩)
- libssh2(用于 SCP/SFTP)
我选择使用 vcpkg 来管理这些依赖:
bash复制vcpkg install openssl:x64-windows openssl:x86-windows
vcpkg install zlib:x64-windows zlib:x86-windows
vcpkg install libssh2:x64-windows libssh2:x86-windows
3.2 编译配置
在开始菜单中找到 "x64 Native Tools Command Prompt for VS2026",然后执行:
bash复制# 进入curl源码目录
cd curl
# 生成Makefile
buildconf
configure --prefix=C:/curl-8.15.0-x64 --with-ssl=C:/vcpkg/installed/x64-windows --with-zlib=C:/vcpkg/installed/x64-windows --with-libssh2=C:/vcpkg/installed/x64-windows --enable-http --enable-ftp --enable-file --enable-ldap --enable-rtsp --enable-proxy --enable-dict --enable-telnet --enable-tftp --enable-pop3 --enable-imap --enable-smb --enable-smtp --enable-gopher --enable-mqtt --disable-manual --disable-ldap --disable-ldaps --disable-rtsp --without-librtmp --without-libidn2 --without-nghttp2 --without-brotli --without-zstd --without-winidn --without-libpsl
3.3 实际编译过程
执行编译和安装:
bash复制nmake
nmake install
编译完成后,你会在 C:/curl-8.15.0-x64 目录下找到编译好的文件:
- bin/:包含 curl.exe 可执行文件
- include/:头文件
- lib/:静态库和动态库
4. VS2026 项目配置详解
4.1 文件组织结构
建议采用以下目录结构:
code复制C:\curlLib
├── include
│ └── curl
│ ├── curl.h
│ ├── easy.h
│ └── ... (其他头文件)
├── lib
│ ├── x64
│ │ ├── libcurl.lib
│ │ └── libcurl.dll
│ └── x86
│ ├── libcurl.lib
│ └── libcurl.dll
└── bin
├── x64
│ └── curl.exe
└── x86
└── curl.exe
4.2 项目属性配置
-
平台工具集选择:
- 确保选择 "Visual Studio 2026 (v143)"
- 字符集使用 "使用 Unicode 字符集"
-
C/C++ 常规设置:
- 附加包含目录:$(SolutionDir)..\curlLib\include
- 预处理器定义:CURL_STATICLIB
-
链接器设置:
- 附加库目录:$(SolutionDir)..\curlLib\lib$(Platform)
- 附加依赖项:libcurl.lib;Ws2_32.lib;Crypt32.lib;Wldap32.lib;Normaliz.lib
-
生成后事件:
添加复制 DLL 到输出目录的命令:bash复制xcopy /Y "$(SolutionDir)..\curlLib\lib\$(Platform)\libcurl.dll" "$(OutDir)"
5. 常见问题与解决方案
5.1 编译时错误
问题1:缺少 openssl 符号
code复制error LNK2019: unresolved external symbol SSLv23_client_method referenced in function...
解决方案:
在项目属性 → 链接器 → 命令行中添加:
code复制/WHOLEARCHIVE:libcurl.lib
问题2:运行时崩溃
code复制0xC0000005: Access violation reading location...
解决方案:
确保所有模块使用相同的运行时库(/MD 或 /MT),建议统一使用 /MD。
5.2 使用中的问题
问题3:HTTPS 请求失败
code复制SSL certificate problem: unable to get local issuer certificate
解决方案:
设置 CA 证书路径:
cpp复制curl_easy_setopt(curl, CURLOPT_CAINFO, "C:\\curlLib\\bin\\curl-ca-bundle.crt");
问题4:多线程下崩溃
code复制heap corruption detected
解决方案:
确保每个线程使用独立的 CURL* 句柄,或正确使用锁:
cpp复制curl_global_init(CURL_GLOBAL_ALL);
6. 性能优化建议
- 连接复用:
cpp复制// 保持连接活跃
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 120L);
curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 60L);
- DNS 缓存:
cpp复制// 启用内置DNS缓存
curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, 60 * 60);
- 启用 HTTP/2:
cpp复制curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
- 压缩支持:
cpp复制curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "gzip, deflate");
7. 实际应用示例
7.1 简单的 HTTP GET 请求
cpp复制#include <curl/curl.h>
#include <iostream>
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) {
size_t total_size = size * nmemb;
output->append((char*)contents, total_size);
return total_size;
}
int main() {
CURL* curl = curl_easy_init();
if (curl) {
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "MyCurlApp/1.0");
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK) {
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res);
} else {
std::cout << "Response: " << response.substr(0, 100) << "...\n";
}
curl_easy_cleanup(curl);
}
return 0;
}
7.2 文件下载进度显示
cpp复制static int progress_callback(void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
if (dltotal > 0) {
double progress = (double)dlnow / (double)dltotal * 100.0;
printf("\rDownloading: %.2f%%", progress);
fflush(stdout);
}
return 0;
}
// 在easy_setopt中添加
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
8. 高级功能探索
8.1 多接口处理
使用 curl_multi 接口可以同时处理多个传输:
cpp复制CURLM* multi_handle = curl_multi_init();
// 添加多个easy句柄
curl_multi_add_handle(multi_handle, curl1);
curl_multi_add_handle(multi_handle, curl2);
int running_handles = 0;
do {
curl_multi_perform(multi_handle, &running_handles);
curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);
} while (running_handles);
// 清理
curl_multi_remove_handle(multi_handle, curl1);
curl_multi_remove_handle(multi_handle, curl2);
curl_multi_cleanup(multi_handle);
8.2 WebSocket 支持
curl 8.15.0 提供了实验性的 WebSocket 支持:
cpp复制curl_easy_setopt(curl, CURLOPT_WS_OPTIONS, CURLWS_RAW_MODE);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ws_write_callback);
size_t ws_write_callback(char* ptr, size_t size, size_t nmemb, void* userdata) {
// 处理WebSocket帧
return size * nmemb;
}
8.3 HTTP/3 配置
虽然 8.15.0 对 HTTP/3 的支持还在早期阶段,但可以尝试配置:
cpp复制curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3);
curl_easy_setopt(curl, CURLOPT_ALTSVC, "h3=\":443\"; ma=3600");
curl_easy_setopt(curl, CURLOPT_ALTSVC_CTRL, CURLALTSVC_H3);