
libcurl CURLOPT_STREAM_WEIGHT 详解HTTP/2 流权重与带宽分配控制【免费下载链接】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_STREAM_WEIGHT是 libcurl 提供的一个 easy handle 级选项用于在 HTTP/2 多路复用multiplexing场景下为单个流设置数值权重从而控制同一连接上多个并发传输之间的资源分配比例。本文以 curl 项目官方文档 CURLOPT_STREAM_WEIGHT.md 为骨架结合 libcurl 源码setopt.c、http2.c与测试用例lib2404.c进行纵深讲解。读完本文你将掌握该选项的取值规则、生效前提、运行时更新机制以及如何结合CURLMOPT_PIPELINING、CURLOPT_PIPEWAIT编写可复用的多流带宽分配代码。概览属性值选项名称CURLOPT_STREAM_WEIGHT头文件#include curl/curl.h适用协议HTTPHTTP/2 多路复用源码中同时允许 HTTP/3 编译路径引入版本7.46.0取值类型long有效范围 1256默认值16关联选项CURLMOPT_PIPELINING、CURLOPT_PIPEWAIT、CURLOPT_STREAM_DEPENDS、CURLOPT_STREAM_DEPENDS_E官方文档定位为 numerical stream weight数值流权重原型定义于 curl.h/* Set stream weight, 1 - 256 (default is 16) */ CURLOPT(CURLOPT_STREAM_WEIGHT, CURLOPTTYPE_LONG, 239),函数原型与基本用法#include curl/curl.h CURLcode curl_easy_setopt(CURL *handle, CURLOPT_STREAM_WEIGHT, long weight);将weight设置为1 到 256 之间的long数值。该选项针对当前 easy handle 所对应的那一条 HTTP/2 流生效用来表达这条流相对同连接上其他流的带宽优先级。文档给出的最小示例展示了如何为两个并发句柄设置不同权重int main(void) { CURL *curl curl_easy_init(); CURL *curl2 curl_easy_init(); /* a second handle */ if(curl) { curl_easy_setopt(curl, CURLOPT_URL, https://example.com/one); curl_easy_setopt(curl, CURLOPT_STREAM_WEIGHT, 10L); /* the second has twice the weight */ curl_easy_setopt(curl2, CURLOPT_URL, https://example.com/two); curl_easy_setopt(curl2, CURLOPT_STREAM_WEIGHT, 20L); /* then add both to a multi handle and transfer them */ } }从源码看参数校验在 setopt.c 中可以看到该选项的落地逻辑case CURLOPT_STREAM_WEIGHT: #if defined(USE_HTTP2) || defined(USE_HTTP3) if((arg 1) (arg 256)) s-weight (int)arg; break; #else result CURLE_NOT_BUILT_IN; break; #endif两点关键事实范围硬校验只有1 weight 256的值才会被写入句柄内部字段越界值会被静默忽略不报错、不生效。编译期依赖该选项仅在启用USE_HTTP2或USE_HTTP3的构建中生效若 libcurl 未编译 HTTP/2/HTTP/3 支持调用会返回CURLE_NOT_BUILT_IN。用户设置的原始值保存在data-set.weighturldata.h 中注释为 Priority information for an easy handle in relation to others on the same connection并在每次创建新流时被浅拷贝到data-state.weight见 urldata.h 的int weight; /* shallow copy of>CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_PIPELINING, long bitmask);bitmask 取值值含义CURLPIPE_NOTHING(0)不做任何多路复用尝试CURLPIPE_HTTP1(1)已废弃自 7.62.0 起无效果CURLPIPE_MULTIPLEX(2)在可行时尝试将新传输复用到一个已有连接上需要 HTTP/2 或 HTTP/3需要注意的是自 7.62.0 起CURLPIPE_MULTIPLEX已是CURLMOPT_PIPELINING的默认值也就是说现代 libcurl 默认就开启多路复用HTTP/1.1 Pipelining 支持则在 7.62.0 被移除。因此在一个默认构建的 libcurl 上只要并发 easy 句柄指向同一主机且协商到 HTTP/2流权重就会实际参与调度。另一个配套选项是CURLOPT_PIPEWAIT见 CURLOPT_PIPEWAIT.md它让 easy 句柄在建立新连接前先等待片刻尝试复用一个已有的同主机连接——这正是让多条流落进同一条 HTTP/2 连接的关键配合手段。完整的同系列选项还包括CURLOPT_STREAM_DEPENDS与CURLOPT_STREAM_DEPENDS_E流依赖声明用于表达优先级树结构不过两者在 8.21.0 已被标记为 deprecated见 curl.h标注 Has no function。权重语义按比例分配资源文档用一段非常直观的比例描述解释权重含义Streams with the same parent should be allocated resources proportionally based on their weight. If you have two streams going, stream A with weight 16 and stream B with weight 32, stream B gets two thirds (32/48) of the available bandwidth (assuming the server can send off the data equally for both streams).即拥有同一父流的子流之间资源按其权重占权重总和的比例分配。例如两条流权重分别为 16 与 32则总权重 48第二条流获得 2/332/48的可用带宽前提是服务器能对两条流以相同速率发送数据。权重相对值才有意义20 相对 10 是两倍带宽但 20 相对 100 只是约 1/6。权重是建议性调度提示HTTP/2 的优先级机制是尽力而为的服务器端调度器可能实现为权重比例也可能实现为先到先得因此不应把权重当作精确带宽保证RFC 7540 第 5.3 节定义了该语义正文协议细节可查阅该规范。默认值 16 是 HTTP/2 协议标准中定义的默认权重NGHTTP2_DEFAULT_WEIGHT与 libcurl 的DEFAULT: 16一致。运行时更新传输过程中动态调权该选项一个实用特性是可以在传输进行中修改。文档说明This option can be set during transfer and causes the updated weight info get sent to the server the next time an HTTP/2 frame is sent to the server.在源码层这一机制由 http2.c 中的h2_progress_egress()实现http2.c/* * Check if there is been an update in the priority / * dependency settings and if so it submits a PRIORITY frame with the updated * info. * Flush any out data pending in the network buffer. */ static CURLcode h2_progress_egress(struct Curl_cfilter *cf, struct Curl_easy *data) { ... if(stream stream-id 0 (sweight_wanted(data) ! sweight_in_effect(data))) { /* send new weight and/or dependency */ nghttp2_priority_spec pri_spec; h2_pri_spec(data, pri_spec); CURL_TRC_CF(data, cf, [%d] Queuing PRIORITY, stream-id); DEBUGASSERT(stream-id ! -1); rv nghttp2_submit_priority(ctx-h2, NGHTTP2_FLAG_NONE, stream-id, pri_spec); ...其工作流程可概括为libcurl 周期性检查用户期望权重sweight_wanted读data-set.weight与当前生效权重sweight_in_effect读data-state.weight是否不一致若不一致则构造nghttp2_priority_spec见h2_pri_spec()http2.c并调用nghttp2_submit_priority()向服务器提交一个PRIORITY 帧服务器收到新的 PRIORITY 帧后会按新权重调整该流的调度实现动态调权。辅助函数中还体现了一个细节sweight_wanted()与sweight_in_effect()在用户未设置权重即data-set.weight 0时会回退到NGHTTP2_DEFAULT_WEIGHT即 16与文档声明的默认值完全一致。也就是说没设置 与 显式设置 16 的效果相同而由于setopt只接受 1256内部用 0 表示未设置这个哨兵值。完整可运行示例多流带宽分配综合以上知识给出一个可直接编译运行的完整示例多 easy 句柄 multi 接口 HTTP/2 权重分配#include stdio.h #include curl/curl.h /* 两个句柄指向同一主机分别设置不同权重 权重 40 与 10 意味着前者期望获得约 4/5 的带宽份额 */ int main(void) { CURLM *multi curl_multi_init(); CURL *curl_a curl_easy_init(); CURL *curl_b curl_easy_init(); int still_running 0; if(!multi || !curl_a || !curl_b) return 1; /* 期望这两条流被复用进同一条 HTTP/2 连接 */ curl_easy_setopt(curl_a, CURLOPT_URL, https://example.com/one); curl_easy_setopt(curl_a, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); curl_easy_setopt(curl_a, CURLOPT_PIPEWAIT, 1L); curl_easy_setopt(curl_a, CURLOPT_STREAM_WEIGHT, 40L); curl_easy_setopt(curl_b, CURLOPT_URL, https://example.com/two); curl_easy_setopt(curl_b, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); curl_easy_setopt(curl_b, CURLOPT_PIPEWAIT, 1L); curl_easy_setopt(curl_b, CURLOPT_STREAM_WEIGHT, 10L); /* 7.62.0 起多路复用默认开启此处显式声明以强调依赖关系 */ curl_multi_setopt(multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); curl_multi_add_handle(multi, curl_a); curl_multi_add_handle(multi, curl_b); do { curl_multi_perform(multi, still_running); if(still_running) { int maxfd -1; fd_set rd, wr, ex; struct timeval tv { 1, 0 }; FD_ZERO(rd); FD_ZERO(wr); FD_ZERO(ex); curl_multi_fdset(multi, rd, wr, ex, maxfd); select(maxfd 1, rd, wr, ex, tv); } } while(still_running); curl_multi_remove_handle(multi, curl_a); curl_multi_remove_handle(multi, curl_b); curl_easy_cleanup(curl_a); curl_easy_cleanup(curl_b); curl_multi_cleanup(multi); return 0; }要点说明CURL_HTTP_VERSION_2_0强制协商 HTTP/2若服务器不支持会回退更保险的做法是配合CURLOPT_HTTP_VERSION使用优先 HTTP/2 的取值。CURLOPT_PIPEWAIT让两条流等待并复用同一连接这是权重比较产生意义的前提。权重比例是期望实际调度仍取决于服务器端的 HTTP/2 调度实现。若要实现运行时调权只需在curl_multi_perform循环中调用curl_easy_setopt(curl_a, CURLOPT_STREAM_WEIGHT, 新值)libcurl 会在下次出站帧发送时提交 PRIORITY 帧见上文h2_progress_egress。测试用例佐证lib2404仓库中的 lib2404.c 是专门覆盖该选项的集成测试对应测试套件中编号 2404 的用例。其关键片段/* go http2 */ easy_setopt(curl[i], CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); /* no peer verify */ easy_setopt(curl[i], CURLOPT_SSL_VERIFYPEER, 0L); easy_setopt(curl[i], CURLOPT_SSL_VERIFYHOST, 0L); /* wait for first connection established to see if we can share it */ easy_setopt(curl[i], CURLOPT_PIPEWAIT, 1L); ... easy_setopt(curl[i], CURLOPT_STREAM_WEIGHT, (long)i 128);该测试同时展示了设置流权重时常用的完整配套CURLMOPT_MAXCONNECTS设为 1强制所有 easy 句柄共用同一条连接CURL_HTTP_VERSION_2_0启用 HTTP/2CURLOPT_PIPEWAIT让后续句柄等待复用首条连接每个句柄的权重按128 i递增验证不同权重在多流并发下的行为。这从侧面印证流权重必须与同连接多流的配置组合使用才有效单独设置权重而不复用连接不会产生任何调度效果。返回值与错误处理文档说明curl_easy_setopt()返回CURLcodeCURLE_OK (0)表示一切正常非零值表示出错详见 libcurl-errors.md。结合源码CURLOPT_STREAM_WEIGHT 的典型非零返回是CURLE_NOT_BUILT_IN当 libcurl 未编译 HTTP/2/HTTP/3 支持时。另外注意参数越界小于 1 或大于 256不会返回错误而是被静默忽略。小结与最佳实践取值范围1256默认 16setopt层有硬校验越界值静默忽略。生效条件HTTP/2或 HTTP/3多路复用下的同连接多流并发需要配合 multi 接口、CURLMOPT_PIPELINING7.62.0 起默认开启与CURLOPT_PIPEWAIT。语义同父流的子流按权重比例分配资源权重是相对值、是调度建议而非带宽硬保证协议细节见 RFC 7540 第 5.3 节。动态性可在传输中更新权重libcurl 通过 http2.c 的h2_progress_egress()在下次出站帧时提交 PRIORITY 帧实现动态调权。关联选项CURLOPT_STREAM_DEPENDS/CURLOPT_STREAM_DEPENDS_E用于构建流依赖树8.21.0 起废弃CURLMOPT_PIPELINING是多路复用总开关。验证方式可参考 lib2404.c 的组合用法编写自己的并发权重测试。【免费下载链接】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),仅供参考