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

libcurl 共享句柄指南:CURLOPT_SHARE 原理、多句柄数据共享与线程安全实践

发布时间:2026/9/10 13:41:06

资讯中心
01
ARTICLE

libcurl 共享句柄指南:CURLOPT_SHARE 原理、多句柄数据共享与线程安全实践

libcurl 共享句柄指南:CURLOPT_SHARE 原理、多句柄数据共享与线程安全实践
libcurl 共享句柄指南CURLOPT_SHARE 原理、多句柄数据共享与线程安全实践【免费下载链接】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导读CURLOPT_SHARE是 libcurl 中用于把多个 easy handleCURL *绑定到同一个共享句柄CURLSH *的核心选项。通过它应用可以让多个请求共享 DNS 缓存、Cookie、TLS 会话、连接池等数据从而显著减少重复握手与解析开销。读完本文你将掌握CURLOPT_SHARE的完整用法、可共享的数据类型、源码层面的实现机制以及多线程场景下必须配合使用的锁回调的正确姿势。CURLOPT_SHARE 是什么CURLOPT_SHARE用于让某个 easy handle 使用由curl_share_init(3)创建的共享句柄share handle中的数据而不是各自维护一份私有数据。该选项自 libcurl 7.10 起引入适用于所有协议其官方声明位于 docs/libcurl/opts/CURLOPT_SHARE.md。核心语义有三点按需共享共享句柄只会共享通过CURLSHOPT_SHARE显式声明的那部分数据未声明共享的数据仍然由每个 easy handle 以常规方式自行管理。可解除将该选项再次设置为NULL即可让 easy handle 停止使用该共享对象。生命周期风险在传输进行中把共享句柄设置为NULL属于不鼓励行为可能引发未定义行为undefined behavior。API 原型与参数说明#include curl/curl.h CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SHARE, CURLSH *share);参数类型说明handleCURL *由curl_easy_init()创建的 easy handleCURLOPT_SHARE选项标识将 easy handle 与一个共享句柄绑定shareCURLSH *由curl_share_init()返回的共享句柄传NULL表示解除绑定在 include/curl/curl.h 中CURLSH即struct Curl_share的公开别名而 lib/easyoptions.c 把CURLOPT_SHARE归类为指针类选项/* CURLSH * */与CURLOPT_STDERR、CURLOPT_CURLU走同一条setopt_pointers分发路径。默认值为NULL即 easy handle 默认不共享任何数据。可共享的数据类型共享内容由配套的curl_share_setopt(share, CURLSHOPT_SHARE, type)声明type取自 include/curl/curl.h 中的curl_lock_data枚举类型共享内容说明CURL_LOCK_DATA_COOKIECookie 数据多个请求共享 HTTP Cookie 状态依赖 HTTP/Cookie 支持CURL_LOCK_DATA_DNSDNS 缓存共享主机名解析结果减少重复 DNS 查询CURL_LOCK_DATA_SSL_SESSIONTLS/SSL 会话缓存复用 TLS 会话跳过完整握手需编译 SSL 支持CURL_LOCK_DATA_CONNECT连接池connection pool在多个 easy handle 之间复用底层连接是“连接共享”的关键见下文源码分析CURL_LOCK_DATA_PSLPublic Suffix List共享 PSL 数据需编译USE_LIBPSLCURL_LOCK_DATA_HSTSHSTS 缓存共享 HTTP Strict Transport Security 状态需编译 HSTS 支持CURL_LOCK_DATA_SHARE内部枚举用于共享句柄自身的加锁不通过CURLSHOPT_SHARE声明对应实现见 lib/curl_share.ccurl_share_setopt处理CURLSHOPT_SHARE时会根据类型初始化对应的内部数据结构如Curl_cookie_init()创建 Cookie 存储、Curl_ssl_scache_create(25, 2, ...)创建默认容量为 25 条、每个对等体 2 个会话的 TLS 会话缓存、Curl_cpool_init(share-cpool, share, 103)初始化容量 103 的连接池并通过share-specifier位图记录哪些数据被共享。若编译时禁用了对应功能如CURL_DISABLE_COOKIES、CURL_DISABLE_HSTS返回CURLSHE_NOT_BUILT_IN。与之对应的CURLSHOPT_UNSHARE则从共享中移除某类数据lib/curl_share.c例如关闭 Cookie 共享时会调用Curl_cookie_cleanup()释放存储。官方示例两个 easy handle 共享 Cookie以下示例完整来自原文档展示两个 easy handle 通过同一个共享句柄共享 Cookie 的典型流程int main(void) { CURL *curl curl_easy_init(); CURL *curl2 curl_easy_init(); /* a second handle */ if(curl) { CURLcode result; CURLSH *shobject curl_share_init(); curl_share_setopt(shobject, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); curl_easy_setopt(curl, CURLOPT_URL, https://example.com/); curl_easy_setopt(curl, CURLOPT_COOKIEFILE, ); curl_easy_setopt(curl, CURLOPT_SHARE, shobject); result curl_easy_perform(curl); curl_easy_cleanup(curl); /* the second handle shares cookies from the first */ curl_easy_setopt(curl2, CURLOPT_URL, https://example.com/second); curl_easy_setopt(curl2, CURLOPT_COOKIEFILE, ); curl_easy_setopt(curl2, CURLOPT_SHARE, shobject); result curl_easy_perform(curl2); curl_easy_cleanup(curl2); curl_share_cleanup(shobject); } }要点解读curl_share_init()创建共享句柄CURLSHOPT_SHARECURL_LOCK_DATA_COOKIE声明共享 CookieCURLOPT_COOKIEFILE设为空字符串表示开启 Cookie 引擎读取/写入会话 Cookie而不是从文件加载第一个请求在https://example.com/收到的 Cookie 会被写入共享对象第二个请求访问/second时自动携带最后必须curl_share_cleanup(shobject)释放共享句柄。CURLOPT_SHARE可与 CURLOPT_COOKIE 等选项配合具体共享配置包括CURLSHOPT_SHARE/CURLSHOPT_UNSHARE详见 docs/libcurl/opts/CURLSHOPT_SHARE.md。源码实现setopt_share 的绑定与解绑逻辑CURLOPT_SHARE的底层处理函数是setopt_share()lib/setopt.cstatic CURLcode setopt_share(struct Curl_easy *data, struct Curl_share *set) { CURLcode result; if(data-conn) { /* As this handle already has a connection attached, changing share now would be complicated and error-prone */ infof(data, Cannot change share object while in use); result CURLE_BAD_FUNCTION_ARGUMENT; } else { /* disconnect from old share, if any and possible */ result Curl_share_easy_unlink(data); if(!result GOOD_SHARE_HANDLE(set)) /* use new share if it set */ result Curl_share_easy_link(data, set); } return result; }从源码可以确认三个关键实现事实已连接句柄禁止换绑如果 easy handle 当前已挂接连接data-conn非空修改共享对象会返回CURLE_BAD_FUNCTION_ARGUMENT并打印 Cannot change share object while in use 日志——这与原文档“传输进行中置 NULL 可能导致未定义行为”的警告互为印证最佳实践是在curl_easy_perform()之前完成绑定。引用计数管理Curl_share_easy_link()/Curl_share_easy_unlink()通过share_ref_inc()/share_ref_dec()维护共享句柄的引用计数lib/curl_share.c。最后一个引用释放时引用计数归零share_destroy()会依次销毁连接池、DNS 缓存、Cookie、HSTS、SSL 会话缓存、PSL 及内部互斥锁并关闭内部 admin easy handlelib/curl_share.c。解绑时的数据剥离Curl_share_easy_unlink()在解绑时若共享了连接池会调用Curl_detach_connection()分离已有连接并把lastconnect_id重置为 -1若 Cookie/HSTS 与共享对象是同一实例也会置回 NULLlib/curl_share.c。进阶实践共享连接池官方示例仓库中的 docs/examples/shared-connection-cache.c 提供了一个完整可编译的进阶示例共享连接缓存使“每次新建 easy handle、传输完成后立即销毁”的循环仍然能复用 TCP/TLS 连接。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); 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; }这个示例演示了标准的共享句柄生命周期curl_global_init→curl_share_init→ 声明共享类型 → 注册锁回调 → 循环绑定CURLOPT_SHARE执行传输 →curl_share_cleanup→curl_global_cleanup。多线程使用与锁回调原文档强调如果多个 curl handle 在多线程中同时使用同一个共享句柄必须使用共享句柄的锁方法。libcurl 本身不为共享数据加锁锁由应用提供通过以下三个CURLSHOPT_*选项注册选项作用CURLSHOPT_LOCKFUNC设置加锁回调原型void (*curl_lock_function)(CURL *handle, curl_lock_data data, curl_lock_access access, void *userptr)CURLSHOPT_UNLOCKFUNC设置解锁回调原型void (*curl_unlock_function)(CURL *handle, curl_lock_data data, void *userptr)CURLSHOPT_USERDATA传给回调的自定义指针如互斥锁对象curl_lock_data与curl_lock_accessCURL_LOCK_ACCESS_NONE/CURL_LOCK_ACCESS_SHARED/CURL_LOCK_ACCESS_SINGLE同样定义在 include/curl/curl.h。从源码看锁回调只在“该类型确实被共享”且回调已注册时才被调用。以加锁为例lib/curl_share.cCURLSHcode Curl_share_lock_share(struct Curl_share *share, struct Curl_easy *data, curl_lock_data type, curl_lock_access accesstype) { if(!share) return CURLSHE_INVALID; if(share-specifier (unsigned int)(1 type) share-lockfunc) /* only call this if set! */ share-lockfunc(data, type, accesstype, share-clientdata); /* else if we do not share this, pretend successful lock */ return CURLSHE_OK; }即未共享的类型会“假装加锁成功”直接放行这也是CURL_LOCK_DATA_SHARE等内部枚举存在的原因。实践中的典型做法是在锁回调内调用pthread_mutex_lock/pthread_mutex_unlock或 Windows 的EnterCriticalSection/LeaveCriticalSection把CURLSHOPT_USERDATA指向锁对象本身。另外注意curl_share_setopt()在共享对象已被任一 easy handle 使用时引用计数大于 1会返回CURLSHE_IN_USE拒绝修改lib/curl_share.c因此共享类型的配置应在绑定任何句柄之前一次性完成。返回值与错误处理curl_easy_setopt(handle, CURLOPT_SHARE, share)返回CURLcodeCURLE_OK (0)设置成功非零值表示出错例如CURLE_BAD_FUNCTION_ARGUMENT在句柄已连接时尝试换绑详细错误码见 libcurl-errors(3)。共享句柄侧的错误码由CURLSHcode表示CURLSHE_OK、CURLSHE_IN_USE、CURLSHE_BAD_OPTION、CURLSHE_NOMEM、CURLSHE_NOT_BUILT_IN、CURLSHE_INVALID等同样定义于 include/curl/curl.h。使用建议与注意事项汇总绑定时机始终在curl_easy_perform()之前设置CURLOPT_SHARE传输进行中修改或置 NULL 可能导致未定义行为。共享类型的声明通过curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_*)显式声明未声明者不共享每个类型可多次设置连接池类型尤其如此源码注释明确“It is safe to set this option several times on a share”。多线程必配锁只要多个 easy handle 可能同时访问共享数据就必须实现CURLSHOPT_LOCKFUNC/CURLSHOPT_UNLOCKFUNC回调。生命周期curl_share_init()创建的句柄必须最终由curl_share_cleanup()释放内部使用引用计数最后一个使用它的 easy handle 被清理后共享句柄才真正销毁因此先清理所有 easy handle 再清理共享句柄是最稳妥的顺序。能力依赖CURL_LOCK_DATA_COOKIE依赖 HTTP Cookie 支持CURL_LOCK_DATA_SSL_SESSION依赖 SSL 后端CURL_LOCK_DATA_HSTS依赖 HSTS 支持CURL_LOCK_DATA_PSL依赖 libpsl未编译对应功能时会返回CURLSHE_NOT_BUILT_IN。【免费下载链接】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 小时内为你输出方案建议。