Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do curl_multi_perform() asynchronously in C++?

I have come to use curl synchronously doing a http request. My question is how can I do it asynchronously?

I did some searches which lead me to the documentation of curl_multi_* interface from this question and this example but it didn't solve anything at all.

My simplified code:

CURLM *curlm;
int handle_count = 0;
curlm = curl_multi_init();

CURL *curl = NULL;
curl = curl_easy_init();

if(curl)
{
    curl_easy_setopt(curl, CURLOPT_URL, "https://stackoverflow.com/");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
    curl_multi_add_handle(curlm, curl);
    curl_multi_perform(curlm, &handle_count);
}

curl_global_cleanup();

The callback method writeCallback doesn't get called and nothing happens.

Please advise me.

EDIT:

According to @Remy's below answer, I got this but seems that it's not quite what I really needed. Cause using a loop is still a blocking one. Please tell me if I'm doing wrong or misunderstanding something. I'm actually pretty new to C++.

Here's my code again:

int main(int argc, const char * argv[])
{
    using namespace std;
    CURLM *curlm;
    int handle_count;
    curlm = curl_multi_init();

    CURL *curl1 = NULL;
    curl1 = curl_easy_init();

    CURL *curl2 = NULL;
    curl2 = curl_easy_init();

    if(curl1 && curl2)
    {
        curl_easy_setopt(curl1, CURLOPT_URL, "https://stackoverflow.com/");
        curl_easy_setopt(curl1, CURLOPT_WRITEFUNCTION, writeCallback);
        curl_multi_add_handle(curlm, curl1);

        curl_easy_setopt(curl2, CURLOPT_URL, "http://google.com/");
        curl_easy_setopt(curl2, CURLOPT_WRITEFUNCTION, writeCallback);
        curl_multi_add_handle(curlm, curl2);

        CURLMcode code;
        while(1)
        {
            code = curl_multi_perform(curlm, &handle_count);

            if(handle_count == 0)
            {
                break;
            }
        }
    }

    curl_global_cleanup();

    cout << "Hello, World!\n";
    return 0;
}

I can now do 2 http requests simultaneously. Callbacks are called but still need to finish before executing following lines. Will I have to think of thread?

like image 389
Thanh-Nhon Nguyen Avatar asked Jun 18 '14 14:06

Thanh-Nhon Nguyen


1 Answers

Read the documentation again more carefully, particularly these portions:

http://curl.haxx.se/libcurl/c/libcurl-multi.html

Your application can acquire knowledge from libcurl when it would like to get invoked to transfer data, so that you don't have to busy-loop and call that curl_multi_perform(3) like crazy. curl_multi_fdset(3) offers an interface using which you can extract fd_sets from libcurl to use in select() or poll() calls in order to get to know when the transfers in the multi stack might need attention. This also makes it very easy for your program to wait for input on your own private file descriptors at the same time or perhaps timeout every now and then, should you want that.

http://curl.haxx.se/libcurl/c/curl_multi_perform.html

When an application has found out there's data available for the multi_handle or a timeout has elapsed, the application should call this function to read/write whatever there is to read or write right now etc. curl_multi_perform() returns as soon as the reads/writes are done. This function does not require that there actually is any data available for reading or that data can be written, it can be called just in case. It will write the number of handles that still transfer data in the second argument's integer-pointer.

If the amount of running_handles is changed from the previous call (or is less than the amount of easy handles you've added to the multi handle), you know that there is one or more transfers less "running". You can then call curl_multi_info_read(3) to get information about each individual completed transfer, and that returned info includes CURLcode and more. If an added handle fails very quickly, it may never be counted as a running_handle.

When running_handles is set to zero (0) on the return of this function, there is no longer any transfers in progress.

In other words, you need to run a loop that polls libcurl for its status, calling curl_multi_perform() whenever there is data waiting to be transferred, repeating as needed until there is nothing left to transfer.

The blog article you linked to mentions this looping:

The code can be used like

Http http;
http:AddRequest("http://www.google.com");

// In some update loop called each frame
http:Update();

You are not doing any looping in your code, that is why your callback is not being called. New data has not been received yet when you call curl_multi_perform() one time.

like image 70
Remy Lebeau Avatar answered Oct 03 '22 12:10

Remy Lebeau