尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

libcurl Share 接口实战:在多个 easy handle 之间共享 Cookie、DNS、SSL 会话与连接缓存

发布时间:2026/9/11 22:50:28

资讯中心
01
ARTICLE

libcurl Share 接口实战:在多个 easy handle 之间共享 Cookie、DNS、SSL 会话与连接缓存

libcurl Share 接口实战:在多个 easy handle 之间共享 Cookie、DNS、SSL 会话与连接缓存
libcurl Share 接口实战在多个 easy handle 之间共享 Cookie、DNS、SSL 会话与连接缓存【免费下载链接】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 仓库中的 libcurl-share(3) 手册 展开系统讲解 libcurl 共享接口share interface的设计目标、share 对象的完整生命周期、六类可共享数据Cookie、DNS 缓存、SSL 会话、连接缓存、PSL、HSTS的配置方法以及多线程场景下互斥锁回调的编写规范。读完本文你将掌握如何用curl_share_*系列 API 让一批 easy handle 共用同一份数据底座从而显著降低重复建连、重复握手和重复解析带来的开销。一、share 接口解决什么问题在 libcurl 的编程模型中每个CURL *easy handle默认都持有自己私有的 Cookie 数据库、DNS 缓存、TLS 会话缓存与连接缓存。当你的程序需要频繁向同一批主机发起大量小请求时每个独立 handle 都会各自重新做 DNS 解析、重新建立 TCP/TLS 连接、重新走一次完整的握手流程造成明显的重复开销。share 接口正是为此而生。按 libcurl-share(3) 的 OBJECTIVES 一节所述The share interface was added to enable sharing of data between curl handles.其核心场景是一套数据多次传输ONE SET OF DATA - MANY TRANSFERS多个 easy handle 可以读写同一个Cookie 数据库、DNS 缓存、TLS 会话缓存和连接缓存每一个单独传输都能直接受益于其它传输产生的数据更新——例如第一个 handle 完成的 TLS 握手其会话票据可以被后续 handle 直接复用。二、share 对象生命周期init → setopt → cleanupshare 接口的全部函数都以curl_share前缀命名见 libcurl-share(3) 的 DESCRIPTION共三个公开 API函数职责详细手册curl_share_init()创建并返回一个 share 句柄CURLSH *curl_share_init(3)curl_share_setopt()设置共享的数据类型、锁回调、用户数据curl_share_setopt(3)curl_share_cleanup()销毁 share 对象并释放其持有的所有缓存curl_share_cleanup(3)2.1 创建curl_share_init()#include curl/curl.h CURLSH *curl_share_init();该函数返回一个指向CURLSH句柄的指针作为其它所有 share 函数的入参文档中也常称其为 share handle。调用成功返回非 NULL 指针返回 NULL 表示出错如内存不足share 对象未被创建。从源码看lib/curl_share.c 中curl_share_init()会分配并清零struct Curl_share设置 magic 标记CURL_GOOD_SHARE将CURL_LOCK_DATA_SHARE位写入specifier位图内部用于对 share 自身状态的锁定初始化 DNS 缓存Curl_dnscache_init(share-dnscache, 23)创建一个内部的admin easy handle 用于管理共享连接缓存share-admin curl_easy_init()。文档明确要求curl_share_init()的每一次调用必须在全部使用该 share 的操作结束后配对调用一次curl_share_cleanup()。2.2 配置curl_share_setopt()CURLSHcode curl_share_setopt(CURLSH *share, CURLSHoption option, parameter);第二个参数option是CURLSHoption枚举可用的选项定义在 include/curl/curl.htypedef enum { CURLSHOPT_NONE, /* do not use */ CURLSHOPT_SHARE, /* specify a data type to share */ CURLSHOPT_UNSHARE, /* specify which data type to stop sharing */ CURLSHOPT_LOCKFUNC, /* pass in a curl_lock_function pointer */ CURLSHOPT_UNLOCKFUNC, /* pass in a curl_unlock_function pointer */ CURLSHOPT_USERDATA, /* pass in a user data pointer used in the lock/unlock callback functions */ CURLSHOPT_LAST /* never use */ } CURLSHoption;CURLSHOPT_SHARE向 share 对象注册一类要共享的数据类型见下一节。CURLSHOPT_UNSHARE让 share 对象停止共享某一类数据。CURLSHOPT_LOCKFUNC/CURLSHOPT_UNLOCKFUNC设置互斥锁回调多线程必需。CURLSHOPT_USERDATA设置一个私有指针原样传递给锁/解锁回调libcurl 自身不使用它。一个重要约束在 CURLSHOPT_SHARE(3) 与 CURLSHOPT_UNSHARE(3) 中均有明确警告不要在 share 对象正在被使用时增删数据类型只能在两次传输之间between transfers进行。对应源码实现中curl_share_setopt()首先检查share_in_use()——如果已有 easy handle 通过引用计数持有该 shareref_count 1直接返回CURLSHE_IN_USE见 lib/curl_share.c。2.3 销毁curl_share_cleanup()CURLSHcode curl_share_cleanup(CURLSH *share_handle);删除 share 对象调用后该句柄不可再使用如果该 share 仍被任何 easy handle 使用调用会失败返回CURLSHE_IN_USE对象不会被删除传入 NULL 时函数立即返回不做任何操作函数返回后再使用该 share 句柄属于非法行为未定义行为。源码层面curl_share_cleanup()会检查share_in_use()然后调用share_unlink()递减引用计数当引用计数归零时才真正执行share_destroy()见 lib/curl_share.c。share_destroy()会依次销毁连接池Curl_cpool_destroy、DNS 缓存Curl_dnscache_destroy、Cookie 数据库Curl_cookie_cleanup、HSTS 缓存Curl_hsts_cleanup、SSL 会话缓存Curl_ssl_scache_destroy与 PSL 数据Curl_psl_destroy最后释放内部 admin handle见 lib/curl_share.c。多线程销毁特别提醒curl_share_cleanup(3) 原文强调当 share 在多个线程中被使用时销毁动作只能发生在其它所有线程都已停止使用它之后。libcurl 只能统计有多少个 easy handle 正在引用该 share无法感知你的应用还持有多少个指向该 share 的指针——所以销毁时机的最终责任在应用代码。三、可共享的六类数据CURL_LOCK_DATA_*CURLSHOPT_SHARE的type参数取值为curl_lock_data枚举完整定义在 include/curl/curl.htypedef enum { CURL_LOCK_DATA_NONE 0, /* CURL_LOCK_DATA_SHARE is used internally to say that the locking is made * to change the internal state of the share itself. */ CURL_LOCK_DATA_SHARE, CURL_LOCK_DATA_COOKIE, CURL_LOCK_DATA_DNS, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_DATA_CONNECT, CURL_LOCK_DATA_PSL, CURL_LOCK_DATA_HSTS, CURL_LOCK_DATA_LAST } curl_lock_data;其中CURL_LOCK_DATA_NONE、CURL_LOCK_DATA_SHARE为内部保留值应用可配置的六类数据及其语义如下类型共享内容版本线程支持CURL_LOCK_DATA_COOKIECookie 数据库7.10.3不支持多线程并发共享CURL_LOCK_DATA_DNS解析后的 DNS 主机缓存7.10.3需互斥锁回调CURL_LOCK_DATA_SSL_SESSIONTLS 会话减少重复握手7.10.3需互斥锁回调CURL_LOCK_DATA_CONNECT连接缓存连接池7.10.3不支持多线程并发共享CURL_LOCK_DATA_PSLPublic Suffix List公共后缀列表7.61.0需互斥锁回调CURL_LOCK_DATA_HSTS内存中的 HSTS 缓存7.88.0不支持多线程并发共享3.1 CURL_LOCK_DATA_COOKIE共享 Cookie 数据库所有绑定该 share 的 easy handle 读写同一份Cookie 数据库。需要注意两点见 CURLSHOPT_SHARE(3)共享 Cookie不会自动激活 easy handle 的 Cookie 引擎你仍需单独通过CURLOPT_COOKIEFILE等选项启用官方明确不支持在多个并发线程间共享 Cookie。一个容易忽略的行为来自 CURLOPT_SHARE(3)当你把一个共享了 Cookie 的 share 绑定到 easy handle 时该 handle 的 Cookie 引擎会被自动启用data-cookies share-cookies反过来当你解绑或换绑到不共享 Cookie 的 share时Cookie 引擎会被自动禁用。对应实现见 lib/curl_share.c 的Curl_share_easy_link()。3.2 CURL_LOCK_DATA_DNS共享 DNS 缓存缓存的主机解析结果在所有绑定该 share 的 easy handle 间共享。文档特别提示当使用 multi 接口时同一 multi handle 下的所有 easy handle 默认就共享 DNS 缓存无需此选项。3.3 CURL_LOCK_DATA_SSL_SESSION共享 TLS 会话SSL 会话在所有绑定该 share 的 easy handle 间共享重连同一服务器时可显著缩短 TLS 握手耗时。同样地multi 接口下同一 multi handle 的 easy handle 默认共享 SSL 会话缓存。从实现看共享的 SSL 会话缓存由Curl_ssl_scache_create(25, 2, ...)创建25 个会话槽位的容量限制见 lib/curl_share.c。3.4 CURL_LOCK_DATA_CONNECT共享连接缓存将连接缓存放入 share 对象让所有绑定它的 easy handle 共享连接池——这是提升重复请求性能最显著的一类共享。文档还给出了三条额外说明不支持多线程间共享连接HTTP/2 与 HTTP/3 多路复用只有当现有连接由同一个 multi 或 easy handle持有时才会把新传输附加到该连接上libcurl 不支持用共享连接在不同线程间做多路复用流连接数量限制CURLMOPT_MAX_HOST_CONNECTIONS与CURLMOPT_MAX_TOTAL_CONNECTIONS对使用共享连接缓存的传输同样生效每个传输会把它所在 multi handle 的限制施加到共享缓存上详见 CURLMOPT_MAX_HOST_CONNECTIONS(3) 与 CURLMOPT_MAX_TOTAL_CONNECTIONS(3)。实现上CURL_LOCK_DATA_CONNECT被设置为可以重复设置It is safe to set this option several times on a share内部通过Curl_cpool_init(share-cpool, share, 103)初始化连接池见 lib/curl_share.c。3.5 CURL_LOCK_DATA_PSL共享 Public Suffix Listshare 对象中保存的 PSL 数据对所有绑定的 easy handle 可用。由于 PSL 会周期性刷新共享它可避免在过多不同上下文中各自维护更新。同样multi 接口下同一 multi handle 默认共享 PSL。该类型要求 libcurl 以USE_LIBPSL编译否则curl_share_setopt()返回CURLSHE_NOT_BUILT_IN见 lib/curl_share.c。3.6 CURL_LOCK_DATA_HSTS共享 HSTS 缓存共享内存中的 HSTSHTTP Strict Transport Security缓存不支持多线程并发共享。该类型同样有编译开关约束libcurl 若以CURL_DISABLE_HSTS构建则返回CURLSHE_NOT_BUILT_IN。3.7 停止共享CURLSHOPT_UNSHARE与CURLSHOPT_SHARE一一对应CURLSHOPT_UNSHARE用于让 share 对象停止共享某类数据例如sh curl_share_setopt(share, CURLSHOPT_UNSHARE, CURL_LOCK_DATA_COOKIE);可以多次调用以移除多类数据之后仍可用CURLSHOPT_SHARE重新加回。同样只能在两次传输之间操作不能在使用中移除。四、把 easy handle 绑定到 shareCURLOPT_SHARE创建并配置好 share 对象后通过curl_easy_setopt的CURLOPT_SHARE选项把它绑定到任意数量的 easy handle 上CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SHARE, CURLSH *share);关键语义见 CURLOPT_SHARE(3)绑定的 share 必须由curl_share_init()创建设置后该 handle 对share 已声明共享的那部分数据改为使用共享副本其余未共享的数据仍按常规方式独立管理Data that the share object is not set to share is dealt with the usual way, as if no share was used默认值为 NULL不共享想解除绑定把CURLOPT_SHARE设为 NULL 即可。警告在传输进行中把 share 设置为 NULL 是不被推荐的可能导致未定义行为如果多个线程同时使用这些 handle则必须给 share 配置锁回调见下一节。从源码看绑定动作最终走到 lib/curl_share.c 的Curl_share_easy_link()递增引用计数share_ref_inc、建立data-share share关联并把 Cookie/HSTS/PSL 指针从 share 复制到 handle 上解绑对应Curl_share_easy_unlink()lib/curl_share.c若共享了连接缓存还会先Curl_detach_connection()摘除当前连接。五、多线程使用LOCKFUNC / UNLOCKFUNC 互斥锁回调libcurl内部没有线程同步机制。因此只要 share 会被多线程并发访问就必须由应用提供锁/解锁回调通过curl_share_setopt()注册。回调函数原型定义在 include/curl/curl.htypedef void (*curl_lock_function)(CURL *handle, curl_lock_data data, curl_lock_access locktype, void *userptr); typedef void (*curl_unlock_function)(CURL *handle, curl_lock_data data, void *userptr);参数含义见 CURLSHOPT_LOCKFUNC(3) 与 CURLSHOPT_UNLOCKFUNC(3)handle当前正在使用该 share 的 easy handledatalibcurl 想锁/解锁的数据类型curl_lock_data建议对每种数据类型使用不同的锁避免不必要的串行化locktype仅 lock 回调访问类型取值为CURL_LOCK_ACCESS_SHARED读或CURL_LOCK_ACCESS_SINGLE写枚举定义见 include/curl/curl.huserptr通过CURLSHOPT_USERDATA设置的私有指针libcurl 原样透传、绝不使用。设置示例extern void mutex_lock(CURL *handle, curl_lock_data data, curl_lock_access access, void *clientp); sh curl_share_setopt(share, CURLSHOPT_LOCKFUNC, mutex_lock); sh curl_share_setopt(share, CURLSHOPT_UNLOCKFUNC, mutex_unlock); sh curl_share_setopt(share, CURLSHOPT_USERDATA, private_stuff);内部实现中共享数据的每次读写都会经由 lib/curl_share.c 的Curl_share_lock_share()/Curl_share_unlock_share()转发到应用回调只有当该数据类型确实被共享specifier位图中对应位为 1时才调用锁回调未共享的类型则直接返回CURLSHE_OK装作加锁成功。特别注意即便配了锁回调以下三类数据官方仍明确不支持多线程并发共享CURL_LOCK_DATA_COOKIE、CURL_LOCK_DATA_CONNECT、CURL_LOCK_DATA_HSTS。六、完整实战示例跨 handle 共享连接缓存仓库的 docs/examples/shared-connection-cache.c 提供了一个可直接编译运行的完整示例循环中反复创建并销毁 easy handle但因为连接池在 share 对象里连接得以跨 handle 复用#include stdio.h #include curl/curl.h static void my_lock(CURL *curl, curl_lock_data data, curl_lock_access laccess, void *useptr) { (void)curl; (void)data; (void)laccess; (void)useptr; fprintf(stderr, - Mutex lock\n); } static void my_unlock(CURL *curl, curl_lock_data data, void *useptr) { (void)curl; (void)data; (void)useptr; fprintf(stderr, - Mutex unlock\n); } int main(void) { CURLSH *share; int i; CURLcode result curl_global_init(CURL_GLOBAL_ALL); if(result ! CURLE_OK) return (int)result; share curl_share_init(); curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); curl_share_setopt(share, CURLSHOPT_LOCKFUNC, my_lock); curl_share_setopt(share, CURLSHOPT_UNLOCKFUNC, my_unlock); /* 每轮都新建并销毁 easy handle但连接池在 share 对象中连接仍被复用 */ for(i 0; i 3; i) { CURL *curl curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, https://curl.se/); curl_easy_setopt(curl, CURLOPT_SHARE, share); /* 使用共享对象 */ result curl_easy_perform(curl); if(result ! CURLE_OK) fprintf(stderr, curl_easy_perform() failed: %s\n, curl_easy_strerror(result)); curl_easy_cleanup(curl); } } curl_share_cleanup(share); curl_global_cleanup(); return (int)result; }代码骨架中同时展示了CURLSHOPT_LOCKFUNC与CURLSHOPT_UNLOCKFUNC的注册方式本例为单线程锁回调仅打印日志多线程下请替换为真实的互斥量实现。再结合 CURLOPT_SHARE(3) 的示例两个 handle 共享 Cookie 的典型写法CURLSH *shobject curl_share_init(); curl_share_setopt(shobject, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); /* 第一个 handle登录/首次请求写入共享 Cookie 库 */ curl_easy_setopt(curl, CURLOPT_URL, https://example.com/); curl_easy_setopt(curl, CURLOPT_COOKIEFILE, ); /* 启用 Cookie 引擎 */ curl_easy_setopt(curl, CURLOPT_SHARE, shobject); curl_easy_perform(curl); curl_easy_cleanup(curl); /* 第二个 handle直接复用第一个 handle 写入的 Cookie */ curl_easy_setopt(curl2, CURLOPT_URL, https://example.com/second); curl_easy_setopt(curl2, CURLOPT_COOKIEFILE, ); curl_easy_setopt(curl2, CURLOPT_SHARE, shobject); curl_easy_perform(curl2); curl_easy_cleanup(curl2); curl_share_cleanup(shobject);七、错误处理CURLSHcode 返回值curl_share_init()之外的两个函数都返回CURLSHcode枚举定义见 include/curl/curl.h返回值值含义CURLSHE_OK0操作成功CURLSHE_BAD_OPTION1选项或数据类型无效CURLSHE_IN_USE2share 仍被使用中无法设置选项或销毁CURLSHE_INVALID3share 句柄非法CURLSHE_NOMEM4内存不足CURLSHE_NOT_BUILT_IN5功能未编入当前 libcurl 构建如禁用 Cookie/HSTS/SSL 或未启用 libpslCURLSHE_LAST-哨兵值不要使用完整的错误码说明见 libcurl-errors(3)。CURLSHE_NOT_BUILT_IN尤其值得注意它直接反映了 lib/curl_share.c 中按编译宏CURL_DISABLE_HTTP、CURL_DISABLE_COOKIES、CURL_DISABLE_HSTS、USE_SSL、USE_LIBPSL逐个校验共享类型的分支逻辑——即使 API 存在某些共享能力也取决于你的构建配置。八、使用约束与注意事项汇总综合 libcurl-share(3) 及其关联手册整理出以下必须遵守的约束生命周期配对curl_share_init()必须配对curl_share_cleanup()且销毁前必须确认没有任何 easy handle 仍在使用该 share否则返回CURLSHE_IN_USE。配置时机CURLSHOPT_SHARE/CURLSHOPT_UNSHARE只能在传输之间调用share 使用中设置会返回CURLSHE_IN_USE。多线程三必须多线程共享时必须设置CURLSHOPT_LOCKFUNCCURLSHOPT_UNLOCKFUNC推荐对每种数据类型使用独立锁CURLSHOPT_USERDATA用来向回调传递你的锁对象或上下文。三类数据禁多线程Cookie、连接缓存、HSTS 官方不支持跨线程并发共享HTTP/2 与 HTTP/3 的多路复用流也不支持跨线程共享连接。解绑时机不要在传输进行中把CURLOPT_SHARE设为 NULL属于未定义行为。multi 接口的默认行为同一 multi handle 下的 easy handle 默认共享 DNS 缓存、SSL 会话缓存、连接缓存与 PSL无需显式使用 share 接口share 接口主要价值在于让不同multi或不同时刻创建的 easy handle 之间共享数据。更宏观的多线程注意事项可进一步参阅 libcurl-thread(3)share 接口与 easy 接口、multi 接口的配合关系见 libcurl-easy(3) 与 libcurl-multi(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),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。