libcurl 多接口通知回调 CURLMOPT_NOTIFYFUNCTION 深入解析:事件驱动式多传输状态监测实战

发布时间:2026/9/10 7:46:44

libcurl 多接口通知回调 CURLMOPT_NOTIFYFUNCTION 深入解析:事件驱动式多传输状态监测实战 libcurl 多接口通知回调 CURLMOPT_NOTIFYFUNCTION 深入解析事件驱动式多传输状态监测实战【免费下载链接】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导读CURLMOPT_NOTIFYFUNCTION是 libcurl 多接口multi interface自 8.17.0 起引入的通知机制它允许应用程序注册一个回调函数在 multi handle 内部发生关键状态变化如某个 easy transfer 完成、有新的结果消息可供读取时被主动告知从而摆脱传统模式下不断轮询 multi handle才能感知变化的方式。本文以该选项的官方文档为骨架结合当前 curl 仓库中 include/curl/multi.h、lib/multi_ntfy.c、lib/multi_ntfy.h 与 lib/multi.c 的源码实现为你讲解回调原型、两种通知类型、启用/停用 API并给出可直接编译运行的完整示例。一、为什么需要通知回调在使用 libcurl 多接口时传统的事件循环通常由以下步骤组成调用curl_multi_socket_action()或curl_multi_perform()驱动传输调用curl_multi_info_read()主动询问是否有 transfer 完成、是否有新消息根据返回值决定下一步动作。这种主动询问模式要求应用在每个事件循环迭代里都去轮询 multi handle 的内部状态即使多数情况下根本没有变化。通知回调改变了这一交互方式当 multi handle 处理传输过程中发生变化时libcurl 会主动收集这些变化并在合适的时机把它们分发给应用注册的回调函数应用无需持续查询即可感知并做出响应。官方文档的原话是This can eliminate the need to constantly interrogate the multi handle to observe such changes to act on them.这可以消除为了观察并响应这些变化而不断询问 multi handle 的需要。二、回调原型与选项安装2.1 函数原型#include curl/curl.h void notify_callback(CURLM *multi, /* multi handle */ unsigned int notification, /* notification type */ CURL *easy, /* easy handle */ void *notifyp); /* private notify pointer */ CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_NOTIFYFUNCTION, notify_callback);在 include/curl/multi.h 中这个回调被正式定义为curl_notify_callback类型typedef void (*curl_notify_callback)(CURLM *m, unsigned int notification, CURL *easy, void *user_data);2.2 回调参数说明参数含义multi触发该通知的 multi handlenotification通知类型即发生了什么当前可用取值见下文未来可能新增更多类型easy与本次通知相关的 easy handle可能是应用自己的 easy handle也可能是 libcurl 内部创建的句柄notifyp应用自定义指针通过CURLMOPT_NOTIFYDATA设置与CURLMOPT_NOTIFYFUNCTION配套使用见 CURLMOPT_NOTIFYDATA 文档2.3 与普通回调的关键区别官方文档特别强调notify 回调与其他回调不同它可以在回调内部调用更多 libcurl API 函数。除了以下 5 个函数外它几乎可以调用 multi 与 easy handle 上的所有其他方法curl_multi_perform(3)curl_multi_socket(3)curl_multi_socket_action(3)curl_multi_socket_all(3)curl_multi_cleanup(3)这意味着你可以在 notify 回调里向 multi handle 添加或移除 easy handlecurl_multi_add_handle/curl_multi_remove_handle实现真正的响应式调度——某个传输完成的通知到来时立即在回调内安排下一个任务。2.4 调用时机任意时刻都可能被触发这是一个需要特别小心的特性该回调可能在应用与 libcurl 交互的任何时刻被调用。官方文档明确指出可能在所有传输都结束后才被触发甚至可能发生在curl_multi_cleanup()调用期间——当缓存的连接被关闭时curl_multi_cleanup会被动关闭空闲连接进而触发状态变化。因此回调实现必须足够轻量且自洽不要在回调内假设某个传输仍然存在也不要依赖当前调用栈之外的生命周期状态。三、通知类型详解当前可用的通知类型定义在 include/curl/multi.h#define CURLMNOTIFY_INFO_READ 0 #define CURLMNOTIFY_EASY_DONE 1 #define CURLMNOTIFY_LAST 2 /* last, not used */CURLMNOTIFY_LAST是哨兵值表示最后一个不使用同时充当类型数量的上限。在 lib/multi_ntfy.c 中有一个编译期约束CURLMNOTIFY_LAST必须小于等于 31以保证每种通知类型可以用一个 32 位标志位(uint32_t)1 type表示。3.1 CURLMNOTIFY_INFO_READ有新消息可读当通过curl_multi_notify_enable()启用后每当 multi handle 内部的消息栈从空变为非空时此通知会告诉应用现在可以调用curl_multi_info_read()来读取新消息了。关键语义官方文档原文此通知只在消息被添加到空消息栈时触发一次后续继续追加消息不会再次触发。应用应当在收到通知后把栈中所有可读消息全部取走、清空栈这样下一次有新消息加入时才会再次触发通知。如果应用每次只读一条消息那么后续消息就不会再触发通知可能造成消息滞留。实现上的对应关系在 lib/multi.c 的multi_addmsg()函数中static void multi_addmsg(struct Curl_multi *multi, struct Curl_easy *data) { if(Curl_uint32_bset_empty(multi-msgsent)) CURLM_NTFY(multi-admin, CURLMNOTIFY_INFO_READ); Curl_uint32_bset_add(multi-msgsent,>static void mstate_enter_done(struct Curl_easy *data, CURLMstate from_state) { (void)from_state; CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE); }static void mstate_enter_completed(struct Curl_easy *data, CURLMstate from_state) { ... if(from_state MSTATE_DONE) CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE); ... }从 lib/multi.c 的状态机跳转表可以看到mstate_enter_done挂在DONE状态、mstate_enter_completed挂在COMPLETED状态上正常流程进入DONE时触发一次通知若某些场景直接从更早的状态跳入COMPLETEDfrom_state MSTATE_DONE也会补发一次CURLMNOTIFY_EASY_DONE保证完成事件绝不遗漏。四、启用与停用curl_multi_notify_enable / disable设置回调本身CURLMOPT_NOTIFYFUNCTION只是登记具体哪种通知类型生效还需要显式启用。libcurl 提供了一对配套 API声明见 include/curl/multi.h实现在 lib/multi.cCURLMcode curl_multi_notify_enable(CURLM *m, unsigned int notification); CURLMcode curl_multi_notify_disable(CURLM *m, unsigned int notification);两个函数都返回CURLMcode传入不存在的类型notification CURLMNOTIFY_LAST时返回CURLM_UNKNOWN_OPTION见 lib/multi_ntfy.cCURLMcode Curl_mntfy_enable(struct Curl_multi *multi, unsigned int type) { if(type CURLMNOTIFY_LAST) return CURLM_UNKNOWN_OPTION; multi-ntfy.flags | CURL_MNTFY_TYPE_FLAG(type); return CURLM_OK; }内部实现是对multi-ntfy.flags这一 32 位位图做置位/清位每种类型对应一个独立位因此可以同时启用多种通知例如同时启用CURLMNOTIFY_INFO_READ和CURLMNOTIFY_EASY_DONE。在导出符号表 lib/libcurl.def 中也能看到这两个新 API 已被正式导出。五、通知的收集与分发源码级工作流程了解回调背后的机制有助于写出正确的回调。整个通知系统由 lib/multi_ntfy.c 与 lib/multi_ntfy.h 实现核心数据结构为struct curl_multi_ntfy见 lib/multi_ntfy.hstruct curl_multi_ntfy { curl_notify_callback ntfy_cb; /* 应用注册的回调 */ void *ntfy_cb_data; /* CURLMOPT_NOTIFYDATA 设置的指针 */ struct mntfy_chunk *head; /* 待分发通知队列头部 */ struct mntfy_chunk *tail; /* 待分发通知队列尾部 */ uint32_t flags; /* 启用类型位图 是否有待处理条目标志 */ CURLMcode failure; /* 队列分配失败等错误记录 */ };工作流程分三步第一步触发登记Curl_mntfy_add。传输状态变化时代码通过CURLM_NTFY宏lib/multi_ntfy.h把事件记录下来#define CURLM_NTFY(d, t) \ do { \ if((d) (d)-multi (d)-multi-ntfy.ntfy_cb) \ Curl_mntfy_add((d), (t)); \ } while(0)Curl_mntfy_add()lib/multi_ntfy.c会检查回调已注册、无失败状态、类型合法且已启用然后以struct mntfy_entry { uint32_t mid; uint32_t type; }为单位把通知追加到分块队列每块固定 128 条见CURL_MNTFY_CHUNK_SIZE中。注意此时并不会立即调用应用回调只是排队。第二步队列化管理。队列以 128 条一组的 chunk 链表形式组织lib/multi_ntfy.c避免每次通知都动态分配小块内存队列满时自动追加新 chunk。若内存分配失败会记录CURLM_OUT_OF_MEMORY到failure字段稍后返回给应用。第三步统一分发Curl_mntfy_dispatch_all。在合适的时机multi handle 处理完一批事件后例如 lib/multi.c 与 lib/multi.c 调用Curl_mntfy_dispatch_all(multi)libcurl 依次取出队列中的条目并调用应用回调lib/multi_ntfy.cif(data (multi-ntfy.flags CURL_MNTFY_TYPE_FLAG(e-type))) { /* this may cause new notifications to be added! */ CURL_TRC_M(multi-admin, [NTFY] dispatch %u to xfer %u, e-type, e-mid); multi-ntfy.ntfy_cb(multi, e-type, data, multi-ntfy.ntfy_cb_data); }分发过程中会做两件重要的事情回调用内添加新通知是允许的——源码注释明确写着 this may cause new notifications to be added!这可能导致新的通知被添加进来分发循环会持续处理到队列清空为止因此你在回调里调用curl_multi_add_handle()等操作是安全的分发时会再次检查该类型的启用位——即使事件已入队如果在分发前应用调用了curl_multi_notify_disable()关闭了该类型该条通知会被跳过。六、完整示例以下代码综合了官方文档示例并补全了启用通知、配套数据指针等关键步骤原型参照 include/curl/multi.h#include stdio.h #include curl/curl.h struct priv { void *ours; }; static void notify_cb(CURLM *multi, unsigned int notification, CURL *easy, void *notifyp) { struct priv *p notifyp; printf(notification%u, my ptr: %p\n, notification, p-ours); switch(notification) { case CURLMNOTIFY_INFO_READ: { /* 新消息可读一次性读空消息栈确保后续消息能再次触发通知 */ CURLMsg *msg; int msgs_left 0; while((msg curl_multi_info_read(multi, msgs_left))) { printf(msg: %s (%u remaining)\n, curl_easy_strerror(msg-data.result), msgs_left); } break; } case CURLMNOTIFY_EASY_DONE: /* 某个传输完成成功或失败都会触发。此处可安全调用 curl_multi_remove_handle / curl_multi_add_handle 安排下一个任务 */ printf(easy handle %p done\n, (void *)easy); break; default: break; } } int main(void) { struct priv setup; CURLM *multi curl_multi_init(); /* ... use socket callback and custom pointer ... */ /* 1. 注册通知回调 */ curl_multi_setopt(multi, CURLMOPT_NOTIFYFUNCTION, notify_cb); /* 2. 配套设置自定义指针回调的 notifyp 参数即取自这里 */ curl_multi_setopt(multi, CURLMOPT_NOTIFYDATA, setup); /* 3. 显式启用想要的通知类型可同时启用多种 */ curl_multi_notify_enable(multi, CURLMNOTIFY_INFO_READ); curl_multi_notify_enable(multi, CURLMNOTIFY_EASY_DONE); /* ... 添加 easy handle、驱动 multi 事件循环 ... */ curl_multi_cleanup(multi); return 0; }要点回顾CURLMOPT_NOTIFYDATA设置的指针会原样出现在回调的notifyp参数中与CURLMOPT_NOTIFYFUNCTION配合使用只用curl_multi_setopt注册回调还不够必须用curl_multi_notify_enable()启用具体类型后通知才会真正派发处理CURLMNOTIFY_INFO_READ时务必用循环curl_multi_info_read()读空消息栈。七、默认值与返回值DEFAULTNULL即默认不注册任何通知回调RETURN VALUEcurl_multi_setopt(multi, CURLMOPT_NOTIFYFUNCTION, ...)返回CURLM_OK表示设置成功CURLMcode 枚举定义于 include/curl/multi.h。八、可用性与注意事项该选项自libcurl 8.17.0起加入文档 front-matter 中的Added-in: 8.17.0适用于所有协议front-matter 中Protocol: All与传输协议无关该特性属于实验性新 API依赖 8.17.0 之后的 libcurl 版本编译前请确认链接的 libcurl 版本号curl_version_info()或curl-config --version回调可能在curl_multi_cleanup()期间因缓存连接关闭而被调用回调内避免访问已释放的资源回调携带的easy可能是内部句柄DoH 等场景不要假定它一定来自应用在回调内可以安全使用除curl_multi_perform(3)、curl_multi_socket(3)、curl_multi_socket_action(3)、curl_multi_socket_all(3)、curl_multi_cleanup(3)之外的所有 multi/easy API包括添加与移除 easy handle。相关文档CURLMOPT_NOTIFYDATA为通知回调设置自定义指针curl_multi_socket_actionmulti 接口事件驱动核心函数curl_multi_info_read读取完成消息栈curl_multi_performmulti 接口驱动函数multi interface 总览multi 接口编程模型通知机制核心实现lib/multi_ntfy.c、lib/multi_ntfy.h通知触发与启用/停用 APIlib/multi.c、include/curl/multi.h【免费下载链接】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),仅供参考
延伸阅读

