curl是Linux下一个很是著名的下载库,经过这个库,能够很简单的实现文件的下载等操做。
看一个简单的例子:css
下面是转载的curl详细使用:html
curl->libcurl的手册能够查看node
http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTWRITEDATAapi
译者:JGood(http://blog.csdn.net/JGood )数组
译者注:这是一篇介绍如何使用libcurl的入门教程。文档不是逐字逐句按原文翻译,而是根据笔者对libcurl的理解,参考原文写成。文中用到的一 些例子,可能不是出自原文,而是笔者在学习过程当中,写的一些示例程序(笔者使用的libcurl版本是:7.19.6)。出如今这里主要是为了更好的说明 libcurl的某些api函数的使用。许多例子都参考libcurl提供的example代码。原文example中的提供的示例程序彻底使用C语言, 而这里笔者提供的例子使用C++语言。由于能力有限,对于libcurl的某些理解和使用可能有误,欢迎批评指正。浏览器
目标本文档介绍了在应用程序开发过程当中,如何正确使用libcurl的基本方式和指导原则。文档使用C语言来调用libcurl的接口,固然也适用于其余与C语言接近的语言。缓存
文档主要针对使用libcurl来进行开发的人员。文档所掼的应用程序泛指你写的源代码,这些代码使用了libcurl进行数据传输。安全
更多关于libcurl的功能和接口信息,能够在相关的主页上查阅。服务器
编译源码有不少种不一样的方式来编译C语言代码。这里使用UNIX平台下的编译方式。即便你使用的是其余的操做系统,你仍然能够经过阅读本文档来获取许多有用的信息。cookie
编译你的编译器必须知道libcurl头文件的位置。因此在编译的时候,你要设置头文件的包含路径。可使用curl-config工具来获取这方面的信息:
$ curl-config –cflags
连接编译完源码(这时的源代码不是指libcurl的源代码,你是你本身写的程序代码)以后,你还必须把目标文件连接成单个可执行文件。你要连接 libcurl库,以及libcurl所依赖的其余库,例如OpenSLL库。固然可能还须要一些其余的操做系统库。最后你还要设置一些编译选项,固然可 以使用curl-config工具简化操做:
$curl-config –libs
是否使用SSL定制编译libcurl。与其余库不一样的是,libcurl能够定制编译,根据实际须要是否支持某些特性,如是否支持SSL传输,像HTTPS和 FTPS。若是决定须要支持SSL,必须在编译时正确的设置。可使用’curl-config’来判断libcurl库是否支持SSL:
$ curl-config –feature
autoconf宏当你编写配置脚原本检测libcurl及其相应设置时,你可使用预约义宏。文档docs/libcurl/libcurl.m4告诉你如何使用这些宏。
跨平台的可移植的代码libcurl的开发人员花费很大的努力,使libcurl尽量在大多数平台上正常运行。
全局初始化应用程序在使用libcurl以前,必须先初始化libcurl。libcurl只需初始化一次。可使用如下语句进行初始化:
curl_global_init()接收一个参数,告诉libcurl如何初始化。参数CURL_GLOBAL_ALL 会使libcurl初始化全部的子模块和一些默认的选项,一般这是一个比较好的默认参数值。还有两个可选值:
CURL_GLOBAL_WIN32只能应用于Windows平台。它告诉libcurl初始化winsock库。若是winsock库没有正确地初始化,应用程序就不能使用socket。在应用程序中,只要初始化一次便可。
CURL_GLOBAL_SSL若是libcurl在编译时被设定支持SSL,那么该参数用于初始化相应的SSL库。一样,在应用程序中,只要初始化一次便可。
libcurl有默认的保护机制,若是在调用curl_easy_perform时它检测到尚未经过curl_global_init进行初始 化,libcurl会根据当前的运行时环境,自动调用全局初始化函数。但必须清楚的是,让系统自已初始化不是一个好的选择。
当应用程序再也不使用libcurl的时候,应该调用curl_global_cleanup来释放相关的资源。
在程序中,应当避免屡次调用curl_global_init和curl_global_cleanup。它们只能被调用一次。
libcurl提供的功能在运行时根据libcurl支持的特性来进行开发,一般比编译时更好。能够经过调用curl_version_info函数返回的结构体来获取运行时的具 体信息,从而肯定当前环境下libcurl支持的一些特性。下面是笔者在visual studio2008中调用相关函数获取libcurl版本信息的截图:
首先介绍libcurl中被称为easy interface的api函数,全部这些函数都是有相同的前缀:curl_easy 。
当前版本的libcurl也提供了multi interface,关于这些接口的详细使用,在下面的章节中会有介绍。在使用multi interface以前,你首先应该理解如何使用easy interface。
要使用easy interface,首先必须建立一个easy handle,easy handle用于执行每次操做。基本上,每一个线程都应该有本身的easy handle用于数据通讯(若是须要的话)。千万不要在多线程之间共享同一个easy handle。下面的函数用于获取一个easy handle :
CURL *easy_handle = curl_easy_init();
在easy handle上能够设置属性和操做(action)。easy handle就像一个逻辑链接,用于接下来要进行的数据传输。
使用curl_easy_setopt函数能够设 置easy handle的属性和操做,这些属性和操做控制libcurl如何与远程主机进行数据通讯。一旦在easy handle中设置了相应的属性和操做,它们将一直做用该easy handle。也就是说,重复使用easy hanle向远程主机发出请求,先前设置的属性仍然生效。
easy handle的许多属性使用字符串(以\0结尾的字节数组)来设置。经过curl_easy_setopt函数设置字符串属性时,libcurl内部会自动拷贝这些字符串,因此在设置完相关属性以后,字符串能够直接被释放掉(若是须要的话)。
easy handle最基本、最经常使用的属性是URL。你应当经过CURLOPT_URL属性提供适当的URL:
curl_easy_setopt(easy_handle, CURLOPT_URL, “http://blog.csdn.net/JGood “);
假设你要获取URL所表示的远程主机上的资源。你须要写一段程序用来完成数据传输,你可能但愿直接保存接收到的数据而不是简单的在输出窗口中打印它们。因此,你必须首先写一个回调函数用来保存接收到的数据。回调函数的原型以下:
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);
若是你没有经过CURLOPT_WRITEFUNCTION属性给easy handle设置回调函数,libcurl会提供一个默认的回调函数,它只是简单的将接收到的数据打印到标准输出。你也能够经过 CURLOPT_WRITEDATA属性给默认回调函数传递一个已经打开的文件指针,用于将数据输出到文件里。
下面是一些平台相关的注意点。在一些平台上,libcurl不能直接操做由应用程序打开的文件。因此,若是使用默认的回调函数,同时经过 CURLOPT_WRITEDATA属性给easy handle传递一个文件指针,应用程序可能会执行失败。若是你但愿本身的程序能跑在任何系统上,你必须避免出现这种状况。
若是以win32动态链接库的形式来使用libcurl,在设置CURLOPT_WRITEDATA属性时,你必须同时 使用CURLOPT_WRITEFUNCTION来注册回调函数。不然程序会执行失败(笔者尝试只传递一个打开的文件指针而不显式设置回调函数,程序并无崩溃。多是我使用的方式不正确。)。
固然,libcurl还支持许多其余的属性,在接下来的篇幅里,你将会逐步地接触到它们。调用下面的函数,将执行真正的数据通讯:
success = curl_easy_perform(easy_handle);
curl_easy_perfrom将链接到远程主机,执行必要的命令,并接收数据。当接收到数据时,先前设置的回调函数将被调用。libcurl可能一 次只接收到1字节的数据,也可能接收到好几K的数据,libcurl会尽量多、及时的将数据传递给回调函数。回调函数返回接收的数据长度。若是回调函数 返回的数据长度与传递给它的长度不一致(即返回长度 != size * nmemb),libcurl将会终止操做,并返回一个错误代码。
当数据传递结束的时候,curl_easy_perform将返回一个代码表示操做成功或失败。若是须要获取更多有关通讯细节的信息,你能够设置CURLOPT_ERRORBUFFER属性,让libcurl缓存许多可读的错误信息。
easy handle在完成一次数据通讯以后能够被重用。这里很是建议你重用一个已经存在的easy handle。若是在完成数据传输以后,你建立另外一个easy handle来执行其余的数据通讯,libcurl在内部会尝试着重用上一次建立的链接。
对于有些协议,下载文件可能包括许多复杂的子过程:日志记录、设置传输模式、选择当前文件夹,最后下载文件数据。使用libcurl,你不须要关心这一切,你只需简单地提供一个URL,libcurl会给你作剩余全部的工做。
下面的这个例子演示了如何获取网页源码,将其保存到本地文件,并同时将获取的源码输出到控制台上。
首先一个基本原则就是:绝对不该该在线程之间共享同一个libcurl handle,无论是easy handle仍是multi handle(将在下文中介绍)。一个线程每次只能使用一个handle。
libcurl是线程安全的,但有两点例外:信号(signals)和SSL/TLS handler。 信号用于超时失效名字解析(timing out name resolves)。libcurl依赖其余的库来支持SSL/STL,因此用多线程的方式访问HTTPS或FTPS的URL时,应该知足这些库对多线程 操做的一些要求。详细能够参考:
OpenSSL: http://www.openssl.org/docs/crypto/threads.html#DESCRIPTION
GnuTLS: http://www.gnu.org/software/gnutls/manual/html_node/Multi_002dthreaded-applications.html
NSS: 宣称是多线程安全的。
何时libcurl没法正常工做传输失败老是有缘由的。你可能错误的设置了一些libcurl的属性或者没有正确的理解某些属性的含义,或者是远程主机返回一些没法被正确解析的内容。
这里有一个黄金法则来处理这些问题:将CURLOPT_VERBOSE属性设置为1,libcurl会输出通讯过程当中的一些细节。若是使用的是http协议,请求头/响应头也会被输出。将CURLOPT_HEADER设为1,这些头信息将出如今消息的内容中。
固然不能否认的是,libcurl还存在bug。当你在使用libcurl的过程当中发现bug时,但愿可以提交给咱们,好让咱们可以修复这些bug。你在 提交bug时,请同时提供详细的信息:经过CURLOPT_VERBOSE属性跟踪到的协议信息、libcurl版本、libcurl的客户代码、操做系 统名称、版本、编译器名称、版本等等。
若是你对相关的协议了解越多,在使用libcurl时,就越不容易犯错。
上传数据到远程站点libcurl提供协议无关的方式进行数据传输。因此上传一个文件到FTP服务器,跟向HTTP服务器提交一个PUT请求的操做方式是相似的:
1. 建立easy handle或者重用先前建立的easy handle。
2. 设置CURLOPT_URL属性。
3. 编写回调函数。在执行上传的时候,libcurl经过回调函数读取要上传的数据。(若是要从远程服务器下载数据,能够经过回调来保存接收到的数据。)回调函数的原型以下:
bufptr指针表示缓冲区,用于保存要上传的数据,size * nitems是缓冲区数据的长度,userp是一个用户自定义指针,libcurl不对该指针做任何操做,它只是简单的传递该指针。可使用该指针在应用程序与libcurl之间传递信息。
4. 注册回调函数,设置自定义指针。语法以下:
5. 告诉libcurl,执行的是上传操做。
有些协议在没有预先知道上传文件大小的状况下,可能没法正确判断上传是否结束,因此最好预先使用CURLOPT_INFILESIZE_LARGE属性:告诉它要上传文件的大小:
6. 调用curl_easy_perform。
接下来,libcurl将会完成剩下的全部工做。在上传文件过程当中,libcurl会不断调用先前设置的回调函数,用于将要上传的数据读入到缓冲区,并执行上传。
下面的例子演示如何将文件上传到FTP服务器。笔者使用的是IIS自带的FTP服务,同时在FTP上设置了可写权限。
客户端向服务器发送请求时,许多协议都要求提供用户名与密码。libcurl提供了多种方式来设置它们。
一些协议支持在URL中直接指定用户名和密码,相似于: protocol://user:password@example.com/path/。libcurl能正确的识别这种URL中的用户名与密码并执行 相应的操做。若是你提供的用户名和密码中有特殊字符,首先应该对其进行URL编码。
也能够经过CURLOPT_USERPWD属性来设置用户名与密码。参数是格式如 “user:password ”的字符串:
(下面这几段文字我理解地模模糊糊)有时候在访问代理服务器的时候,可能时时要求提供用户名和密码进行用户身份验证。这种状况下,libcurl提供了另外一个属性CURLOPT_PROXYUSERPWD:
在UNIX平台下,访问FTP的用户名和密码可能会被保存在$HOME/.netrc文件中。libcurl支持直接从这个文件中获取用户名与密码:
在使用SSL时,可能须要提供一个私钥用于数据安全传输,经过CURLOPT_KEYPASSWD来设置私钥:
上一章介绍了如何在libcurl中,对须要身份验证的URL设置用户名与密码。在使用HTTP协议时,客户端有不少种方式向服务器提供验证信息。默认的 HTTP验证方法是”Basic”,它将用户名与密码以明文的方式、经Base64编码后保存在HTTP请求头中,发往服务器。固然这不太安全。
当前版本的libcurl支持的验证方法有:basic, Digest, NTLM, Negotiate, GSS-Negotiate and SPNEGO。(译者感叹:搞Web这么多年,尽然不知道这些Http的验证方式,实在惭愧。)能够经过CURLOPT_HTTPAUTH属性来设置具体 的验证方式:
向代理服务器发送验证信息时,能够经过CURLOPT_PROXYAUTH设置验证方式:
也能够同时设置多种验证方式(经过按位与), 使用‘CURLAUTH_ANY‘将容许libcurl能够选择任何它所支持的验证方式。经过CURLOPT_HTTPAUTH或 CURLOPT_PROXYAUTH属性设置的多种验证方式,libcurl会在运行时选择一种它认为是最好的方式与服务器通讯:
这一章介绍如何使用libcurl以Post方式向HTTP服务器提交数据。
方法一,也是最简单的方式,就像html中使用