I spent a few hours trying to figure out how to implement curl_multi_perform() in a general sort of way. This example may help out others.
It basically takes a struct object and places all curl output into that as a character string. The programmer can then take that character string and do whatever processing they like. If anyone has any improvements, etc I'd be more than happy to see them.
Here's a header file called "multicurl.h" with a struct and function prototypes.
#ifndef _MULTICURL_HEADER_H
#define _MULTICURL_HEADER_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
// A data structure used to hold the result of the cURL request.
typedef struct{
size_t size;
char *memory;
} MemType;
// Function Prototypes
void *SetUpCurlHandle(char*,MemType*);
void *SetUpMultiCurlHandle();
void *PerformMultiCurl();
#endif
Here's the source code file called multicurl.c with the function definitions.
#include "multicurl.h"
#define MAX_WAIT_MSECS 5*1000 /* Wait max. 5 seconds */
CURLM *mult_hnd = NULL;
int still_running = 0;
size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata){// cURL callback function [read in datastream to memory]
// This prototype is defined by cURL, with an argument at the end for our data structure.
// This function is repeatedly called by cURL until there is no more data in the data stream; *ptr.
size_t realsize = size * nmemb;// The number of bytes in the datastream [there is no NULL char]
MemType *mem = (MemType *)userdata;
char *tmp = realloc(mem->memory, mem->size + realsize + 1);// We add 1 for the NULL char.
if (tmp == NULL){
printf("Not Enough Memory, realloc returned NULL.\n");
exit(EXIT_FAILURE);
}
mem->memory = tmp;
memcpy(&(mem->memory[mem->size]), ptr, realsize);// Starting at the last element copy in datastream
mem->size += realsize;// The actual size is realsize + 1, however realsize gives us the location of the last element.
mem->memory[mem->size] = 0;// The datastream doesn't include a NULL char, so we zeroize the last element.
// We overwrite the NULL char {the zeroized element} on the next callback iteration, if any.
return realsize;// cURL crosschecks the datastream with this return value.
}
void *SetUpCurlHandle(char *url, MemType *output){// Take in a URL and a struct pointer address, set up curl easy handle.
CURL *hnd = NULL;
output->memory = malloc(1); // Initialize the memory component of the structure.
output->size = 0; // Initialize the size component of the structure.
// Initialize the cURL handle.
hnd = curl_easy_init();
if(hnd){
// Setup the cURL options.
curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 102400L);
curl_easy_setopt(hnd, CURLOPT_URL, url);// Set the request 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);// The callback function to write data to.
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, (void *)output);// Send the address of the data struct to callback func.
//curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1);
curl_multi_add_handle(mult_hnd, hnd);
}else{
output->memory[0] = '\0';
}
return NULL;// The output struct was passed by reference no need to return anything.
}
void *SetUpMultiCurlHandle(){// set up curl multi handle
curl_global_init(CURL_GLOBAL_ALL);
mult_hnd = curl_multi_init();
return NULL;
}
void *PerformMultiCurl(){// Request data from remote server asynchronously
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, "error: curl_multi_wait() returned %d\n", res);
return NULL;
}
curl_multi_perform(mult_hnd, &still_running);
/* if there are still transfers, loop! */
} 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 error code: %d\n", msg->data.result);
continue;
}
curl_multi_remove_handle(mult_hnd, hnd);
curl_easy_cleanup(hnd);
hnd = NULL;
}
else {
fprintf(stderr, "error: after curl_multi_info_read(), CURLMsg=%d\n", msg->msg);
}
}
curl_multi_cleanup(mult_hnd);
curl_global_cleanup();
return NULL;
}
Here is the main source code file called 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"; //Boston Weather
char* MyUrl2 = "https://api.weather.gov/stations/KLGA/observations/latest"; //NYC Weather
MemType MyOutputStruct1;
MemType MyOutputStruct2;
SetUpMultiCurlHandle();
SetUpCurlHandle(MyUrl1, &MyOutputStruct1);
SetUpCurlHandle(MyUrl2, &MyOutputStruct2);
PerformMultiCurl();
printf("Output:\n%s\n", MyOutputStruct1.memory);
printf("Output:\n%s\n", MyOutputStruct2.memory);
free( MyOutputStruct1.memory );
free( MyOutputStruct2.memory );
return 0;
}