更多相关文章

2026/9/10 7:46:44

SpringBoot+Vue在线装修管理系统设计与实现全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/10 8:31:53

onnxruntime部署实时视频帧插值:从模型导出到C++/Python落地

简介:这是一套基于ONNX Runtime的实时视频帧插值部署方案,核心面向算法工程师及C/Python开发者,解决视频流中生成中间帧、提升流畅度的工程落地问题。压缩包共收录13个文件,包含RIFE视频帧插值ONNX模型、C与Python两套推理源码、用…

2026/9/10 8:31:53

AI Agent开发实战:从核心原理到自动化测试与安全落地

最近一段时间,只要打开技术社区,第一屏大概率能看到Agent相关的内容。热搜词从pi agent、hermes agent到agent框架、gpt-6引爆agent代际跃迁预期,再到agent安全、agent面试题,几乎每隔几天就会出现一个新概念。我最早接触Agent还是…

2026/9/10 8:26:52

考虑源荷随机特征的热电联供微网优化Matlab实现

做热电联供微网优化有一段时间了,从最初的确定性调度一步步做到考虑源荷随机特征的随机优化,踩了不少坑,也积累了一些能直接用的经验。这篇就围绕“考虑源荷随机特征的热电联供微网优化研究(Matlab代码实现)”这个方向…

