一、LIbcurl简单介绍
其实关于Liccurl的介绍最好的是看官方文档:http://curl.haxx.se/ 几乎大部分的信息里面都能够查找到。
在这边简要介绍:
1)跨平台特性,几乎所有平台都可以使用
2)有许多其他语言的包装,如PHP、PYTHON等,也就是很多语言都可以使用libcurl
3)Libcurl的所有接口被设计成线程安全(线程安全的意思是:在多线程之中可以同时调用一个API而不会互相影响,也就是函数可重入),另外要特别注意的是,任何一个libcurl的handle都不应该在多个线程之间共享,另外若使用HTTPS、FTPS需要OpenSSL或GnuTls的支持。
4)支持IPV6,前提是在编译的时候打开相应的选项
5)Libcurl性能很不错,但是若通过其他语言(非C语言)性能会有一定减弱,这和其他语言本身有关系
6)Libcurl提供三种handle:easy_handle、multi_handle、share_handle
easy_handle:为libcurl的最基础部分,所有的操作都是在easy_handle上进行的,比如发送、请求数据都是在其上进行的。如果直接在easy_handle执行操作 curl_easy_perform 函数是阻塞的(即需要等到完成才返回)
multi_handle:libcurl为异步操作提供的接口,允许调用方在一个线程中处理多个操作(就是easy_handle上的操作,注意是单线程下的),内部multi_handle采用堆栈的方式保存多个easy_handle,然后在一个线程中可以同时对多个easy_handle进行处理,multi_handle的执行操作 curl_multi_perform 函数是立即返回的,不会阻塞
share_handle:有时候多个easy_handle需要分享一些信息,比如cookie,当一个连接获取一个新的cookie,就可以将这个cookie共享到所有的连接上
二、一些应用实例
1、实现cookie共享
1)场景:客户端与服务器之间为了提高传输性能,建立了多个http连接。
服务区为了管理这个客户端的信息要使用一个会话来保存该客户端的一些信息,为了方便将会话信息保存在cookie之中。
当服务器检查到某个http连接没有带cookie或者cookie失效时,会自动设置一个新的cookie。
客户端希望当获取新的cookie时,马上生效到所有到该服务器的http连接上。
2)解决方案:使用libcurl提供的share_handle在多个http连接之间实现共享cookie操作
CURLSH *pShared = curl_share_init( ); ///创建一个share_handle
curl_share_setopt(pShared,CURLSHOPT_SHARE,CURL_LOCK_DATA_COOKIE); ///设置在盖share_handle上共享的cookie
CURL* pCurl = curl_easy_init();
curl_easy_setopt(pCurl, CURLOPT_SHARE, pShared); ///创建easy_handle并设置器share属性
curl_easy_setopt(pCurl,CURLOPT_COOKIEFILE,""); ///设置给easy_handle的连接添加上cookie支持
///可以以同样的方式添加多个easy_handle到该share_handle之中实现cookie共享,然后再easy_handle上执行的操作就能够自动共享cookie
2、实现HEAD请求
1)场景:有时候你想查询服务器某种资源的状态,比如某个文件的属性:修改时间,大小等等,但是并不像具体得到该文件,这时就是HEAD请求出场的时候
2)解决方案:起始使用libcurl很容易实现
主要设置该easy_handle的NOBODY属性即可
curl_easy_setopt(curl,CURLOPT_NOBODY ,1L ); ///告诉libcurl我想发起一个HEAD请求
3、实现断点下载
1)场景:当从服务器下载一个大文件时,可能需要相当长的时间来完成,在这过程中若出现网络超时或者客户端或者服务器宕机的情况时,若恢复时再从头开始下载势必浪费,这时可以使用从断点处下载。
2)解决方案:在HTTP的GET请求之中可以设置range头部告诉服务器要从指定位置取数据,libcurl如下:
curl_easy_setopt(curl, CURLOPT_RANGE,"100-"); //设置了该属性后,发送的GET请求,会有 “Range:100-“ 头部告诉服务器需要100字节后的数据
4、实现断点上传
1)场景:类似断点下载,就是向服务器传输了一部分数据后异常,当服务恢复时就可以使用断点上传
2)解决方案:需要两个步骤来实现断点上传,第一个不使用一个HEAD请求,查询服务器已经保存的该文件大小,服务器应该在HEAD应答的 Content-Length 头部中说明该文件服务器持有的大小,然后客户端在通过一个POST或者PUT请求并且设置 Content_Range 告诉服务器上传的位置。
发起HEAD请求上面已经叙述,这里特别之处在于需要读取服务器对HEAD应答报文的Content_Length部分,需要告诉libcurl你要读取header内容:
curl_easy_setopt(m_pCURL, CURLOPT_NOBODY, 1L); ///告诉libcurl发起HEAD请求
curl_easy_setopt(m_pCURL, CURLOPT_HEADERFUNCTION, pIoReadHeader_cb); ///告诉libcurl要读取应答报文的HEADER,当libcurl收到一个完整的header时就会调用该回调函数,其格式如下:int OnReadHeader(char *ptr, size_t size, size_t nmemb, void *userdata) 只要在回调函数中查看是否包含 Content-Length 字符串然后解析后面的内容即可。
curl_easy_setopt(m_pCURL, CURLOPT_WRITEHEADER, pPrivateData); ///设置私有数据,该数据会被传递到回调函数的 userdata 参数之中
///假如收到服务器返回的Content_Length为100,这时候POST时就只要从100字节开始上传,并且设置Content_Range头部标示出开始位置:
///注意格式: Content_Range:begin-end/size 以这里为例,假如文件大小为1000字节,从100开始那么Content_range应该就是 100-999/1000
curl_easy_setopt(curl, CURLOPT_RANGE, "100-999"); ///设置前面部分 100-999
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE,(curl_off_t)1000); ///设置size部分为1000
然后就可以使用WRITEFUNCTION等函数进行上传了。