cURL / Mailing Lists / curl-library / Single Mail


Re: CURLM Chain of events

From: Daniel Stenberg <>
Date: Fri, 22 Jan 2016 17:25:54 +0100 (CET)

On Fri, 22 Jan 2016, surf via curl-library wrote:

> So at what point does transfer kick-in exactly?
> curl_multi_init();
> curl_multi_setopt(curl_m_handle, CURLMOPT_SOCKETFUNCTION, on_socket_callback);
> curl_multi_setopt(curl_m_handle, CURLMOPT_TIMERFUNCTION, on_timer_callback);
> curl_easy_init();
> curl_easy_setopt(curl_e_handle, CURLOPT_WRITEDATA, (void *)file);
> curl_easy_setopt(curl_e_handle, CURLOPT_URL, url);
> curl_multi_add_handle(curl_m_handle, curl_e_handle);

At this point you're ready to go.

> curl_multi_socket_action(curl_m_handle, CURL_SOCKET_TIMEOUT, 0,
> &pending_handles);

When you did curl_multi_add_handle() above, it registered a very short timeout
in libcurl so when you do this CURL_SOCKET_TIMEOUT, libcurl will take care of
that timeout action internally. It will kick of the first things to get the
transfer going.

That will most likely involve telling your socket callback about one or more
sockets to wait for, and it will also most likely tell your timer callback
about a timeout value to wait for.

Your implementation of those callbacks tell your event system which sockets to
wait for and the timeout time.

Thus after that curl_multi_socket_action call, you should let your event loop

   event_loop_engine(); /* run until everything is done */

... and it should really soon detect socket activity and then you'll call
curl_multi_socket_action() again etc.

> What if I decide to add a new easy handle (do another transfer) after kick
> starting everything will the new transfer be initiated automatically or do I
> need to call call curl_multi_socket_action(..., CURL_SOCKET_TIMEOUT, 0, ...)
> again.

You can add and remove easy handles as you see fit and libcurl will update
your socket and timer callbacks accordingly.

> I really don't see how I can call curl_multi_socket_action since I don't
> have the socketfd for that easy_handle until socket_callback…

Right, but the callback will be called ASAP and then you have sockets to wait
for - and act on when they trigger.

> BTW I assume "easy handle" == transfer or can an "easy handle" represent
> multiple active transfers? (if so how would I distinguish among them in
> curl_multi_info_read())

easy handle == transfer, yes.

If you're feeling adventurous, you can try out reading these chapters in my
pending book about curl, starting here:

(The book is still work in progress so all kinds of feedback is appreciated)

>> You will find out that libcurl can very well use more than one socket per
>> transfer, so you most probably get several created and several removed
>> before the transfer is complete.
> In what scenario/protocol?

You shouldn't care. We do happy eyeballs so we might use two sockets for each
connection. We can use a socket for name resolving depending on the name
resolver backend. FTP uses two connections. And so on. Internal libcurl
decisions really.

> "At return, the integer running_handles points to will contain the number of
> running easy handles within the multi handle. When this number reaches zero,
> all transfers are complete/done"
> If I understand you correctly, what you're saying is that "running_handles"
> actually amounts to the total number of "sockets" associated with *this*
> transfer

It is the amount of currently "running transfers". That means all easy handles
within the multi handle whose transfers have not yet completed. The number of
sockets currently in use can be fewer or more than that number.

>> Quite simply: the timeout is necessary for when libcurl needs to act
>> without any detected socket acitivity. Like when someone cuts off your
>> internet connection somewhere in the middle and no more socket activities
>> happen.
> So the timeout is on receiving/sending bytes? Like libcurl expects to get
> 100 bytes within 10ms. If they don't arrive within that time it aborts the
> transfer (regardless of any particular socket state).

The timer_callback is to handle timeouts for libcurl when you're using the
multi_socket API.

Timeouts within libcurl are used for many things and you control most of them
with options set in the easy handles and they are specific then for each
transfer/easy handle. You can make them timeout if too slow. Or you can make
them timeout of they run too long. You decide.

>> No you shouldn't. If libcurl determines that the timeout isn't necessary
>> anymore (like after socket activity), it will call the timer callback and
>> say so.
> How does it say so? Didn't see any parameter for that ..


   A timeout_ms value of -1 means you should delete your timer.

>> If a transfer is done, successful or not, libcurl will tell you to
> But CURL_POLL_REMOVE is only per socketfd isn't it?

Yes, libcurl will tell you about each socketfd one call at a time to the
socket_callback. And yes, it can add up to a lot of calls if you do many
transfers... =)

> I mean as you pointed out transfer can be associated with several sockets. I
> might need to wait for several CURL_POLL_REMOVE

You shouldn't wait for that at all. When libcurl tells you to stop monitor a
socket with CURL_POLL_REMOVE you remove it from the collection of sockets you
monitor at once.

> and test if the entire transfer completed by calling curl_multi_info_read()
> after each CURL_POLL_REMOVE …

That's making it complicated. First, you can check if the 'running_handles'
counter (that curl_multi_socket_action provides) has changed. If the number
changes, it means one or more transfers have completed. Then you can call
curl_multi_info_read() to figure out which.

Or you can even just call curl_multi_info_read() unconditionally after every
call to curl_multi_socket_action() if you prefer that, as it is virtually just
a nop if no transfer completed.


List admin:
Received on 2016-01-22