libcurl提供了Easy和Multi两套接口,具体使用哪套接口取决于你的需求。
Easy接口是同步、高效、容易使用的文件传输接口,许多应用程序都基于这个接口来构建。
Multi接口是异步且在单线程或多线程中提供并发传输,具体如何使用这个接口请参看这里。
下面会简要介绍Easy接口如何使用。
一、主要接口
Easy主要接口如下:
curl_easy_init():初始化并获取一个handle。
curl_easy_setopt():设置传输选项。
curl_easy_perfrom():开始传输数据,由于是同步传输,因此,线程会在此阻塞。
curl_easy_getinfo():在传输回调过程中或传输结束之后获取相关信息。
curl_easy_cleanup():清除handle。
二、简单例程
使用Easy接口是:首先调用函数curl_easy_init初始化一个easy-session并得到一个handle,接着调用函数curl_easy_setopt设置传输选项,可以设置URL也可以设置数据可用时的回调函数,设置完毕后调用函数curl_easy_perfrom传输数据,该过程是同步的,即要么成功要么失败要么一直阻塞,该函数返回后,可以调用函数curl_easy_getinfo获取接收到的数据,最后调用函数curl_easy_cleanup清除handle。这里附上官方的sample.c:
#include
#include
int main(void)
{
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
/* example.com is redirected, so we tell libcurl to follow redirection */
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
/* always cleanup */
curl_easy_cleanup(curl);
}
return 0;
}
需要注意的是:不要在多线程之间共享同一个Easy handle,这不安全,原则上每个线程都应该有自己的Easy handle用于传输数据;Easy handle在完成一次数据通讯后可以被重用。
在编译前需要执行如下指令以确认是否安装必须的包:
#rpm -qa|grep curl
curl-7.15.5-9.el5_6.3
curl-devel-7.15.5-9.el5_6.3
libcurl提供了用于查询包括但不限于编译所需信息的工具curl-config,直接执行curl-config能得到所有功能参数:
#curl-config --cflags
预处理和编译参数。
#curl-config --libs
需要link或者支持的的库。
以sample.c为例可以如下来编译之:
#gcc -o simple simple.c -lcurl
也可以如下来链接所有库:
#gcc $(curl-config --libs) -o simple simple.c
执行上面操作后就编译出可执行文件simple了,执行simple会在屏幕打印出http://example.com的主页信息。
之所以这样是因为libcurl会默认提供一个用于接收数据的回调函数,该函数的默认规则是将接收到的数据打印到标准输出。
当然你也可以手动设置该函调函数以便于自己处理接收到的数据,要做到这点很简单,调用curl_easy_setopt接口函数设置接收数据的回调函数,回调函数原型如下:
size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp);
如下来注册回调函数:
curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, write_data);
还可以如下来给回调函数提供一个自定义参数,libcurl透传该参数:
curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, &internal_struct);
调用函数curl_easy_perfrom执行各种命令后开始接收数据,libcurl可能一次只接收到1字节的数据,也可能接收到好几K的数据,但这都是libcurl内部的行为,对于外部调用curl_easy_perfrom的程序来说是一直阻塞在该函数处,直到出错或者接收完成。
三、问题优化
Easy接口使用同步机制,无法像异步一样随意控制无压力,以下两种情况均可能引发curl_easy_perform阻塞卡死:
(1)下载某些大文件时,由于本地路由器流量控制,网络未断开,但是却无法接收到数据。
(2)下载过程中,网络异常中断。
解决方案:
(1)使用Multi接口。
Multi为异步接口,牺牲了一部分效率但确保了大部分灵活性,当然使用复杂度也相对上升。
(2)设置下载超时时间(CURLOPT_TIMEOUT)
设置超时时间后,curl会在超过此时间后无论正处于什么状态都会跳出堵塞,此种方案适用于小文件的下载,当然你也可以在超时之后重复连接以满足大文件的下载需求。
(3)使用回调函数返回abort value
此种方法是最简单方便的方法。一般这样操作, 首先设置进度回调函数,然后在回调函数里面判断某个时间内有没有接收到数据,如超时则返回非0值。
四、其它接口
此外,还提供了一些有用的接口函数,具体如下:
curl_version():返回一个指向libcurl版本号字符串的指针。
curl_getdate():转换日期字符串为time_t。
curl_formadd():构建多个POST表单数据。
curl_formfree():释放之前POST构建的数据。
curl_slist_append():构建链表curl_slist。
curl_slist_free_all():释放由curl_slist_append构建的curl_slist。
curl_easy_escape():URL编码一个字符串。
curl_easy_unescape():URL解码一个字符串。
更多信息请参见这里,要深入理解libbcurl请参看这里,也可以参看这里。