例子
API
本文档试图描述使用libcurl编程时要考虑的一般原则和一些基本方法。该文本将主要关注C接口,也能够在其他类型的接口上得到应用,它们通常非常接近于C接口。
本文档将“用户(the user)”称为编写使用libcurl的源代码的人。通常将“程序(the program)”是用户编写的使用libcurl进行传输的收集源代码,该程序在libcurl之外。
要获得有关此处所述的选项和功能的更多详细信息,请参阅其详细手册页。
构建C程序有许多不同的方法。 本章将假设一个Unix风格的构建过程。 如果您使用其他构建系统,您仍然可以阅读此内容以获取可能适用于您的环境的一般信息。
您的编译器需要知道如何链接到libcurl。 因此,必须将编译器的include路径设置为指向安装它们的目录。 'curl-config'[3]工具可用于获取此信息:
$ curl-config --cflags
编译程序时,需要链接目标文件以创建单个可执行文件。 为了成功,您需要链接libcurl,也可能与libcurl本身依赖的其他库链接。与OpenSSL库一样,但即使命令行上也可能需要一些标准的OS库。 为了弄清楚要使用哪些文件,可再次使用'curl-config'工具:
$ curl-config --libs
libcurl可以通过多种方式构建和定制。 不同库和构建的不同之处在于是否需要支持基于SSL的传输,如HTTPS和FTPS。 如果在构建时检测到需要支持SSL的库,则将使用SSL支持构建libcurl。需要确定是否已在启用SSL支持的情况下构建已安装的libcurl,请使用'curl-config',如下所示:
$ curl-config --feature
如果支持SSL,则关键字“SSL”将写入stdout,可能还有一些其他功能可以打开或关闭以用于不同的libcurl。
另请参阅"Features libcurl Provides"。
当您编写配置脚本以相应地检测libcurl和相应的设置变量时,我们提供了一个预先编写的宏,可能会在此时执行您需要的所有操作。 请参阅docs / libcurl / libcurl.m4文件 - 它包含有关如何使用它的文档。
libcurl背后的人们付出了相当大的努力,使libcurl能够在大量不同的操作系统和环境中运行。
您可以在可运行libcurl的所有平台上以相同的方式编写libcurl。 只有很少的次要考虑因素需要考虑。 如果你只是确保编写足够的可移植代码,你可以很好地创建一个非常便携的程序。
该程序必须初始化一些libcurl功能。这意味着无论您打算使用多少次库,都应该“一次”完成。“一次”为您的程序的整个生命周期。使用以下方法:
curl_global_init()
它需要一个参数,它是一个位模式(bit pattern),告诉libcurl初始化什么。使用CURL_GLOBAL_ALL将使其初始化所有已知的内部子模块,并且可能是一个很好的默认选项。指定的当前两位是:
CURL_GLOBAL_WIN32
只能在windows上执行。当在Windows上使用时,它将使libcurl初始化win32 套接字的相关内容。如果没有正确初始化,您的程序将无法正确使用相关套接字。对于每个应用程序,只执行一次,因此如果您的程序已经执行此操作或使用其他库,则不应该执行此操作。
CURL_GLOBAL_SSL
只能在libcurls上编译和构建SSL的任何内容。在这些系统上,这将使libcurl为此应用程序正确初始化SSL库。这只需要为每个应用程序执行一次,因此如果您的程序或其他库已经执行此操作,则不需要执行此操作。
libcurl有一个默认的保护机制,可以检测 curl_easy_perform 被调用时是否被正常初始化。如果为否,libcurl会使用被猜测的位模式(bit pattern)运行函数本身。 请注意,这并未被考虑的很完善也不能很好的执行。
当程序不再使用libcurl时,它应该调用 curl_global_cleanup,这与init调用相反。 然后它将执行反向操作以清理curl_global_init所初始化的资源。
应避免重复调用curl_global_init和curl_global_cleanup。 在程序的生命周期中只会被调用一次。
如果可能的话,在运行时而不是在构建时确定libcurl功能是做好的方式。通过调用 curl_version_info 并检查返回结构的内容,可以确定当前运行的libcurl能支持的内容。
libcurl首先介绍了简易接口(easy interface)。简易接口中的所有操作都以'curl_easy'为前缀。 简易接口允许您使用同步和阻塞函数进行单次传输。
libcurl还提供了另一个接口,允许在单个线程中进行多个同时传输,即所谓的多接口(multi interface)。有关该接口的更多信息将在下一章单独详细介绍。 您仍然需要先了解简易接口,所以请继续阅读以便更好地理解相关内容。
要使用简易接口,您必须首先创建一个句柄。 对于要执行的每个相关会话,您需要一个句柄。 基本上,您应该为计划用于传输的每个线程使用一个句柄, 您绝不能在多个线程中共享相同的句柄。
使用如下方法:
easyhandle = curl_easy_init();
它返回一个简单句柄。 使用它,您可以继续执行下一步:设置首选操作。 句柄是即将进行的传输或一系列传输的逻辑实体。
您可以使用 curl_easy_setopt 为此句柄设置属性和选项,控制后续传输或传输的方式。 选择的结果仍保留在句柄中,直到再次设置为不同的值。使用相同句柄的多个请求将使用相同的选项。
如果您在任何时候想要重置句柄选项,您可以调用 curl_easy_reset ,您还可以使用 curl_easy_duphandle 复制一个句柄(包含所有设置选项)。
您在libcurl中设置的许多选项都是“字符串”,是一个零字节大小的指针。 当您使用 curl_easy_setopt 设置,libcurl会创建选项字符串的副本,以便在设置后不需要在应用程序中保留它们[4]。
在句柄中设置的最基本属性之一是URL。 您可以使用以下的方式设置首选URL:
curl_easy_setopt(handle,CURLOPT_URL,“http://domain.com/”);
让我们假设,URL标识了您想要获取的远程资源,编写了一种可用于此传输的应用程序,并希望直接将数据传递给您,而不是简单地将其传递给stdout。 所以,你编写自己的函数来匹配这个原型:
size_t write_data(void * buffer,size_t size,size_t nmemb,void * userp);
可以将此函数以参数传入以接收数据:
curl_easy_setopt(easyhandle,CURLOPT_WRITEFUNCTION,write_data);
也可以通过设置相关参数以直接获得的数据:
curl_easy_setopt(easyhandle,CURLOPT_WRITEDATA,&internal_struct);
使用该属性,您可以轻松地传递数据。 libcurl本身不会触及您使用CURLOPT_WRITEDATA传递的数据。
libcurl提供了默认回调方法,如果不使用 CURLOPT_WRITEFUNCTION 设置回调,它将处理数据。将接收的数据输出到stdout,您可以使用默认回调将数据写入不同的文件句柄,方法是将“FILE *”传递给使用CURLOPT_WRITEDATA选项打开的文件。
现在,在某些平台上[2],libcurl将无法对程序打开的文件进行操作。因此,如果您使用默认回调并使用CURLOPT_WRITEDATA传入打开的文件,它将崩溃。(CURLOPT_WRITEDATA以前称为 CURLOPT_FILE 。两个名称仍然有效并且做同样的事情)
如果您使用libcurl 作为 win32 DLL,如果设置CURLOPT_WRITEDATA,则必须使用CURLOPT_WRITEFUNCTION - 否则您的程序会崩溃。
其他:
success = curl_easy_perform(easyhandle);
curl_easy_perform 将连接到远程站点,执行必要的命令并接收数据。每当它接收数据时,它就会调用我们之前设置的回调函数。该函数可以一次获得一个字节,也可以一次获得多个千字节。你的回调函数应该返回它获取的字节数。如果和他传递的字节数不同,libcurl将中止操作并返回错误代码。
传输完成后,该函数返回一个返回代码,通知您是否成功执行了任务。如果返回代码不够,您可以使用 CURLOPT_ERRORBUFFER 将libcurl指向您的缓冲区,它也会存储可读的错误消息。
如果您想要传输另一个文件,则可以再次使用该句柄。请注意,如果您打算进行另一次传输,您甚至可以重新使用现有的手柄。然后libcurl将尝试重用以前的连接。
对于某些协议,下载文件可能涉及复杂的登录过程,设置传输模式,更改当前目录以及最后传输文件数据。 libcurl为您解决所有复杂问题。仅仅提供文件的URL,libcurl将处理所需的所有细节。
libcurl是线程安全的,但也有一些例外。 有关更多信息,请参阅libcurl-thread。
由于某种原因,传输总会失败。您可能设置了错误的选项或误解了选项实际执行的操作,或者远程服务器可能返回混淆库的非标准回复,这会使您的程序混乱。
当这些事情发生时,可执行以下操作:
将 CURLOPT_VERBOSE 选项设置为1.它将导致库发出它发送的整个协议详细信息,一些内部信息和一些接收到的协议数据(特别是在使用FTP时)。如果您正在使用HTTP,那么将接收到的输出中的标题添加到研究中也是一种聪明的方法,可以更好地理解服务器的行为方式。使用CURLOPT_HEADER设置1在普通正文输出中包含标题。
当然,还有漏洞。我们需要了解它们才能修复它们,因此我们完全依赖于您的错误报告!当您报告libcurl中的可疑错误时,请尽可能多地包含详细信息:CURLOPT_VERBOSE生成的协议转储,库版本,尽可能使用libcurl的代码,操作系统名称和版本,编译器名称和版本等等
如果 CURLOPT_VERBOSE 不够,则使用 CURLOPT_DEBUGFUNCTION 增加应用程序所接收的调试数据的级别。
深入了解所涉及的协议是不会错的,如果你想尝试做有趣的事情,至少简单地研究适当的RFC文档,你可能会很好地理解 libcurl ,如何更好地使用它。
libcurl尝试传输方法独立于传输协议,因此上传到远程FTP站点非常类似于使用PUT请求将数据上载到HTTP服务器。
当然,首先要么创建一个简单的句柄,要么重新使用一个现有的句柄。设置一个远程上传URL。
由于我们编写了一个应用程序,我们希望libcurl通过询问我们来获取上传数据。为了做到这一点,我们设置了read回调,自定义指针libcurl将传递给我们的read回调。读回调应该有一个类似于以下函数的原型:
size_t function (char * bufptr,size_t size,size_t nitems,void * userp);
其中bufptr是指向缓冲区的指针,我们填写要上传的数据,size * nitems是缓冲区的大小,因此也是我们可以在此调用中返回libcurl的最大数据量。 'userp'指针是我们设置的自定义指针,指向我们的结构,以在应用程序和回调之间传递私有数据。
curl_easy_setopt(easyhandle,CURLOPT_READFUNCTION,read_function);
curl_easy_setopt(easyhandle,CURLOPT_READDATA,&filedata);
告诉我们要上传的libcurl:
curl_easy_setopt(easyhandle,CURLOPT_UPLOAD,1L);
在没有事先了解预期文件大小的情况下完成上载时,一些协议将无法正常运行。因此,使用CURLOPT_INFILESIZE_LARGE 为所有已知文件大小设置上传文件大小,如[1]:
/ *在这个例子中,file_size必须是curl_off_t变量* /
curl_easy_setopt(easyhandle,CURLOPT_INFILESIZE_LARGE,file_size);
当你这次调用 curl_easy_perform 时,它将执行所有必要的操作,当它调用上传时,它将调用你提供的回调来获取要上传的数据。程序应该在每次调用中返回尽可能多的数据,因为这可能使上传尽可能快。会返回它在缓冲区中写入的字节数。返回0将表示上传结束。
许多协议使用要求提供用户名和密码以便能够下载或上传您选择的数据。 libcurl提供了几种指定它们的方法。
大多数协议都支持您在URL本身中指定名称和密码。 libcurl将检测到这一点并相应地使用它们。这是这样写的:
protocol://user:[email protected]/path/
如果您的用户名或密码中需要任何奇怪的字母,则应输入URL编码,如%XX,其中XX是两位十六进制数字。
libcurl还提供了设置各种密码的选项。嵌入在URL中的用户名和密码可以使用CURLOPT_USERPWD选项进行设置。传递给libcurl的参数应该是一个char *字符串,格式为“user:password”。以这种方式:
curl_easy_setopt(easyhandle,CURLOPT_USERPWD,“myname:thesecret”);
有时可能需要对自己使用的代理进行身份验证。 libcurl为此提供了另一种选择,即CURLOPT_PROXYUSERPWD。它与 CURLOPT_USERPWD选项非常相似,如下所示:
curl_easy_setopt(easyhandle,CURLOPT_PROXYUSERPWD,“myname:thesecret”);
存在很长时间的Unix“标准”方式来存储FTP用户名和密码,即在$ HOME / .netrc文件中。该文件应该是私有的,以便只有用户可以读取它(另请参阅“Security Considerations”一章),因为它可能包含纯文本密码。 libcurl能够使用此文件来确定用于特定主机的用户名和密码集。作为正常功能的扩展,libcurl还支持此文件用于非FTP协议,例如HTTP。要使curl使用此文件,请使用 CURLOPT_NETRC 选项:
curl_easy_setopt(easyhandle,CURLOPT_NETRC,1L);
以及这样一个.netrc文件的基本示例:
machine myhost.mydomain.com login userlogin password secretword |
所有这些例子都是密码是可选的情况,或者至少你可以把它留在外面并让libcurl试图在没有它的情况下完成它的工作。有时密码不是可选的,例如当您使用SSL私钥进行安全传输时。
要将已知的私钥密码传递给libcurl:
curl_easy_setopt(easyhandle,CURLOPT_KEYPASSWD,“keypassword”);
前一章介绍了如何设置用户名和密码以需要身份验证的URL。使用HTTP协议时,客户端可以通过许多不同的方式将这些凭据提供给服务器,您可以控制libcurl将(尝试)使用它们的方式。默认的HTTP身份验证方法称为“Basic”,它在HTTP请求中以明文形式发送名称和密码,base64编码。这是不安全的。
在撰写本文时,可以构建libcurl以使用:Basic,Digest,NTLM,Negotiate(SPNEGO)。您可以告诉libcurl与 CURLOPT_HTTPAUTH 使用哪一个,如:
curl_easy_setopt(easyhandle,CURLOPT_HTTPAUTH,CURLAUTH_DIGEST);
当您向代理发送身份验证时,您也可以使用相同的方式设置身份验证类型,而使用CURLOPT_PROXYAUTH:
curl_easy_setopt(easyhandle,CURLOPT_PROXYAUTH,CURLAUTH_NTLM);
这两个选项都允许您设置多种类型(通过将它们组合在一起),使libcurl选择服务器/代理支持的类型中最安全的类型。但是,此方法会添加往返,因为libcurl必须首先询问服务器它支持的内容:
curl_easy_setopt(easyhandle,CURLOPT_HTTPAUTH,CURLAUTH_DIGEST | CURLAUTH_BASIC);
为方便起见,您可以使用'CURLAUTH_ANY'定义(而不是具有特定类型的列表),它允许libcurl使用它想要的任何方法。
当要求多种类型时,libcurl将在其自己的内部优先顺序中选择它认为“最佳”的可用类型。
关于如何以正确的方式使用libcurl发出 POST请求,我们遇到了很多问题。因此,本章将包含使用libcurl支持的两种不同版本的HTTP POST的示例。
第一个版本是使用