You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言curl_multi_perform()通用实现可运行完整示例代码

cURL多请求通用实现示例

我花了数小时研究如何以通用方式实现curl_multi_perform(),这个示例或许能帮到有需要的开发者。
该示例的核心逻辑是将所有curl输出以字符串形式存入结构体对象,开发者拿到该字符串后可按需做任意处理,若有改进方案也欢迎交流。

1. 头文件 multicurl.h

包含结构体定义与函数原型:

#ifndef _MULTICURL_HEADER_H
#define _MULTICURL_HEADER_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>


// 用于存储cURL请求结果的数据结构
typedef struct{ 
    size_t size;
    char *memory;
} MemType;

// 函数原型
void *SetUpCurlHandle(char*,MemType*);
void *SetUpMultiCurlHandle();
void *PerformMultiCurl();

#endif

2. 实现文件 multicurl.c

包含函数具体实现:

#include "multicurl.h"

#define MAX_WAIT_MSECS 5*1000 /* 最大等待5秒 */

CURLM *mult_hnd = NULL;
int still_running = 0;

size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata){// cURL回调函数,将数据流读入内存
    // 该函数原型由cURL定义,最后一个参数传入我们自定义的数据结构
    // cURL会重复调用该函数直到数据流*ptr读取完毕
    size_t realsize = size * nmemb;// 数据流中的字节数,不包含NULL终止符

    MemType *mem = (MemType *)userdata;
    char *tmp = realloc(mem->memory, mem->size + realsize + 1);// 多申请1字节存放NULL终止符

    if (tmp == NULL){
        printf("内存不足,realloc返回NULL\n");
        exit(EXIT_FAILURE);
    }

    mem->memory = tmp;
    memcpy(&(mem->memory[mem->size]), ptr, realsize);// 从内存末尾开始拷贝数据流
    mem->size += realsize;// 实际占用内存为realsize+1,realsize标识最后一个有效字节的位置
    mem->memory[mem->size] = 0;// 数据流不含NULL终止符,我们手动在末尾补0
    // 若还有下一次回调,会覆盖本次补的0

    return realsize;// cURL会用返回值校验数据流是否完整
}

void *SetUpCurlHandle(char *url, MemType *output){// 传入URL和结构体指针,初始化curl easy句柄
    CURL *hnd = NULL;
    output->memory = malloc(1);              // 初始化结构体的内存字段
    output->size = 0;                        // 初始化结构体的长度字段

    // 初始化cURL句柄
    hnd = curl_easy_init();

    if(hnd){

        // 设置cURL参数
        curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 102400L);
        curl_easy_setopt(hnd, CURLOPT_URL, url);// 设置请求URL
        curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L);
        curl_easy_setopt(hnd, CURLOPT_USERAGENT, "curl/7.76.0");
        curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
        curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS);
        curl_easy_setopt(hnd, CURLOPT_FTP_SKIP_PASV_IP, 1L);
        curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);
        curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, write_callback);// 绑定数据写入的回调函数
        curl_easy_setopt(hnd, CURLOPT_WRITEDATA, (void *)output);// 将数据结构体地址传入回调函数
        //curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1);

        curl_multi_add_handle(mult_hnd, hnd);
    }else{
        output->memory[0] = '\0';
    }    
    return NULL;// 输出结构体以引用方式传入,无需返回值
}

void *SetUpMultiCurlHandle(){// 初始化curl multi句柄
    curl_global_init(CURL_GLOBAL_ALL);

    mult_hnd = curl_multi_init();
    return NULL;
}

void *PerformMultiCurl(){// 异步向远端服务器请求数据
    CURLMsg *msg=NULL;
    CURL *hnd = NULL;
    CURLcode return_code = 0;
    int msgs_left = 0;

    curl_multi_perform(mult_hnd, &still_running);
    do {
        int numfds=0;
        int res = curl_multi_wait(mult_hnd, NULL, 0, MAX_WAIT_MSECS, &numfds);
        if(res != CURLM_OK) {
            fprintf(stderr, "错误:curl_multi_wait()返回%d\n", res);
            return NULL;
        }
        curl_multi_perform(mult_hnd, &still_running);
        /* 还有未完成的传输就继续循环 */
    } while(still_running);

    while ((msg = curl_multi_info_read(mult_hnd, &msgs_left))) {
        if (msg->msg == CURLMSG_DONE) {
            hnd = msg->easy_handle;

            return_code = msg->data.result;
            if(return_code!=CURLE_OK) {
                fprintf(stderr, "CURL错误码:%d\n", msg->data.result);
                continue;
            }

            curl_multi_remove_handle(mult_hnd, hnd);
            curl_easy_cleanup(hnd);
            hnd = NULL;
        }
        else {
            fprintf(stderr, "错误:curl_multi_info_read()执行后,CURLMsg=%d\n", msg->msg);
        }
    }

    curl_multi_cleanup(mult_hnd);
    curl_global_cleanup();
    return NULL;
}

3. 使用示例 multicurlexample.c

编译命令:gcc -o multicurl -lcurl multicurlexample.c multicurl.c

#include "multicurl.h"

int main(){
    char* MyUrl1 = "https://api.weather.gov/stations/KBOS/observations/latest"; //波士顿天气
    char* MyUrl2 = "https://api.weather.gov/stations/KLGA/observations/latest"; //纽约天气
    MemType MyOutputStruct1;
    MemType MyOutputStruct2;

    SetUpMultiCurlHandle();

    SetUpCurlHandle(MyUrl1, &MyOutputStruct1);
    SetUpCurlHandle(MyUrl2, &MyOutputStruct2);

    PerformMultiCurl();

    printf("输出:\n%s\n", MyOutputStruct1.memory);
    printf("输出:\n%s\n", MyOutputStruct2.memory);

    free( MyOutputStruct1.memory );
    free( MyOutputStruct2.memory );
    
    return 0;
}

内容的提问来源于stack exchange,提问作者NorthStar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 19:06:00