ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

libcurl 多接口编程:curl_multi_waitfds 提取文件描述符详解

libcurl 多接口编程:curl_multi_waitfds 提取文件描述符详解 libcurl 多接口编程curl_multi_waitfds 提取文件描述符详解【免费下载链接】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_waitfds是 libcurl 在 8.8.0 版本Added-in: 8.8.0见 curl_multi_waitfds.md中新增的多接口multi interface函数用于从CURLM句柄中提取与poll(2)的pollfd结构相似的curl_waitfd描述符数组从而让应用可以按poll()的语义自行轮询 libcurl 内部使用的所有 socket。本文基于官方手册 docs/libcurl/curl_multi_waitfds.md结合 lib/multi.c 的实现、include/curl/multi.h 的类型定义与 tests/libtest/lib2405.c 的测试用例完整讲解该 API 的声明、行为、错误处理与典型用法帮助你把它无缝接入自己的事件循环或 poll 驱动的 I/O 框架。1. 函数声明与头文件#include curl/curl.h #include stdlib.h CURLMcode curl_multi_waitfds(CURLM *multi, struct curl_waitfd *ufds, unsigned int size, unsigned int *fd_count);curl_multi_waitfds属于 libcurl 多接口multi interface因此使用前需要先通过curl_multi_init()创建CURLM *句柄。函数原型在公共头文件 include/curl/multi.h 中声明并通过 lib/libcurl.defWindows 导出表与 projects/OS400/curl.inc.in 等平台导出清单对外暴露所有协议Protocol: AllDICT、FILE、FTP、HTTP、HTTPS、MQTT、SCP、SFTP、SMTP、WS 等下的传输都适用。1.1 参数含义参数说明multi已初始化并挂载了 easy 句柄的CURLM *多句柄ufds由调用方提供的struct curl_waitfd数组libcurl 会向其中填充待轮询的描述符数组容量由size指定sizeufds数组可容纳的元素个数unsigned intfd_count输出参数可为 NULL返回时写入 multi 句柄当前需要检查可读/可写的描述符总数1.2 curl_waitfd 结构与 pollfd 对齐的公共类型struct curl_waitfd在 include/curl/multi.h 中定义为/* Based on poll(2) structure and values. * We do not use pollfd and POLL* constants explicitly * to cover platforms without poll(). */ #define CURL_WAIT_POLLIN 0x0001 #define CURL_WAIT_POLLPRI 0x0002 #define CURL_WAIT_POLLOUT 0x0004 struct curl_waitfd { curl_socket_t fd; short events; short revents; };该结构刻意与 POSIXpollfd保持同构fd、events、revents三个字段事件掩码语义也与POLLIN/POLLPRI/POLLOUT对应但使用 libcurl 自己的常量CURL_WAIT_POLLIN/CURL_WAIT_POLLPRI/CURL_WAIT_POLLOUT这样在不提供poll()的平台如某些 Windows/Winsock 环境上也能编译使用。头文件注释明确说明这是有意为之We do not use pollfd and POLL* constants explicitly to cover platforms without poll()。2. 行为说明2.1 功能定位该函数从给定的 multi 句柄中提取curl_waitfd结构数组用于以与curl_multi_poll(3)相似的方式轮询 multi 句柄的文件描述符。一旦其中某个描述符可读或可写就应立即调用curl_multi_perform(3)驱动传输推进。它与 curl_multi_fdset 的关系是后者面向select()的fd_set模型而前者面向poll()的pollfd模型两者底层都源自同一个内部 pollset 收集结果见下文第 4 节。2.2 填充规则libcurl 会向调用方提供的ufds数组填充数据最多填充size个元素。若 multi 句柄实际使用的描述符数量大于sizelibcurl 返回CURLM_OUT_OF_MEMORY错误语义是缓冲区太小装不下并非真实内存耗尽。若fd_count非空返回时它指向的变量会保存 multi 句柄当前需要检查可读/可写的描述符总数用于让调用方判断数组是否够大。调用方可以传size等于 0 来先问个数此时 libcurl 只统计不填充fd_count会收到一个大于或等于实际描述符数量的值调用方据此分配足够的存储再在后续调用中传入。官方手册原文强调此时 fd_countreceives a number greater than or equal to the number of descriptors。2.3 错误处理函数返回CURLMcodeCURLM_OK (0)表示一切正常非零表示发生错误具体错误码参见 libcurl-errors(3)。两个典型的非零返回值CURLM_BAD_FUNCTION_ARGUMENT参数不合法。从 lib/multi.c 的实现可见当!ufds (size || !fd_count)即传入 NULL 的ufds却同时给出非零size或 NULL 的fd_count导致无法返回计数时返回该错误。CURLM_OUT_OF_MEMORY传入的数组容量size小于所需描述符数量。3. 官方示例两步走动态分配curl_multi_waitfds.md 给出的完整示例展示了先问数量、再分配、再填充、再轮询的标准用法#include stdlib.h int main(void) { CURLMcode mresult; struct curl_waitfd *ufds; CURLM *multi curl_multi_init(); do { /* call curl_multi_perform() */ /* get the count of file descriptors from the transfers */ unsigned int fd_count 0; mresult curl_multi_waitfds(multi, NULL, 0, fd_count); if(mresult ! CURLM_OK) { fprintf(stderr, curl_multi_waitfds() failed, code %d.\n, mresult); break; } if(!fd_count) continue; /* no descriptors yet */ /* allocate storage for our descriptors */ ufds malloc(fd_count * sizeof(struct curl_waitfd)); /* get wait descriptors from the transfers and put them into array. */ mresult curl_multi_waitfds(multi, ufds, fd_count, fd_count); if(mresult ! CURLM_OK) { fprintf(stderr, curl_multi_waitfds() failed, code %d.\n, mresult); free(ufds); break; } /* Do polling on descriptors in ufds */ free(ufds); } while(!mresult); }代码中的关键步骤与注意事项第一次调用传NULL与size 0仅通过fd_count获取所需描述符数量若返回0说明 multi 句柄当前没有活动 socket例如句柄为空或传输尚未建立连接可continue继续下一轮循环。动态分配数组malloc(fd_count * sizeof(struct curl_waitfd))容量刚好等于所需数量保证第二次调用不会触发CURLM_OUT_OF_MEMORY。第二次调用填充数组ufds与fd_count同时传入此时fd_count既是容量上限也是输出参数返回后代表实际写入的描述符个数与所需数量相等。轮询与驱动对ufds中每个元素执行poll()或epoll/kqueue等只要语义对齐CURL_WAIT_POLLIN/CURL_WAIT_POLLOUT一旦revents命中即调用curl_multi_perform()推进传输。循环退出条件!mresult只是示意真实应用中一般会结合curl_multi_perform的剩余句柄数和curl_multi_info_read判断传输是否全部完成。生产代码还应注意fd_count在两次调用之间可能变化新连接建立、连接复用释放等若第二次返回CURLM_OUT_OF_MEMORY应重新分配后重试。4. 源码实现从 pollset 到 curl_waitfd4.1 主流程lib/multi.ccurl_multi_waitfds的实现位于 lib/multi.c核心逻辑如下通过CURL_MAPI_ENTER/CURL_MAPI_LEAVE守卫struct Curl_mapi_guard做并发保护保证多线程场景下 API 调用的安全性。参数校验if(!ufds (size || !fd_count))返回CURLM_BAD_FUNCTION_ARGUMENT。初始化内部struct easy_pollset ps与struct Curl_waitfds cwfds后者封装用户数组与计数定义见 lib/select.hwfds指向用户数组、n为已填充数、count为容量。遍历multi-process一个 uint32 位集合记录待处理的 easy 句柄 ID对每个 easy 句柄调用Curl_multi_pollset(data, ps)收集其当前 socket 及读写关注事件再通过Curl_waitfds_add_ps(cwfds, ps)增量写入用户数组并累加need所需描述符数。追加关闭连接Curl_cshutdn_add_waitfds相关的描述符见 lib/cshutdn.c。容量判断if(need ! cwfds.n ufds)即所需数量超过已填充数量数组不够装时返回CURLM_OUT_OF_MEMORY。无论成败fd_count都会被写入need总需求数这正是传size0可以问个数的实现基础。4.2 事件映射lib/select.c描述符事件从内部 pollset 到公共curl_waitfd的映射位于 lib/select.c 的Curl_waitfds_add_psfor(i 0; i ps-n; i) { short events 0; if(ps-actions[i] CURL_POLL_IN) events | CURL_WAIT_POLLIN; if(ps-actions[i] CURL_POLL_OUT) events | CURL_WAIT_POLLOUT; if(events) need cwfds_add_sock(cwfds, ps-sockets[i], events); }内部CURL_POLL_IN/CURL_POLL_OUT动作被转换为公共的CURL_WAIT_POLLIN/CURL_WAIT_POLLOUT位掩码。值得注意的是cwfds_add_socklib/select.c会对重复 socket 做事件合并if(sock cwfds-wfds[i].fd) { wfds[i].events | events; return 0; }即同一 socket 同时关注读写时只出现一个条目因此fd_count返回的是去重后的描述符数量。这一行为也被测试用例明确覆盖见下节。另外可以推断与curl_multi_fdsetlib/multi.c共享同一个Curl_multi_pollset收集机制因此两种 API 拿到的 socket 集合是一致的只是输出形式不同——fd_setselect 模型与curl_waitfd数组poll 模型。5. 测试用例验证数量语义与错误路径lib/multi.c 的配套测试 tests/libtest/lib2405.c数据文件为 tests/data/test2405另有同族测试 tests/data/test2407专门验证curl_multi_waitfds的各种场景注释中明确列出了预期场景预期描述符数空 multi 句柄0 个描述符HTTP/1 两个传输无多路复用2 个描述符HTTP/2 两个传输无多路复用2 个描述符HTTP/2 启用多路复用CURLOPT_PIPEWAIT1 个描述符同一连接被合并去重同时测试还验证了错误路径非法参数如ufds与size组合不当返回CURLM_BAD_FUNCTION_ARGUMENT传入空ufds且size 0时返回所需描述符数量先问个数模式传入非空ufds但容量小于需求时返回CURLM_OUT_OF_MEMORY且fd_count返回大于等于实际需求的数值所有由 multi 句柄驱动的传输最终都成功完成。其中HTTP/2 多路复用只有 1 个描述符的结果正好印证了第 4.2 节的去重合并逻辑——两个 easy 句柄共享同一条 HTTP/2 连接时该连接的 socket 只被报告一次这正是curl_multi_waitfds相比简单累加 fd 的fd_set方案在事件驱动编程中更精确的优势。6. 与 curl_multi_wait / curl_multi_poll / curl_multi_fdset 的关系curl_multi_waitfds官方手册的 See-also 部分关联了四个函数docs/libcurl/Makefile.inc 中亦有登记curl_multi_wait(3)/curl_multi_poll(3)这两者在内部完成收集 fd 阻塞轮询 返回就绪数的全部工作调用方无需关心 fd 细节适合简单场景curl_multi_waitfds则把收集 fd这一步暴露出来适合已有事件循环epoll/kqueue/io_uring需要自行注册 socket 的架构。curl_multi_perform(3)无论用哪种等待方式一旦描述符就绪都必须调用curl_multi_perform()实际读写数据、推进传输状态机。curl_multi_fdset(3)select 模型的等价物返回三个fd_set与max_fdcurl_multi_waitfds是其 poll 模型对应物二者选一即可具体见 curl_multi_fdset.md。三者选型建议自定义事件循环用curl_multi_waitfds拿 fd 自己管想省事直接阻塞等待就用curl_multi_poll历史代码基于 select 则保留curl_multi_fdset。7. 实战要点小结先用size0问数量再分配数组curl_multi_waitfds(multi, NULL, 0, fd_count)返回需求数≥ 实际数据此malloc后再调用一次填充可避免CURLM_OUT_OF_MEMORY。注意fd_count是输出参数第二次调用传入的fd_count会被覆盖为实际填充数别把旧的容量值留在别处使用。容量不足返回CURLM_OUT_OF_MEMORY且不保证部分填充的可用性应重新分配更大数组后重试。事件语义用CURL_WAIT_POLLIN/CURL_WAIT_POLLOUT/CURL_WAIT_POLLPRI不要直接使用POLLIN等平台常量前者在 include/curl/multi.h 中定义跨平台一致。轮询到就绪后立即调用curl_multi_perform()该函数是传输推进引擎轮询只是叫醒机制。8.8.0 及以上版本才可用若需要兼容更早的 libcurl 版本请改用curl_multi_fdsetselect 模型或curl_multi_poll内部封装轮询或在编译期用版本宏做条件分支。【免费下载链接】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),仅供参考
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表