真要异步必须用libcurl的multi接口:调用curl_multi_init()创建句柄,curl_easy_init()初始化请求后add到multi,设curlopt_nosignal=1l和curlopt_private绑定用户数据,循环调用curl_multi_perform()推进传输,并用curl_multi_wait()等待socket事件避免忙等。

用 libcurl 开启异步请求,但别直接用 curl_easy_perform
同步阻塞调用 curl_easy_perform 会卡住线程,根本不算异步。真要异步,得用 libcurl 的 multi 接口——它不自己开线程,而是让你轮询或配合事件循环驱动多个请求并发跑。
关键不是“多线程”,而是“非阻塞 I/O 复用”。你得自己管理 CURLM * 句柄、设置超时、处理 socket 事件(比如可读/可写)。
-
curl_multi_init()创建 multi 句柄,每个请求用curl_easy_init()初始化后,curl_multi_add_handle()加进去 - 必须设
CURLOPT_NOSIGNAL=1L,否则 DNS 超时可能发信号中断你的主线程 - 别漏掉
CURLOPT_PRIVATE:把用户数据(比如回调用的std::shared_ptr或原始指针)绑到 easy handle 上,curl_multi_info_read返回完成结果时能取回 - 每次循环调用
curl_multi_perform()发起/推进传输,再用curl_multi_wait()等待 socket 事件,避免忙等
如何安全地在回调里访问 C++ 对象
libcurl 的回调函数是 C 风格的,CURLOPT_WRITEFUNCTION 和 CURLOPT_HEADERFUNCTION 的第三个参数是 void *,你传进去的不能是栈变量或临时对象。
最稳妥的是传 std::shared_ptr 的裸指针(比如 ptr.get()),并在回调里用 static_cast 转回;或者更轻量地,传一个指向堆上分配的结构体的指针,里面包含 std::string 成员用于拼接响应体。
- 千万别传
this指针然后在回调里调成员函数——如果对象提前析构,回调触发时就是 UAF - 写入回调返回值必须严格是接收字节数,否则 libcurl 会报
CURLE_WRITE_ERROR - 如果用了
std::string::append,注意它可能抛异常;libcurl 回调里抛异常会导致未定义行为,务必用try/catch(...)吞掉
超时和错误处理不能只靠 CURLOPT_TIMEOUT
CURLOPT_TIMEOUT 只控制整个请求生命周期,对 DNS 解析、TCP 连接、TLS 握手这些中间环节没用。实际中常见 “卡在 connecting” 却不报错。
必须组合使用:CURLOPT_CONNECTTIMEOUT_MS 控制建连超时(毫秒级),CURLOPT_LOW_SPEED_LIMIT + CURLOPT_LOW_SPEED_TIME 防止慢速响应拖死,再加上 multi 接口自身的超时机制——调用 curl_multi_timeout() 获取建议等待毫秒数,作为 curl_multi_wait() 的 timeout 参数。
- multi 接口的错误码从
curl_multi_info_read()返回的CURLMsg中取msg->data.result,不是 easy handle 自己的curl_easy_getinfo(..., CURLINFO_RESPONSE_CODE, ...) -
CURLE_COULDNT_CONNECT和CURLE_OPERATION_TIMEDOUT表现相似,但前者通常发生在 connect 阶段,后者多见于传输中空转 - HTTP 状态码(如 404、502)要等传输结束才能拿到,得在
CURLMSG_DONE事件里用curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &code)
Windows 下 select() 不支持大于 64 个 socket 怎么办
libcurl 默认在 Windows 上用 select() 等待 socket,而 Windows 的 FD_SETSIZE 默认是 64。如果你并发发起超过 64 个请求,curl_multi_wait() 会直接失败并返回 CURLE_BAD_FUNCTION_ARGUMENT。
解决方案只有两个:要么改用 libcurl 的 poll() 后端(需编译时开启 USE_POLL,MSVC 下较麻烦),要么主动禁用 multi 的内部 wait,改用你自己的事件循环(如 IOCP 或 WSAPoll)——把 CURLMOPT_SOCKETFUNCTION 设置为自定义 socket 回调,在那里注册句柄到你的 IO 多路复用器中。
- 用
WSAPoll时注意它不支持监听文件描述符,只适用于 socket;且某些旧 Windows 版本不支持 - 如果选 IOCP,得在
CURLMOPT_SOCKETFUNCTION里调用WSAEventSelect或直接绑定到 completion port,复杂度陡增 - 简单项目建议直接限制并发请求数 ≤60,比折腾底层兼容更省时间
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











