libcurl使用需先调用curl_global_init()一次且仅一次,否则curl_easy_init()返回nullptr;下载须设超时、检查HTTP状态码、二进制模式写文件、正确处理重定向,链接时注意运行时库匹配。

libcurl 初始化失败:curl_global_init() 必须调用且只能调一次
很多初学者一上来就 curl_easy_init(),结果返回 nullptr,却找不到原因。根本问题常是漏掉全局初始化,或在多线程环境下重复调用 curl_global_init() 导致未定义行为。
- 必须在程序启动时(如
main()开头)调用curl_global_init(CURL_GLOBAL_DEFAULT),且仅一次 - Windows 下若用 OpenSSL,还需确保
libeay32.dll和ssleay32.dll(或新版libcrypto-*.dll)在 PATH 或可执行目录中,否则curl_global_init()可能静默失败 - 不建议在 DLL 的
DLL_PROCESS_ATTACH中调用它——多个模块各自初始化会冲突;统一由主程序负责 - 对应地,退出前应调用
curl_global_cleanup(),但实际项目中常省略(进程结束自动释放),不过单元测试里不清理会导致 ASan 报告内存泄漏
下载文件时卡住或超时:默认无超时 + 无进度回调易误判成功
curl_easy_perform() 默认会无限等待服务器响应,尤其遇到 DNS 慢、TCP 连接挂起、或服务器不发响应头时,看起来就像“卡死”。更隐蔽的问题是:即使返回 CURLE_OK,也可能只写入了 0 字节(比如 404 页面被当成文件内容)。
- 务必设置
curl_easy_setopt(handle, CURLOPT_TIMEOUT, 30L)(单位秒),对大文件还可加CURLOPT_CONNECTTIMEOUT - 用
CURLOPT_WRITEFUNCTION自定义写入逻辑,而非依赖CURLOPT_WRITEDATA直接写 FILE* —— 否则无法捕获写入失败(如磁盘满) - 加上
CURLOPT_NOBODY+CURLOPT_HEADERFUNCTION预检 HTTP 状态码,避免下载错误页;或者下载后检查CURLINFO_RESPONSE_CODE - 示例片段:
long http_code = 0;
curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_code);
if (http_code != 200) { /* 处理非200 */ }
二进制文件损坏:文本模式打开文件 + 缺少 CURLOPT_FOLLOWLOCATION 安全限制
用 fopen("out.bin", "w") 写下载内容,在 Windows 上会把 \n 自动转成 \r\n,导致图片、ZIP 等二进制文件损坏。另一个常见坑是重定向处理不当:有些资源返回 302 跳转到 CDN 地址,但默认 CURLOPT_FOLLOWLOCATION 关闭,结果下回来的是 HTML 重定向页面。
- 文件必须以二进制模式打开:
fopen("out.bin", "wb"),哪怕只是临时存个 PDF - 开启重定向需同时设
CURLOPT_FOLLOWLOCATION和CURLOPT_MAXREDIRS(防环形跳转),但注意:libcurl 7.68.0+ 默认禁用libcurl对file://协议的重定向,防止本地路径泄露 - 若服务端用分块传输(chunked encoding),无需额外处理 —— libcurl 自动拼接,但要确保
WRITEFUNCTION正确累计字节数,别依赖ftell()判断大小
Windows 下链接失败:忽略 curl.lib 与运行时库不匹配
编译通过但链接时报一堆 unresolved external symbol curl_*,大概率是用了预编译的 libcurl 库(如 vcpkg 提供的),但它的 curl.lib 是 /MT(静态链接 CRT)编译的,而你的项目设成了 /MD(动态链接 CRT)。
立即学习“C++免费学习笔记(深入)”;
- 检查项目属性 → C/C++ → 代码生成 → 运行时库,必须和你用的 libcurl 一致(vcpkg 默认 /MD,mingw-w64 构建的可能是 /MT)
- 用
dumpbin /all curl.lib | findstr "CRT"查看库依赖的 CRT 类型 - 更稳妥的方式是用 CMake +
find_package(CURL),它会自动适配;手写 VS 工程时,别直接加libcurl.lib到附加依赖项,先确认CURL_STATICLIB宏是否定义(影响导出符号) - MinGW 用户注意:
-lcurl要放在命令行末尾,否则链接器可能忽略未解析符号
实际写下来,最麻烦的从来不是怎么发起请求,而是判断“到底算不算下成功”——状态码、Content-Length、写入字节数、文件哈希,得交叉验证。少盯一个,上线后就可能发现用户下载的 ZIP 打不开。










