使用 curl_multi_get_offt 精确监控 libcurl 多句柄中的传输状态
【免费下载链接】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_get_offt是 libcurl 提供的一个多接口(multi interface)信息查询函数,用于从CURLM多句柄中提取与传输数量相关的数值信息,帮助开发者实时掌握多句柄当前管理了多少 easy handle、其中有多少正在运行、多少在排队等待、多少已完成但尚未取回结果。本文以 docs/libcurl/curl_multi_get_offt.md 为骨架,结合 include/curl/multi.h 中的枚举定义与 lib/multi.c 中的底层实现,完整介绍该函数的原型、五个信息选项的语义与源码级实现原理、返回值与错误处理,并给出可直接编译运行的实战示例,帮助读者在并发传输、连接复用等场景中精确监控传输进度。
函数原型
curl_multi_get_offt于 curl 8.16.0 版本加入(见 docs/libcurl/symbols-in-versions 中记录的五个信息选项的引入版本),声明位于头文件include/curl/multi.h中,函数定义如下:
#include <curl/curl.h> CURLMcode curl_multi_get_offt(CURLM *multi_handle, CURLMinfo_offt info, curl_off_t *pvalue);三个参数的含义分别是:
| 参数 | 含义 |
|---|---|
multi_handle | 通过curl_multi_init()创建的多句柄,即要查询的对象 |
info | 要提取的信息类型,类型为CURLMinfo_offt枚举,当前可取五个CURLMINFO_XFERS_*选项 |
pvalue | 输出参数,指向curl_off_t类型变量的指针,函数执行成功后该变量被写入查询结果 |
CURLMinfo_offt是专门为这类"返回 64 位数值"的信息查询而设计的枚举类型,其定义位于 include/curl/multi.h,在CURLMINFO_NONE(保留占位,永远不要使用)之后依次定义了五个取值:
typedef enum { CURLMINFO_NONE, /* first, never use this */ CURLMINFO_XFERS_CURRENT = 1, /* 当前已添加但尚未移除的 easy handle 数 */ CURLMINFO_XFERS_RUNNING = 2, /* 正在运行、既未完成也未排队的 easy handle 数 */ CURLMINFO_XFERS_PENDING = 3, /* 等待启动的 easy handle 数 */ CURLMINFO_XFERS_DONE = 4, /* 已完成、等待通过 curl_multi_info_read() 读取结果的 easy handle 数 */ CURLMINFO_XFERS_ADDED = 5, /* 历史上总共添加过的 easy handle 数 */ CURLMINFO_LASTENTRY /* the last unused */ } CURLMinfo_offt;注意函数名中的offt即off_t,表示返回值类型为curl_off_t(一个有符号的 64 位整数类型)。使用curl_off_t而不是普通的long,可以保证在多句柄生命周期内累计的传输数量不会因整数宽度不足而溢出。
五个信息选项详解
CURLMINFO_XFERS_CURRENT:当前管理的 easy handle 数量
返回当前已添加到多句柄、但尚未移除的 easy handle 数量。不包含已经移除的句柄;包含为内部任务添加的句柄,例如通过 DoH 解析域名时产生的内部句柄。完整语义见 docs/libcurl/opts/CURLMINFO_XFERS_CURRENT.md。
CURLMINFO_XFERS_RUNNING:正在运行的 easy handle 数量
返回当前正在运行的 easy handle 数量,即传输已开始但尚未结束的句柄。当句柄已经完成、尚未处理,或者正在排队等待时,不计入该数值。完整语义见 docs/libcurl/opts/CURLMINFO_XFERS_RUNNING.md。
CURLMINFO_XFERS_PENDING:等待启动的 easy handle 数量
返回当前等待启动的 easy handle 数量。一个已添加的传输可能因多种原因进入等待状态,例如:
- 受连接数限制(连接池容量、并发上限)被迫等待空闲连接;
- DNS 解析尚未完成;
- 无法确定已有的匹配连接是否允许多路复用(HTTP/2 或 HTTP/3 的 multiplexing),需要等待判断结果。
完整语义见 docs/libcurl/opts/CURLMINFO_XFERS_PENDING.md。
CURLMINFO_XFERS_DONE:已完成但未取回结果的 easy handle 数量
返回当前已完成、但尚未通过curl_multi_info_read()处理结果的 easy handle 数量。这部分句柄的结果仍滞留在多句柄内部的消息队列中,等待应用层读取。完整语义见 docs/libcurl/opts/CURLMINFO_XFERS_DONE.md。
CURLMINFO_XFERS_ADDED:累计添加的 easy handle 总数
返回多句柄历史上总共添加过的 easy handle 数量(累计值,只增不减),同样包含 DoH 解析等内部任务产生的句柄。若想知道当前正在管理的数量,应使用CURLMINFO_XFERS_CURRENT。完整语义见 docs/libcurl/opts/CURLMINFO_XFERS_ADDED.md。
五个选项可概括为一张对照表:
| 信息选项 | 枚举值 | 含义 | 典型用途 |
|---|---|---|---|
CURLMINFO_XFERS_CURRENT | 1 | 当前已添加未移除的句柄数 | 判断多句柄当前负载 |
CURLMINFO_XFERS_RUNNING | 2 | 正在运行的句柄数 | 判断活跃传输量 |
CURLMINFO_XFERS_PENDING | 3 | 等待启动的句柄数 | 判断是否因连接限制而排队 |
CURLMINFO_XFERS_DONE | 4 | 已完成未读取结果的句柄数 | 判断是否需要调用curl_multi_info_read() |
CURLMINFO_XFERS_ADDED | 5 | 累计添加过的句柄总数 | 统计历史吞吐量 |
底层实现原理
从源码看,lib/multi.c 中curl_multi_get_offt的实现与文档描述完全一致,每个选项对应多句柄内部的一个数据结构:
CURLMINFO_XFERS_CURRENT读取multi->xfers(Curl_uint32_tbl类型的句柄表)中的条目数;由于多句柄内部会维护一个multi->admin管理句柄,统计时如果该管理句柄在表中,会先减去 1,确保返回的是用户可见的传输数量;CURLMINFO_XFERS_RUNNING统计multi->process(Curl_uint32_bset类型的位集合)中正在处理的条目,同样会扣除admin管理句柄本身;CURLMINFO_XFERS_PENDING直接统计multi->pending位集合中的条目数;CURLMINFO_XFERS_DONE统计multi->msgsent位集合中的条目数,即已完成并进入消息发送队列、等待curl_multi_info_read()取走的句柄;CURLMINFO_XFERS_ADDED直接返回multi->xfers_total_ever计数器,这是一个只增不减的累计值。
函数内部通过CURL_MAPI_ENTER/CURL_MAPI_LEAVE守卫机制保证并发安全。如果传入的info不在上述五个枚举值中,走default分支:把*pvalue置为-1,并返回CURLM_UNKNOWN_OPTION。
实现中还体现了一个重要细节:pvalue不允许为NULL,如果传入空指针,函数直接返回CURLM_BAD_FUNCTION_ARGUMENT(参数错误)。
返回值与错误处理
函数返回CURLMcode类型,遵循"0 表示成功、非 0 表示出错"的约定:
CURLM_OK(0):查询成功,*pvalue中写入了有效数值;CURLM_BAD_FUNCTION_ARGUMENT:pvalue为空指针(由 lib/multi.c 中的参数校验产生);CURLM_UNKNOWN_OPTION:info不是有效的CURLMINFO_XFERS_*选项,此时*pvalue会被置为-1;- 其他非零值表示发生了其他错误,完整的错误码说明参见 docs/libcurl/libcurl-errors.md(即手册中的
libcurl-errors(3))。
完整示例
下面是一个完整的可编译示例,演示了创建多句柄、添加 easy handle 后查询CURLMINFO_XFERS_ADDED的用法:
#include <curl/curl.h> int main(void) { /* init a multi stack */ CURLM *multi = curl_multi_init(); CURL *curl = curl_easy_init(); curl_off_t n; if(curl) { /* add the transfer */ curl_multi_add_handle(multi, curl); curl_multi_get_offt(multi, CURLMINFO_XFERS_ADDED, &n); /* on successful add, n is 1 */ } }对应的五个选项各自的调用示例可以参考对应选项文档中的EXAMPLE小节:CURLMINFO_XFERS_CURRENT见 docs/libcurl/opts/CURLMINFO_XFERS_CURRENT.md、CURLMINFO_XFERS_RUNNING见 docs/libcurl/opts/CURLMINFO_XFERS_RUNNING.md、CURLMINFO_XFERS_PENDING见 docs/libcurl/opts/CURLMINFO_XFERS_PENDING.md、CURLMINFO_XFERS_DONE见 docs/libcurl/opts/CURLMINFO_XFERS_DONE.md、CURLMINFO_XFERS_ADDED见 docs/libcurl/opts/CURLMINFO_XFERS_ADDED.md。
在实际的多接口事件循环中,一个典型的使用模式是:在每轮curl_multi_perform()(或等价的curl_multi_poll())之后查询CURLMINFO_XFERS_RUNNING,判断是否还有活跃传输;查询CURLMINFO_XFERS_PENDING,判断是否有句柄因连接限制而排队;查询CURLMINFO_XFERS_DONE,判断是否有已完成结果等待通过curl_multi_info_read()取走(后者的语义可参考 docs/libcurl/curl_multi_info_read.md);查询CURLMINFO_XFERS_CURRENT与CURLMINFO_XFERS_ADDED则分别用于把握当前负载与历史吞吐量。
使用注意事项
- 该函数适用于所有协议(文档中
Protocol: All),与具体传输协议无关,可以在任何多接口应用中安全使用; - 返回值统一为
curl_off_t(64 位有符号整数),适合累计计数场景,不会轻易溢出; - 统计口径包含内部句柄(如 DoH 解析句柄),在高频添加/移除句柄时,
CURLMINFO_XFERS_CURRENT等瞬时值可能短暂包含内部任务,精确语义请以 include/curl/multi.h 中枚举注释与 lib/multi.c 的实现为准; - 该 API 自 curl 8.16.0 起可用,使用前请确认链接的 libcurl 版本不低于此版本(可参见 docs/libcurl/symbols-in-versions);
- 调用前务必为
pvalue提供有效的非空指针,并检查返回值,避免将-1误当作有效计数。
通过合理组合这五个信息选项,开发者可以精确掌握多句柄内部每一个传输的生命周期阶段(排队、运行、完成、累计),为并发下载器、批量请求调度等场景提供可靠的监控与调度依据。
【免费下载链接】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),仅供参考