libcurl之curl_multi_xxx接口

本文详细介绍了CURL库中curl_multi_setopt函数及其四个关键部分:CURLMOPT_PIPELINING用于配置HTTP管道和多路复用;curl_multi_perform处理非阻塞传输;curl_multi_wait轮询easyhandle;curl_multi_info_read读取传输信息。通过这些API,开发者可以优化HTTP请求并发和性能。
该文章已生成可运行项目,

一、curl_multi_setopt

原型:CURLMcode curl_multi_setopt(CURLM* multi_handle, CURLMoption option, param);
功能: 用于设置multi handle的选项。
可用版本库:7.15.4版本及以后。

  • CURLMOPT_PIPELINING
    启用HTTP管道和多路复用,值类型为:long
    用于设置multi handle是否启用HTTP的管道还是HTTP/2多路复用。
    启用后,libcurl将在对相同主机发起并行请求时使用这些协议特性。
    管道:意味着对于后续请求可以使用之前已经存在的连接,后续的请求将在同一个连接上进行“管道传输”(等待前一个请求完成),而不是并行执行。
    多路复用:意味着后续请求可以复用现有已经存在的连接,即在其他传输已经在使用该连接的情况下,可以多路复用发送新的请求。
    在7.43.0版本之前,该选项可以设置为1和0,用来启用和禁用HTTP/1.1的管道。
    从7.43.0版本开始,可以设置管道和多路复用。
    CURLPIPE_NOTHING (0)
    默认值,表示即不使用管道,也不使用多路复用。
    CURLPIPE_HTTP1 (1)
    如果设置为该值,libcurl将尝试把HTTP/1.1请求通过管道传输到已经建立并正在使用连接的主机。
    该选项将被删除,7.62.0版本以后无效。
    CURLPIPE_MULTIPLEX (2)
    如果设置为该值,libcurl将尝试在现有连接上进行多路传输。需要HTTP/2。7.62.0版本以后,该值为默认值。
    适用的协议:HTTP(S)。
    可用库版本:7.16.0版本及以后。
  • CURLMOPT_MAX_PIPELINE_LENGTH
    管道中请求的最大数量,值类型为:long
    用于设置HTTP/1.1管道连接中未完成的请求的最大数量。此选择只用于HTTP/1.1管道,不适用于HTTP/2多路复用。
    当达到限制值时,libcurl将使用另一个连接到同一个主机的另一个连接。或者将请求排队,直到主机的一个管道准备好接受请求为止。
    因此,当前可以处理的请求总数是:CURLMOPT_MAX_HOST_CONNECTIONS * CURLMOPT_MAX_PIPELINE_LENGTH
    默认值为5。
    适用的协议:HTTP(S)。
    可用库版本:7.30.0版本及以后。
  • CURLMOPT_MAX_TOTAL_CONNECTIONS
    最大同时打开连接数,值类型为:long
    用于设置multi handle可同时打开的最大连接总数。对于每个新会话,libcurl将打开一个新的连接,上限值由该值设置。
    当达到上限值时,会话将会挂起,直到有可用的连接为止。如果启用了CURLMOPT_PIPELINING,则libcurl将会尝试使用管道或多路复用。
    默认值为0,表示没有限制。
    适用的协议:所有。
    可用库版本:7.30.0版本及以后。
  • CURLMOPT_MAXCONNECTS
    设置连接缓存的大小,值类型为:long
    用于设置在multi handle使用结束后,libcurl可以在连接缓存中保留的同时打开的最大连接数。默认情况下,libcurl将会增大该值,使其4倍于已添加的easy handle数量。
    通过设置该选项,可以防止缓存大小增加超出我们所设置的限制。当缓存满了时候,curl将关闭缓存中最老的一个连接,防止增加打开的连接数量。
    适用的协议:所有。
    可用库版本:7.16.3版本及以后。
  • CURLMOPT_MAX_HOST_CONNECTIONS
    设置到单个主机的最大连接数,值类型为:long
    用于设置同时连接到某个主机(主机由主机名+端口确定)的最大连接数。
    对于主机的每个新会话,libcurl将会打开一个新连接,可打开的连接数上限值受到该值的限制。当达到上限值时,会话将会挂起,直到有可用的连接为止。
    如果启用了CURLMOPT_PIPELINING,libcurl将会尝试使用管道。
    默认值为0,表示没有限制。但是,为了向后兼容,当CURLMOPT_PIPELINING被设置为1时,该值设置为0将不会被视为无限制。
    相反地,它将只打开一个连接,并尝试在该连接上进行管道操作。
    该值的限制也用于代理连接,代理将被视为该限制计数的主机。
    适用的协议:HTTP(S)。
    可用库版本:7.30.0版本及以后。

