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
相关产品推荐
相关产品推荐

