这是一个非常简单的 libev
和 libcurl
协作的例子。
libcurl Nonblocking Multi Interface
有时我们想在某个 event loop 中发送大量 http 请求,我们可以使用 libcurl 提供的 curl_multi_socket_action
机制来实现。要使用这个函数,需要设置两个主要选项: CURLMOPT_SOCKETFUNCTION
和 CURLMOPT_TIMERFUNCTION
。
CURLMOPT_TIMERFUNCTION
函数原型:
1 | int timer_callback(CURLM *multi, /* multi handle */ |
该函数被用于通知用户设置一个一次性的超时为 timeout_ms
的定时器。若 timeout_ms
的值是 -1 则代表用户可以移除该定时器。
参数 userp
可以通过 CURLMOPT_TIMERDATA
选项来设置。
CURLMOPT_SOCKETFUNCTION
函数原型:
1 | int socket_callback(CURL *easy, /* easy handle */ |
该函数被用于通知用户 libcurl 感兴趣的 socket 事件。 s
即是关心的 socket 描述符, what
就是需要监听的事件,可能的值有 CURL_POLL_IN
, CURL_POLL_OUT
, CURL_POLL_INOUT
和 CURL_POLL_REMOVE
。顾名思义,前三个值即是需要事件循环监听的 I/O 事件;而第四个值则是通知用户可以移除与 s
关联的事件监听器。
参数 userp
可以通过 CURLMOPT_SOCKETDATA
选项来设置。而 socketp
则是与 s
关联的自定义数据,初始值是 NULL
,可以通过 curl_multi_assign
函数来设置,有了这个参数可以更方便地将 event loop 的 I/O 事件监听器和 s
进行关联。
Implementation
我们将使用 libev
作为主事件循环,这样一来定时器即是 struct ev_timer
,I/O 监听器即是 struct ev_io
。
定时器超时或者监听的事件出现后,我们需要调用前述的 curl_multi_socket_action
来让 libcurl 进行下一步操作。
那么最初始的事件如何设置呢,在使用 curl_multi_add_handle
添加请求时会调用定时器的回调函数来设置一个超时为 0ms 的定时器,接下来直接启动事件循环即可。
若在 curl_multi_add_handle
之前没有设置 CURLMOPT_TIMERFUNCTION
,那么可以在之后手动调用 action 函数:
1 | curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, &running); |
或者直接手动注册一个超时为 0 的定时器,因为最终在定时器超时后也会调用上述函数。
Demo
Source Code
1 | /* file: curl-libev-demo.c */ |
Build & Run
1 | gcc curl-libev-demo.c -o demo -lcurl -lev |