二、curl_multi_perform

原型: CURLMcode curl_multi_perform(CURLM* multi_handle, int* running_handles);
功能: 从每个easy handle读取/写入有效数据。
说明: 此函数用于处理所有添加到multi handle上的需要以非阻塞方式处理的传输。
        当程序发现multi handle有可用数据时,或者超时已过,程序应该调用此函数用于读/写当前需要读或写的数据等。此函数在读/写完后立即返回。此函数不需要有实际的数据可读或者可写,只要有需要,它都可以被调用。参数running_handles用于存储当前仍在传输数据的easy handle数量。如果running_handles的数量与前一次调用相比发生了变化(或者小于添加到multi handle中easy handle的数量),那么可以知道有一个或者多个传输已经完成。然后可以调用curl_multi_info_read获取每个完成传输的消息,返回的消息包含CURLcode等信息。
        如果添加的easy handle很快就失败,那么它可能永远不会被视作runnable_handle。当running_handles在调用后被设置为0时,表示已经没有任何正在进行的传输。

三、curl_multi_wait

原型: CURLMcode curl_multi_wait(CURLM* multi_handle, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, int* numfds);
功能: 在multi handle中轮询所有的easy handle。
说明: 用于轮询指定的multi handle中所有easy handle所使用的文件描述符。它将阻塞,直到在至少一个easy handle上检测到活动,或者timeout_ms超时已过。
        另外,如果multi handle有一个内部的等待超时,它的时间如果比timeout_ms短,那么将会使用更短的内部等待超时,这是为了确保合理的保持超时准确性。可以传入额外的参数extra_fds,它是curl_waitfd结构体数组,类似pollpollfd结构体,用于在同个调用中等待。完成时,如果numfds是非NULL,那么它将填充发生感兴趣事件的文件描述符总数。这个数字包括了libcurl内部描述符和extra_nfds所提供的描述符。
        如果没有提供额外的文件描述符,并且libcurl中也没有等待的文件描述符,那么调用此函数将立即返回。在使用multi接口时,鼓励使用该函数用以替代select,这样可以使程序更容易规避最大文件描述符1024个的常见问题。
示例:

CURL* easy_handle;
CURLM* multi_handle;
curl_multi_add_handle(multi_handle, easy_handle);
CURLMcode mc;
int still_running;
int numfds;
do {
	mc = curl_multi_perform(multi_handle, &still_running);
	if (CURLM_OK == mc) {
		/* wait for activity, timeout or "nothing" */
		mc = curl_multi_wait(multi_handle, NULL, 0, 1000, &numfds);
	}
	if (CURLM_OK != mc) {
		fprintf(stderr, "curl_multi failed, code %d.\n", mc);
		break;
	}
	*/* 'numfds' being zero means either a timeout or no file descriptors to wait for. Try timeout on first occurrence, then assume no file descriptors and no file descriptors to wait for means wait for 100 milliseconds. */*
	if (!numfds) {
		repeats++; /* count number of repeated zero numfds */
		if (repeats > 1) {
			WAITMS(100); /* sleep 100 milliseconds */
		}
	} else {
		repeats = 0;
	}
} while(still_running);
curl_multi_remove_handle(multi_handle, easy_handle);

