ARTICLE DETAIL

资讯详情

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

curl 共享对象取消共享详解:CURLSHOPT_UNSHARE 使用指南与实现原理

curl 共享对象取消共享详解:CURLSHOPT_UNSHARE 使用指南与实现原理 curl 共享对象取消共享详解CURLSHOPT_UNSHARE 使用指南与实现原理【免费下载链接】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导读CURLSHOPT_UNSHARE 是 libcurl 共享对象share object机制中与 CURLSHOPT_SHARE 对应的反向操作用于在运行时将 Cookie、DNS 缓存、SSL 会话、连接缓存等数据从共享状态中解除恢复各 easy handle 的独立数据视图。本文基于 curl 官方文档与 libcurl 源码讲解该选项的语义、五种可取消共享的数据类型、与 CURLSHOPT_SHARE 的配合用法、底层实现细节及适用场景帮助读者在需要动态调整共享策略时安全、正确地使用它。一、什么是 CURLSHOPT_UNSHARECURLSHOPT_UNSHARE 是 libcurl 共享对象share object机制中与 CURLSHOPT_SHARE 对应的反向操作用于在运行时将 Cookie、DNS 缓存、SSL 会话、连接缓存等数据从共享状态中解除恢复各 easy handle 的独立数据视图。共享对象是 libcurl 中一套独立于 easy handle 的生命周期管理机制通过 curl_share_init(3) 创建通过 curl_share_setopt(3) 配置而 CURLSHOPT_SHARE 与 CURLSHOPT_UNSHARE 正是其中控制共享什么数据的一对开关前者把某类数据加入共享后者把某类数据从共享中移除。函数原型#include curl/curl.h CURLSHcode curl_share_setopt(CURLSH *share, CURLSHOPT_UNSHARE, int type);share由 curl_share_init() 创建的有效共享对象句柄type要停止共享的数据类型必须是下文列出的CURL_LOCK_DATA_*枚举值之一返回值CURLSHE_OK零表示设置成功非零表示出错详见 libcurl-errors(3)。从签名可以看出CURLSHOPT_UNSHARE 与 CURLSHOPT_SHARE 是同一个函数curl_share_setopt()的两个选项二者通过第二个参数区分第三个参数统一为int type。该选项自7.10.3版本加入适用于所有协议。与 CURLSHOPT_SHARE 的对称关系在 include/curl/curl.h 中二者被定义为相邻的两个枚举值typedef 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(3) 文档中明确指出Unset a type again by setting CURLSHOPT_UNSHARE(3)通过设置 CURLSHOPT_UNSHARE 来取消某类数据的共享反之亦然。这印证了两者是一对可反复切换的状态开关。二、可取消共享的数据类型type参数的可选值定义在 include/curl/curl.h 的curl_lock_data枚举中typedef 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_LAST为哨兵值实际可供应用层使用的有 6 个。CURLSHOPT_UNSHARE 文档明确支持其中 5 个HSTS 仅在 CURLSHOPT_SHARE 中列出逐一说明如下CURL_LOCK_DATA_COOKIE停止共享 Cookie 数据。取消后使用该共享对象的 easy handle 将不再共享彼此之间的 Cookie 状态每个 easy handle 回到各自独立的 Cookie 处理逻辑。从源码 lib/curl_share.c 看取消共享时若共享对象中已存在 Cookie 仓库会执行清理并置空case CURL_LOCK_DATA_COOKIE: #if !defined(CURL_DISABLE_HTTP) !defined(CURL_DISABLE_COOKIES) if(share-cookies) { Curl_cookie_cleanup(share-cookies); share-cookies NULL; } #else /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */ res CURLSHE_NOT_BUILT_IN; #endif break;注意若 libcurl 在编译时被禁用 HTTP 或禁用 Cookie 支持CURL_DISABLE_HTTP/CURL_DISABLE_COOKIES该操作会返回CURLSHE_NOT_BUILT_IN。CURL_LOCK_DATA_DNS停止共享 DNS 缓存。取消后各 easy handle 将各自维护独立的 DNS 解析缓存。从源码看DNS 类型在 UNSHARE 分支中是一个空操作lib/curl_share.c因为它没有独立的资源句柄需要销毁——共享 DNS 时仅通过specifier标志位记录共享意图lib/curl_share.c取消共享时同样只需清除标志位。DNS 缓存的销毁统一在share_destroy()中通过Curl_dnscache_destroy()完成lib/curl_share.c。值得补充的是当使用 multi 接口时加入同一 multi handle 的所有 easy handle默认就共享 DNS 缓存无需也不依赖本选项。CURL_LOCK_DATA_SSL_SESSION停止共享 SSL 会话缓存。取消后各 easy handle 不再复用彼此缓存的 TLS 会话重新建立 SSL 连接时需再次执行完整握手。源码实现lib/curl_share.c会销毁共享的 SSL 会话缓存case CURL_LOCK_DATA_SSL_SESSION: #ifdef USE_SSL if(share-ssl_scache) { Curl_ssl_scache_destroy(share-ssl_scache); share-ssl_scache NULL; } #else res CURLSHE_NOT_BUILT_IN; #endif break;若 libcurl 编译时未启用任何 TLS 后端未定义USE_SSL同样返回CURLSHE_NOT_BUILT_IN。CURL_LOCK_DATA_CONNECT停止共享连接缓存。取消后各 easy handle 不再复用彼此缓存的 TCP/TLS 连接。源码实现lib/curl_share.c中连接缓存类型在 UNSHARE 分支同样是空操作——连接池本身在share_destroy()中通过Curl_cpool_destroy()统一销毁lib/curl_share.c取消共享仅清除specifier标志位。连接缓存的共享有更严格的约束不支持在多个并发线程之间共享连接HTTP/2 与 HTTP/3 的多路复用流只有在连接被同一 multi 或 easy handle 持有时才会追加新传输libcurl 不支持跨线程通过共享连接做多路复用。CURL_LOCK_DATA_PSL停止共享 Public Suffix List公共后缀列表。PSL 用于识别 Cookie 的作用域边界如区分example.com与com取消共享后各 easy handle 回退到各自的 PSL 上下文。源码实现lib/curl_share.c中PSL 在 UNSHARE 分支也是空操作需要说明的是若 libcurl 编译时未启用 libpsl未定义USE_LIBPSL即使在 SHARE 分支也会返回CURLSHE_NOT_BUILT_INlib/curl_share.c。需要特别提醒在 CURLSHOPT_UNSHARE 的实现中CURL_LOCK_DATA_PSL与CURL_LOCK_DATA_HSTS均落入default分支并返回CURLSHE_BAD_OPTIONlib/curl_share.c因为文档明确列出的可取消类型仅包含 COOKIE、DNS、SSL_SESSION、CONNECT、PSL 五种。HSTSCURL_LOCK_DATA_HSTS7.88.0 加入仅出现在 CURLSHOPT_SHARE(3) 中当前不通过 UNSHARE 取消——若需停止共享 HSTS应通过 curl_share_cleanup(3) 销毁整个共享对象或调整使用该共享对象的 easy handle 集合。三、使用约束不要在共享对象使用中时取消共享CURLSHOPT_UNSHARE 文档明确警告Do not remove types from a shared object that is being in use. Unshare them only between transfers.不要在正在使用中的共享对象上移除数据类型只能在两次传输之间执行 UNSHARE。原因是共享对象内部通过引用计数跟踪使用状态——lib/curl_share.c 中share_ref_inc()/share_ref_dec()维护ref_countshare_in_use()lib/curl_share.c判断ref_count 1即视为使用中。更关键的是curl_share_setopt()的入口处直接执行了检查lib/curl_share.cif(!GOOD_SHARE_HANDLE(share)) return CURLSHE_INVALID; if(share_in_use(share)) { /* do not allow setting options while one or more handles are already using this share */ return CURLSHE_IN_USE; }也就是说只要还有 easy handle 通过CURLOPT_SHARE绑定该共享对象任何curl_share_setopt()调用包括 SHARE 与 UNSHARE都会返回CURLSHE_IN_USE。这一点在测试 tests/libtest/lib506.c 中有直接验证——测试先让 easy handle 使用共享对象再调用curl_share_cleanup()期望其失败并打印 SHARE_CLEANUP failed, correct。因此正确的取消共享流程必须是先确保所有使用该共享对象的 easy handle 完成当前传输并解绑curl_easy_cleanup()或不再使用再调用curl_share_setopt(share, CURLSHOPT_UNSHARE, type)取消共享之后创建的 easy handle 将不再共享该数据类型如需恢复则重新调用CURLSHOPT_SHARE。四、完整示例与错误处理CURLSHOPT_UNSHARE 文档给出的最小示例int main(void) { CURLSHcode sh; CURLSH *share curl_share_init(); sh curl_share_setopt(share, CURLSHOPT_UNSHARE, CURL_LOCK_DATA_COOKIE); if(sh) printf(Error: %s\n, curl_share_strerror(sh)); }下面是一个更完整、可运行的实战示例——先共享 Cookie 与 DNS再在两个传输之间取消 Cookie 共享#include stdio.h #include curl/curl.h int main(void) { CURLSHcode sh; CURLSH *share; CURL *easy; curl_global_init(CURL_GLOBAL_DEFAULT); share curl_share_init(); if(!share) return 1; /* 第一步开启共享 */ sh curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); if(sh) { printf(SHARE cookie failed: %s\n, curl_share_strerror(sh)); goto cleanup; } sh curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); if(sh) { printf(SHARE dns failed: %s\n, curl_share_strerror(sh)); goto cleanup; } /* 第二步创建 easy handle 并绑定共享对象执行传输 */ easy curl_easy_init(); if(!easy) { curl_share_cleanup(share); return 1; } curl_easy_setopt(easy, CURLOPT_SHARE, share); curl_easy_setopt(easy, CURLOPT_URL, https://example.com/); curl_easy_perform(easy); curl_easy_cleanup(easy); /* 解绑共享对象回到空闲状态 */ /* 第三步两次传输之间取消 Cookie 共享DNS 继续保持共享 */ sh curl_share_setopt(share, CURLSHOPT_UNSHARE, CURL_LOCK_DATA_COOKIE); if(sh) printf(UNSHARE cookie failed: %s\n, curl_share_strerror(sh)); /* 后续创建的 easy handle 将独立维护 Cookie但 DNS 仍共享 */ cleanup: curl_share_cleanup(share); curl_global_cleanup(); return 0; }返回值与错误码返回值类型为CURLSHcode定义于 include/curl/curl.htypedef enum { CURLSHE_OK, /* all is fine */ CURLSHE_BAD_OPTION, /* 1 */ CURLSHE_IN_USE, /* 2 */ CURLSHE_INVALID, /* 3 */ CURLSHE_NOMEM, /* 4 out of memory */ CURLSHE_NOT_BUILT_IN, /* 5 feature not present in lib */ CURLSHE_LAST /* never use */ } CURLSHcode;调用 CURLSHOPT_UNSHARE 时可能遇到的错误码及典型触发条件错误码含义典型触发场景CURLSHE_OK设置成功正常取消共享CURLSHE_BAD_OPTION非法选项type不是受支持的CURL_LOCK_DATA_*值如传入 HSTS、PSLCURLSHE_IN_USE共享对象正在使用中仍有 easy handle 绑定该共享对象时调用CURLSHE_INVALID句柄无效share 句柄被破坏或不是有效的CURLSH*CURLSHE_NOMEM内存不足内部资源分配失败CURLSHE_NOT_BUILT_IN特性未编译进库库编译时禁用了对应功能如无 SSL、无 Cookie可使用 curl_share_strerror 将错误码转换为可读的错误描述字符串。五、底层实现specifier 标志位机制理解 CURLSHOPT_UNSHARE 的底层原理关键在于specifier位图字段。共享对象struct Curl_share用一个 32 位无符号整数specifier记录当前共享了哪些数据每种数据类型占一个位。在 lib/curl_share.c 的CURLSHOPT_SHARE分支中case CURLSHOPT_SHARE: /* this is a type this share will share */ type va_arg(param, int); /* ... 各类型初始化对应资源 ... */ if(!res) share-specifier | (unsigned int)(1 type); break;成功共享某类型数据后通过share-specifier | (1 type)置位而在CURLSHOPT_UNSHARE分支中lib/curl_share.c成功取消后通过share-specifier ~(unsigned int)(1 type)清位。这一位图在运行时被广泛用于共享行为的判定例如 Curl_share_lock_share()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 */即只有specifier中对应位被置位该类型处于共享状态时才调用应用层提供的加锁回调否则直接返回成功。同理Curl_share_unlock_share() 也只有在该类型仍处于共享状态时才调用解锁回调。而 easy handle 与共享对象的绑定关系由Curl_share_easy_link()/Curl_share_easy_unlink()lib/curl_share.c管理绑定时递增ref_count并让 easy handle 的字段如data-cookies、data-hsts指向共享资源解绑时还原。这也解释了为什么使用中ref_count 1时不能执行 UNSHARE——此时正在被引用的数据结构如share-cookies一旦被清理已绑定的 easy handle 就会悬空。在 curl_share_init() 创建共享对象时specifier初始只置位内部使用的CURL_LOCK_DATA_SHARE位share-specifier | (1 CURL_LOCK_DATA_SHARE)其余数据类型的位均处于清零状态即默认不共享任何数据——所有共享行为都需要应用层显式通过 CURLSHOPT_SHARE 开启CURLSHOPT_UNSHARE 则用于反向关闭。六、典型应用场景与注意事项适用场景动态调整共享策略同一共享对象在程序生命周期内前阶段共享 Cookie 用于会话保持后阶段出于隔离需求取消共享使各 easy handle 的 Cookie 互不干扰资源释放与回收共享的 SSL 会话缓存、连接缓存占据内存在不再需要时通过 UNSHARE 触发内部清理Cookie 缓存会立即Curl_cookie_cleanup()与 CURLSHOPT_SHARE 配合实现局部共享例如仅共享 DNS 与 SSL 会话加速重连但不共享 Cookie保持隔离通过先 SHARE 后按需 UNSHARE 灵活组合。注意事项必须传输间隙操作UNSHARE 只能在没有任何 easy handle 使用该共享对象时执行否则返回CURLSHE_IN_USE多线程场景若共享数据被多线程访问必须同时设置 CURLSHOPT_LOCKFUNC 与 CURLSHOPT_UNLOCKFUNC 回调且 Cookie、连接、HSTS 等数据类型官方明确不支持跨并发线程共享编译期功能裁剪未启用 TLS 的构建不支持 SSL 会话共享禁用 Cookie/HTTP 的构建不支持 Cookie 共享这些场景下 UNSHARE 会返回CURLSHE_NOT_BUILT_INUNSHARE 后无需显式恢复取消共享后easy handle 自然回归各自的独立数据维护逻辑与从未共享过的行为一致共享对象生命周期最终使用 curl_share_cleanup(3) 销毁共享对象时会统一释放所有仍存留的共享资源lib/curl_share.c因此 UNSHARE 更适合运行中调整而非程序退出前的清理。七、相关文档与资源反向操作CURLSHOPT_SHARE(3) — 将数据类型加入共享选项总览curl_share_setopt(3) — 共享对象选项设置入口生命周期curl_share_init(3) 与 curl_share_cleanup(3)错误处理curl_share_strerror 与 libcurl-errors(3)源码与测试共享对象核心实现、CURL_LOCK_DATA 与 CURLSHOPT 枚举定义、共享功能综合测试 lib506、选项手册构建清单【免费下载链接】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),仅供参考
返回列表