2026/9/9 13:11:35

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/8 7:15:15

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/10 0:00:55

目录对比去重实战:用哈希算法精准清理重复文件

我电脑里现在还有一块换了三次机的“数据墓地”硬盘,里面存着2016年以前所有旧笔记本的完整备份。平时不觉得有什么,直到前阵子想把它整理归档,发现同一个安装包、同一批照片、同一份论文草稿,在几个不同的备份目录里反复出现。更…

2026/9/10 0:00:55

Leaflet离线地图完整Demo合集:内网部署与坐标纠偏实战

简介:这是一份面向Web GIS开发者的LeafLet离线地图示例合集,帮助开发者快速掌握离线地图从搭建到交互的完整流程。压缩包共723个文件,大小14.06MB,以319个js脚本、175个html页面和29个css样式文件为主体,配合png/svg图…

2026/9/10 0:00:55

MATLAB读取Rinex 3.02观测文件:多系统GNSS数据解析实战

简介:基于MATLAB开发的Rinex3.02版观测文件(o文件)读取代码包,面向卫星定位导航方向的学习者与研究人员,用于解决新版观测文件的数据解析、历元提取与时间转换问题。压缩包共4个文件,包含两个m脚本、一个19…

2026/9/7 16:23:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/7 22:46:00

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/9 10:21:54

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码