ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

libcurl CURLMOPT_NOTIFYDATA 详解:为 multi 通知回调传递自定义上下文指针

libcurl CURLMOPT_NOTIFYDATA 详解:为 multi 通知回调传递自定义上下文指针 libcurl CURLMOPT_NOTIFYDATA 详解为 multi 通知回调传递自定义上下文指针【免费下载链接】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_NOTIFYDATA是 libcurl 多接口multi interface中的一个选项用于向通过CURLMOPT_NOTIFYFUNCTION安装的通知回调传递一个自定义指针clientp让应用可以在回调中访问自己的上下文数据。本文以 CURLMOPT_NOTIFYDATA.md 为主线结合仓库中的 multi.h、multi.c 与 multi_ntfy.c 源码讲解该选项的原型、默认值、底层存储与分发路径、完整示例以及配合curl_multi_notify_enable的实战用法帮助你写出事件驱动的 libcurl multi 应用。选项一览CURLMOPT_NOTIFYDATA是 libcurl 8.17.0 版本引入的 multi 句柄选项适用于所有协议。它本身不做任何数据处理仅承担携带用户上下文的角色属性值选项名CURLMOPT_NOTIFYDATA类型CURLOPTTYPE_OBJECTPOINT对象指针枚举编号19见 multi.h默认值NULL返回值CURLM_OK引入版本8.17.0适用协议全部在 multi.h 中该选项与回调函数选项成对声明/* This is the notify callback function pointer */ CURLOPT(CURLMOPT_NOTIFYFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 18), /* This is the argument passed to the notify callback */ CURLOPT(CURLMOPT_NOTIFYDATA, CURLOPTTYPE_OBJECTPOINT, 19),函数原型#include curl/curl.h CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_NOTIFYDATA, void *pointer);调用方式与所有curl_multi_setopt一致第一个参数是curl_multi_init()返回的 multi 句柄第二个参数是选项名第三个参数是任意类型指针。libcurl 不会检查、复制或释放该指针指向的内容它只是原样存储、原样传回。语义libcurl 不触碰的 clientp根据官方文档的说明CURLMOPT_NOTIFYDATA设置的指针不被 libcurl 触碰仅作为通知回调的第四个参数clientp传入。通知回调的原型定义在 multi.htypedef void (*curl_notify_callback)(CURLM *m, unsigned int notification, CURL *easy, void *user_data);四个参数的职责参数含义CURLM *m触发本次通知的 multi 句柄unsigned int notification通知类型见下文CURL *easy与本次通知关联的 easy 句柄可能是内部句柄void *user_data即CURLMOPT_NOTIFYDATA设置的指针原样回传也就是说CURLMOPT_NOTIFYDATA与CURLMOPT_NOTIFYFUNCTION的关系等价于CURLMOPT_PUSHDATA与CURLMOPT_PUSHFUNCTION的关系函数指针决定回调做什么数据指针决定回调拿到什么上下文。源码实现指针如何存储与回传存储multi 句柄的 ntfy 结构在 multi_ntfy.h 中multi 句柄内保存通知机制状态的结构如下struct curl_multi_ntfy { curl_notify_callback ntfy_cb; void *ntfy_cb_data; struct mntfy_chunk *head; struct mntfy_chunk *tail; uint32_t flags; CURLMcode failure; };其中ntfy_cb_data字段专门存放CURLMOPT_NOTIFYDATA传入的指针。setopt直接存入 ntfy_cb_datamulti.c 中两个选项的解析逻辑相邻case CURLMOPT_NOTIFYFUNCTION: multi-ntfy.ntfy_cb va_arg(param, curl_notify_callback); break; case CURLMOPT_NOTIFYDATA: multi-ntfy.ntfy_cb_data va_arg(param, void *); break;可以看到实现极其简单va_arg取出指针后直接赋值没有校验、没有拷贝。这印证了文档中libcurl 不触碰该指针的描述——指针生命周期完全由调用方管理。回传dispatch 时作为第四个参数通知被触发后libcurl 在合适的时机统一分发。分发逻辑在 multi_ntfy.c 的mntfy_chunk_dispatch_all中if(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); }注意这行关键代码multi-ntfy.ntfy_cb_data就是CURLMOPT_NOTIFYDATA设置的指针原封不动传给回调。从源码结构可以推断libcurl 采用批量收集、统一分发的策略通知条目按 128 条一个 chunk 缓存见 multi_ntfy.c 的CURL_MNTFY_CHUNK_SIZE在 multi 处理周期中通过Curl_mntfy_dispatch_all一次性派发派发过程中新产生的通知会追加到队列尾部继续处理。默认值CURLMOPT_NOTIFYDATA的默认值为NULL。如果只设置回调而不设置数据指针回调的notifyp/user_data参数将为NULL访问前需要判空因此建议总是成对设置回调与数据。配套机制通知类型与开关数据指针本身没有含义它的意义取决于回调收到的通知类型。当前版本支持两种通知类型multi.h#define CURLMNOTIFY_INFO_READ 0 #define CURLMNOTIFY_EASY_DONE 1 #define CURLMNOTIFY_LAST 2 /* last, not used */CURLMNOTIFY_INFO_READ当 multi 句柄的消息栈从空变为非空时触发提示应用调用curl_multi_info_read读取消息。该通知只在消息加入空栈时触发一次回调应把消息全部读空后续新消息才会再次触发。CURLMNOTIFY_EASY_DONE某个 easy 句柄传输结束成功或失败时触发。注意这里传入的easy在启用 DoH 等特性时可能是 libcurl 的内部句柄而非应用自己的句柄。通知的收集需要显式开启。在 curl_multi_notify_enable.md 中说明只有同时满足安装了回调函数且该通知类型被 enable两个条件通知才会被收集并派发。对应实现是 multi_ntfy.c 中通过位掩码multi-ntfy.flags记录启用的类型CURLMcode 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.c 中实现为curl_multi_notify_enable与curl_multi_notify_disable两者对非法类型返回CURLM_UNKNOWN_OPTION。重复 enable 同一个类型不是错误disable 同样通过位运算清除对应位。通知的触发点散落在 multi 状态机中例如 multi.c 在 easy 句柄进入 DONE 状态时触发static void mstate_enter_done(struct Curl_easy *data, CURLMstate from_state) { (void)from_state; CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE); }CURLM_NTFY宏multi_ntfy.h会先检查回调是否已安装再通过Curl_mntfy_add将通知条目追加到队列避免在没有回调时产生任何开销。完整可运行示例下面把官方示例补全为可编译的完整程序演示CURLMOPT_NOTIFYDATA的典型用法把应用自定义结构体struct priv的指针交给回调回调中读取并打印。#include stdio.h #include curl/curl.h struct priv { void *ours; /* 应用自定义数据 */ int notify_count; /* 可扩展字段用于统计通知次数 */ }; /* 通知回调notifyp 即 CURLMOPT_NOTIFYDATA 传入的指针 */ 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); p-notify_count; /* ... 在这里处理业务逻辑 ... */ } int main(void) { struct priv setup {0}; CURLM *multi curl_multi_init(); /* 成对设置回调函数 回调数据 */ curl_multi_setopt(multi, CURLMOPT_NOTIFYFUNCTION, notify_cb); curl_multi_setopt(multi, CURLMOPT_NOTIFYDATA, setup); /* 开启需要的通知类型二者可同时开启 */ curl_multi_notify_enable(multi, CURLMNOTIFY_INFO_READ); curl_multi_notify_enable(multi, CURLMNOTIFY_EASY_DONE); /* ... 添加 easy 句柄并驱动 multi 循环 ... */ curl_multi_cleanup(multi); return 0; }运行要点成对设置CURLMOPT_NOTIFYFUNCTION与CURLMOPT_NOTIFYDATA应在驱动 multi 循环前设置好显式开启只设置回调还不够必须用curl_multi_notify_enable开启对应通知类型指针生命周期setup是栈上变量其生命周期必须覆盖整个 multi 使用期间直到curl_multi_cleanup完成。回调内可调用的 API 边界通知回调与其他 libcurl 回调不同它拥有更宽的使用权限。根据 CURLMOPT_NOTIFYFUNCTION.md 的说明除以下五个函数外回调内可以调用 multi 与 easy 句柄上的几乎所有方法包括向 multi 句柄添加或移除 easy 句柄curl_multi_performcurl_multi_socketcurl_multi_socket_actioncurl_multi_socket_allcurl_multi_cleanup同时官方文档强调该回调可能在任意时刻被调用甚至可能在所有传输结束之后、或在curl_multi_cleanup关闭缓存连接的过程中被调用。因此回调内不要假设此时没有传输在运行setup结构体的释放必须推迟到curl_multi_cleanup返回之后否则可能在清理阶段触发悬垂指针访问若回调中需要调用 libcurl API可参考 api.c 中关于allow_ntfy_cb的标记机制了解 libcurl 如何对回调期间的 API 调用做防护。类型检查与错误处理在 GCC/Clang 环境下curl_multi_setopt的变参会被 typecheck-gcc.h 中的宏检查CURLMOPT_NOTIFYDATA要求传入指针类型误传整型会在编译期告警。curl_multi_setopt(multi, CURLMOPT_NOTIFYDATA, pointer)正常情况下返回CURLM_OK值为 0若传入的option无法识别则返回CURLM_UNKNOWN_OPTION该选项始终被识别不会走到 multi.c 的 default 分支。建议对返回值做一次检查CURLMcode rc curl_multi_setopt(multi, CURLMOPT_NOTIFYDATA, setup); if(rc ! CURLM_OK) { fprintf(stderr, setopt failed: %d\n, rc); }典型应用场景结合数据指针与通知回调可以构建事件驱动的 multi 应用避免轮询curl_multi_info_read或反复检查状态传输完成通知开启CURLMNOTIFY_EASY_DONE在回调中通过curl_multi_info_read获取结果并立即添加新任务实现流水线式任务队列上下文关联easy句柄与回调之间没有直接的用户数据通道CURLMOPT_NOTIFYDATA提供的结构体可以作为共享状态如全局计数器、日志句柄、应用配置弥补这一缺口消息消费开启CURLMNOTIFY_INFO_READ收到通知后一次性把消息栈读空减少主循环中无谓的轮询开销。相关选项与接口CURLMOPT_NOTIFYFUNCTION与CURLMOPT_NOTIFYDATA成对使用的回调安装选项curl_multi_notify_enable开启指定通知类型curl_multi_notify_disable关闭指定通知类型相关头文件multi.h相关实现multi.c、multi_ntfy.c、multi_ntfy.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),仅供参考
返回列表