ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

curl_multi_add_handle 详解:将 easy handle 加入 multi 会话的正确姿势

curl_multi_add_handle 详解:将 easy handle 加入 multi 会话的正确姿势 curl_multi_add_handle 详解将 easy handle 加入 multi 会话的正确姿势【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl导读curl_multi_add_handle()是 libcurl 多接口multi interface的核心入门口它把单个 easy handle 挂载到由curl_multi_init()创建的 multi 会话上使该句柄进入 libcurl 内部的异步调度体系。在 curl 的 multi 编程模型中几乎所有并发传输多个 HTTP 请求并行、事件驱动的 socket 回调、DoH 内部解析等都以它为起点。读完本文你将掌握该函数的调用签名、加入后的行为约束、与curl_easy_perform()的互斥关系、连接池与 DNS 缓存的共享机制、事件驱动下的定时器联动以及完整的错误码与清理顺序。函数签名与原型curl_multi_add_handle()在头文件 include/curl/curl.h 中声明自libcurl 7.9.6起提供适用于所有协议。#include curl/curl.h CURLMcode curl_multi_add_handle(CURLM *multi_handle, CURL *easy_handle);参数说明multi_handle由curl_multi_init()返回的 multi 会话句柄CURLM *传输完成后应交给curl_multi_cleanup()释放easy_handle由curl_easy_init()创建、并已通过curl_easy_setopt()配置好选项的 easy 句柄CURL *返回值是CURLMcodeCURLM_OK (0)表示成功非零表示出错完整错误码见 libcurl-errors(3)。DESCRIPTION加入 multi 栈后发生了什么行为一easy 句柄进入 multi 独占模式一旦 easy handle 被加入 multi 栈在它仍处于 multi 栈中的期间绝不能对同一个句柄调用curl_easy_perform()。从源码看curl_easy_perform()在 lib/easy.c 中本身就会为单个 easy 句柄内部创建私有 multi 句柄并调用内部Curl_multi_add_handle()若一个 easy 句柄同时被外部 multi 与内部私有 multi 引用会产生状态冲突。只有在通过curl_multi_remove_handle()把该 easy 句柄从 multi 栈移除之后才能再次放心地以 easy 接口curl_easy_perform()使用它。行为二共享 DNS 缓存如果该 easy handle 没有通过CURLOPT_SHARE指定共享缓存那么当curl_multi_add_handle()被调用时它会被设置为使用一个在该 multi 句柄内所有 easy handle 之间共享的 DNS 缓存。这意味着同一 multi 会话中并发的多个 easy 句柄解析同一主机名时可以复用解析结果减少重复 DNS 查询。行为三共享连接缓存与连接复用easy 接口被加入 multi 句柄后它会被设置为使用由 multi 句柄拥有的共享连接缓存connection cache / cpool。反复地移除再加入新的 easy 句柄不会影响连接池中的连接也不影响连接复用能力——已经建立的连接会留在 multi 的连接池里供后续加入的 easy 句柄复用。这一点在 lib/multi.c 的内部实现中通过Curl_cpool_xfer_init(data)以及multi_conn_should_close()lib/multi.c对连接关闭与保留的判定得到印证当传输结束时只要连接仍可复用未被标记关闭、协议支持复用、没有 premature 结束连接会被保留在 cpool 中infof(data, Connection #... left intact, ...)而不是随 easy 句柄一起销毁。行为四事件驱动模式下的定时器联动如果你在 multi 句柄上设置了CURLMOPT_TIMERFUNCTION使用curl_multi_socket_action()及配套函数做事件驱动编程时理应如此那么该回调会在这个函数内部被调用用于请求一个更新后的定时器以便你的主事件循环能及时获得该句柄上的活动并开始处理。源码层面的对应关系是内部实现Curl_multi_add_handle()在完成注册后会调用Curl_update_timer(multi)lib/multi.c并在注释中明确说明“在基于事件的处理中dirty handle 会触发一次 timeout callback 调用”。行为五句柄会一直留在 multi 栈中easy handle 在被curl_multi_remove_handle()移除之前始终保留在 multi 句柄中——即使该句柄上的传输已经完成。也就是说一次传输结束不代表句柄被自动摘除你需要显式地调用移除函数。正确的清理顺序Teardown 顺序文档明确给出了在终止前应当遵循的移除/清理顺序curl_multi_remove_handle()—— 先从 multi 栈中移除 easy 句柄curl_easy_cleanup()—— 再释放 easy 句柄本身curl_multi_cleanup()—— 最后销毁 multi 句柄。先移除、后清理的顺序保证 easy 句柄不再被 multi 会话引用避免在释放句柄时出现悬空引用或双重释放。参考实现中curl_easy_perform()的收尾lib/easy.c也是“先Curl_multi_remove_handle()、再让私有 multi 在curl_multi_cleanup()中释放”的模式可以作为正确用法的内部印证。完整示例添加两个 easy 句柄到 multi 会话int main(void) { /* init a multi stack */ CURLM *multi curl_multi_init(); /* create two easy handles */ CURL *http_handle curl_easy_init(); CURL *http_handle2 curl_easy_init(); /* add individual transfers */ curl_multi_add_handle(multi, http_handle); curl_multi_add_handle(multi, http_handle2); }更完整的实战流程是创建 easy 句柄 →curl_easy_setopt()设置 URL 等选项 →curl_multi_add_handle()加入会话 → 用curl_multi_poll()/curl_multi_perform()驱动传输 → 用curl_multi_info_read()读取每个句柄的完成状态 → 对完成的句柄调用curl_multi_remove_handle()并curl_easy_cleanup()→ 最后curl_multi_cleanup()结束会话。注意同一 easy 句柄不能重复添加见下文错误码CURLM_ADDED_ALREADY且不能同时添加到多个 multi 栈。RETURN VALUE 与错误码该函数返回CURLMcode成功为CURLM_OK (0)非零表示错误。常见错误码及其含义摘自 libcurl-errors.md错误码数值含义CURLM_OK0一切正常CURLM_BAD_HANDLE1传入的不是有效的CURLM句柄CURLM_BAD_EASY_HANDLE2easy 句柄无效/损坏可能根本不是 easy 句柄或该句柄已被本 multi 或其他 multi 使用CURLM_OUT_OF_MEMORY3内存不足文档原话You are doomedCURLM_ADDED_ALREADY7尝试把一个已经加入过 multi 句柄的 easy 句柄再次加入从源码可以印证这些错误的产生路径公开入口curl_multi_add_handle()lib/multi.c会先校验句柄合法性不合法返回CURLM_BAD_EASY_HANDLE再调用内部Curl_multi_add_handle()内部实现lib/multi.c中如果data-multi已被设置句柄已属于某个 multi直接返回CURLM_ADDED_ALREADY注释明确写着“Prevent users from adding same easy handle more than once and prevent adding to more than one multi stack”。此外如果 multi 处于“dead”状态且仍存在存活的传输返回CURLM_ABORTED_BY_CALLBACK句柄表扩容失败时返回CURLM_OUT_OF_MEMORY。内部实现要点源码级入口与校验公开函数curl_multi_add_handle()lib/multi.c通过CURL_MAPI_ENTER/LEAVE保护并发访问并检查GOOD_EASY_HANDLE(data)不通过则返回CURLM_BAD_EASY_HANDLE。重复添加防护内部Curl_multi_add_handle()lib/multi.c先检查data-multi已存在则返回CURLM_ADDED_ALREADY。私有 multi 的回收如果该 easy 句柄此前通过curl_easy_perform()使用过data-multi_easy非空内部会调用curl_multi_cleanup(data-multi_easy)清掉这个私有 multilib/multi.c保证句柄干净地进入外部 multi。注册与状态初始化将 easy 插入 multi 的传输表并分配mid初始化超时multi_timeouts_init把data-multi指回 multi 句柄设置状态机为MSTATE_INIT标记为 dirty 以触发运行最后调用Curl_update_timer()更新事件驱动定时器lib/multi.c。连接池准备Curl_cpool_xfer_init(data)lib/multi.c为这个 easy 句柄准备与 multi 共享的连接池关系这正是文档中“共享连接缓存、连接可复用”的实现基础。DoH 的内部复用libcurl 自身在 DoHDNS over HTTPS解析时也会用内部 multi 添加 DoH 请求句柄lib/vdns/doh.c 调用Curl_multi_add_handle(multi, doh)说明该函数不仅是用户接口也是 libcurl 内部异步架构的基础设施。相关接口curl_multi_init(3) —— 创建 multi 会话curl_multi_remove_handle(3) —— 从 multi 会话移除 easy 句柄curl_multi_cleanup(3) —— 销毁 multi 会话curl_multi_get_handles(3) —— 获取 multi 会话中的句柄列表curl_multi_setopt(3) —— 设置 multi 会话选项如CURLMOPT_TIMERFUNCTIONcurl_multi_socket_action(3) —— 事件驱动模式下驱动传输libcurl-errors(3) —— 完整错误码说明【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表