四、curl_multi_info_read

原型: CURLMsg* curl_multi_info_read( CURLM* multi_handle, int* msgs_in_queue);
功能: 读取multi stack信息。
说明: 询问multi handle是否有任务消息/信息可以从传输中获取。消息可能包含例如传输错误代码或者传输完成相关的信息。
        重复调用该函数每次都会返回一个新的结构体,直到返回一个NULL表示此时不需要再获取的信号。msgs_in_queue指向的int指针用于存放该函数此次调用结束后剩余的消息数量。
        当调用该函数获取消息时,它将从内部队列中删除,因此再次调用次函数将不会再次返回相同的消息。相反地,它将在每次新调用时返回新消息,直到队列被清空。
示例:

struct CURLMsg* m;
int msgq = 0;
do {
	m = curl_multi_info_read(multi_handle, &msgq);
	if (m && (CURLMSG_DONE == m->msg)) {
		CURL* e = m->easy_handle;
		curl_multi_remove_handle(multi_handle, e);
		curl_easy_cleanup(e);
	}
} while (m);
本文章已经生成可运行项目
<think>好的,我现在需要回答用户关于libcurlcurl_multi_fdset函数的作用的问题。用户之前已经问过关于select超时的问题,现在可能是在尝试使用libcurl的多接口处理网络请求,涉及到与select或类似系统调用的集成。首先,我要确保自己正确理解curl_multi_fdset的功能。 首先,我记得libcurlmulti接口是用来进行非阻塞、异步的多个并发传输的。而curl_multi_fdset这个函数,应该是用来获取当前multi句柄中所有活跃的文件描述符(sockets),以便用户可以将这些fd加入到select、poll或者epoll等I/O复用机制中。这样用户就可以在自己的事件循环中等待这些fd上的事件,当有事件发生时,再调用curl_multi_perform进行处理。 接下来,我需要详细解释这个函数的作用。可能的结构包括:函数的参数说明,返回的fd集合,以及它在事件循环中的使用流程。同时,用户可能想知道这个函数在整合libcurl到现有事件驱动架构中的重要性,比如如何与select配合使用,避免阻塞并提高效率。 需要注意的常见误区是,用户可能误以为必须使用curl_multi_fdset,但实际上在更高版本的libcurl中,可能推荐使用curl_multi_poll或curl_multi_wait,这些函数封装了内部的文件描述符管理,简化了使用流程。因此,可能需要指出这一点,避免用户使用过时的方法。 还要考虑用户可能的实际应用场景,比如在基于select的事件循环中如何正确调用curl_multi_fdset,如何处理返回的最大文件描述符值,以及超时时间的处理,如何结合curl_multi_timeout来获取等待时间。此外,可能需要提醒用户关于fd_set的大小限制,尤其是在处理大量连接时可能遇到的FD_SETSIZE限制问题,这可能导致需要使用更高效的I/O复用方法如epoll。 另外,我需要检查是否有过时的信息,例如旧版libcurl中的某些行为是否在最新版本中有所改变。比如,curl_multi_fdset在某些情况下可能返回-1,这时候用户应该依赖curl_multi_timeout提供的超时值,而不是立即进行select调用。 最后,要确保语言简洁明了,结构清晰,分点说明,可能还需要给出一个简单的代码示例来演示如何使用curl_multi_fdset与select结合,这样用户可以更直观地理解其作用和使用方法。</think>`curl_multi_fdset` 是 libcurl接口multi interface)中的一个关键函数,主要用于 **将 libcurl 内部管理的网络文件描述符(sockets)与应用程序的事件循环(如 `select`、`poll`、`epoll`)集成**。以下是它的核心作用和使用场景: --- ### **功能解析** 1. **获取监控的文件描述符集合** 当使用 libcurlmulti interface 发起多个并发网络请求时,libcurl 会管理底层所有的 sockets(如 HTTP、FTP 连接等)。`curl_multi_fdset` 的作用是提取这些 sockets 的 **读、写、异常** 文件描述符集合(`fd_set`),供应用程序通过 `select`/`poll` 等系统调用监控事件。 2. **返回最大文件描述符值** 函数会返回当前所有活跃 sockets 中最大的文件描述符值(`max_fd`),便于在 `select` 调用中设置监控范围,优化性能。 3. **与事件驱动循环配合** 应用程序通常会在自己的事件循环中调用 `curl_multi_fdset`,将返回的 `fd_set` 传递给 `select`/`poll`,等待网络事件就绪后,再调用 `curl_multi_perform` 驱动传输。 --- ### **函数原型** ```c CURLMcode curl_multi_fdset( CURLM *multi_handle, // multi 句柄 fd_set *read_fd_set, // 返回需要监控"可读"的 fd 集合 fd_set *write_fd_set, // 返回需要监控"可写"的 fd 集合 fd_set *exc_fd_set, // 返回需要监控"异常"的 fd 集合 int *max_fd // 当前所有 fd 的最大值 ); ``` --- ### **典型使用场景** ```c CURLM *multi_handle = curl_multi_init(); // 添加多个 easy handle(请求)到 multi_handle... while (running) { fd_set read_fds, write_fds, exc_fds; int max_fd = -1; FD_ZERO(&read_fds); FD_ZERO(&write_fds); FD_ZERO(&exc_fds); // 获取需要监控的 fd 集合 curl_multi_fdset(multi_handle, &read_fds, &write_fds, &exc_fds, &max_fd); // 获取建议的超时时间(单位:毫秒) long timeout_ms = -1; curl_multi_timeout(multi_handle, &timeout_ms); struct timeval timeout; if (timeout_ms >= 0) { timeout.tv_sec = timeout_ms / 1000; timeout.tv_usec = (timeout_ms % 1000) * 1000; } else { // 默认超时(避免永久阻塞) timeout.tv_sec = 1; timeout.tv_usec = 0; } // 调用 select 等待事件就绪 int rc = select(max_fd + 1, &read_fds, &write_fds, &exc_fds, &timeout); if (rc == -1) { // 处理错误(如 EINTR) } else { // 事件就绪后,驱动传输 int still_running = 0; curl_multi_perform(multi_handle, &still_running); // 处理完成的任务... } } ``` --- ### **注意事项** 1. **`max_fd` 为 -1 的情况** 当没有活跃的传输时,`max_fd` 可能返回 `-1`。此时应依赖 `curl_multi_timeout` 返回的超时时间,而非直接调用 `select`(避免阻塞整个循环)。 2. **超时时间建议** 配合 `curl_multi_timeout` 获取 libcurl 建议的等待时间,避免错过内部定时事件(如重试、超时逻辑)。 3. **多线程与并发** 在非线程安全的环境下,需确保 `curl_multi_fdset` 和 `curl_multi_perform` 在同一个线程中调用。 4. **替代方案** - libcurl 7.66.0+ 提供了更简化的 `curl_multi_poll` 和 `curl_multi_wait`,封装了事件监控逻辑,无需手动操作 `fd_set`。 - 若使用 `epoll` 或 `kqueue`,需自行管理 fd 的添加/删除,而非依赖 `curl_multi_fdset`。 --- ### **总结** `curl_multi_fdset` 是整合 libcurl接口到自定义事件循环的桥梁。它帮助开发者获取需要监控的网络文件描述符,结合 `select`/`poll` 实现高效异步 I/O。对于现代应用,建议优先考虑 `curl_multi_poll` 或更高层抽象(如 libevent 集成),以简化代码并避免 `fd_set` 的大小限制(如 `FD_SETSIZE`)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值