原文:http://www.cppprog.com/2009/1209/185.html
libcurl库是一个实现了各种客户端协议的网络编程库。目前它支持12种以上的协议,包括 FTP、HTTP、Telnet以及其他安全变体。
如果您有 10 年以上的脚本语言经验,您就会注意到它们的标记有很大的变化。Python、Ruby、Perl 等这些脚本语言不仅包含套接字层(C 或 C++ 中也有),还包含了应用层协议 API。这些脚本语言合并了高级功能,可以创建 HTTP 服务器或客户端。libcurl 库为 C 和 C++ 之类的语言添加了类似的功能,但是它可以在不同的语言之间移植。在所有它支持的语言中都能找到与 libcurl 相当的行为,但是由于这些语言的差异很大(设想一下 C 和 Scheme),提供这些行为的方式也很不相同。
libcurl 库以 API 的形式封装各种客户端协议,因此它可以被高级语言使用(如今已超过 30 种)。下面的示例研究使用 C 构建的简单 HTTP 客户端(适合构建 Web 爬行器)。
基于 C 的 HTTP 客户端
C API 在 libcurl 功能上提供了两个 API。easy 接口是一个简单的同步 API(意味着当您使用请求调用 libcurl 时,将能够满足您的请求,直到完成或发生错误)。多接口可以进一步控制 libcurl,您的应用程序可以执行多个同步传输,并控制 libcurl 何时何地移动数据。
该示例使用 easy 接口。该 API 还能控制数据移动过程(使用回调),但正如其名称所示,使用起来非常简单。清单 3 提供了 HTTP 的 C 语言示例。
使用 libcurl easy 接口的 C HTTP 客户端
- #include <stdio.h>
- #include <string.h>
- #include <curl/curl.h>
- #define MAX_BUF 65536
- char wr_buf[MAX_BUF+1];
- int wr_index;
- /*
- * Write data callback function (called within the context of
- * curl_easy_perform.
- */
- size_t write_data( void *buffer, size_t size, size_t nmemb, void *userp )
- {
- int segsize = size * nmemb;
- /* Check to see if this data exceeds the size of our buffer. If so,
- * set the user-defined context value and return 0 to indicate a
- * problem to curl.
- */
- if ( wr_index + segsize > MAX_BUF ) {
- *(int *)userp = 1;
- return 0;
- }
- /* Copy the data from the curl buffer into our buffer */
- memcpy( (void *)&wr_buf[wr_index], buffer, (size_t)segsize );
- /* Update the write index */
- wr_index += segsize;
- /* Null terminate the buffer */
- wr_buf[wr_index] = 0;
- /* Return the number of bytes received, indicating to curl that all is okay */
- return segsize;
- }
- /*
- * Simple curl application to read the index.html file from a Web site.
- */
- int main( void )
- {
- CURL *curl;
- CURLcode ret;
- int wr_error;
- wr_error = 0;
- wr_index = 0;
- /* First step, init curl */
- curl = curl_easy_init();
- if (!curl) {
- printf("couldn't init curl ");
- return 0;
- }
- /* Tell curl the URL of the file we're going to retrieve */
- curl_easy_setopt( curl, CURLOPT_URL, "www.exampledomain.com" );
- /* Tell curl that we'll receive data to the function write_data, and
- * also provide it with a context pointer for our error return.
- */
- curl_easy_setopt( curl, CURLOPT_WRITEDATA, (void *)&wr_error );
- curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_data );
- /* Allow curl to perform the action */
- ret = curl_easy_perform( curl );
- printf( "ret = %d (write_error = %d) ", ret, wr_error );
- /* Emit the page if curl indicates that no errors occurred */
- if ( ret == 0 ) printf( "%s ", wr_buf );
- curl_easy_cleanup( curl );
- return 0;
- }
最上方是必需的 include
文件,包括 cURL 根文件。接下来,我定义了两个用于传输的变量。第一个变量是 wr_buf
,表示将在其中写入传入数据的缓冲区。wr_index
表示缓冲区的当前写入索引。
转到 main
函数,该函数使用 easy API 进行设置。所有 cURL 调用都通过维护特定请求状态的句柄进行操作。这称为 CURL
指针引用。本例还创建一个特殊的返回码,称为 CURLcode
。在使用任何 libcurl 函数之前,您需要调用 curl_easy_init
获取 CURL
句柄。接下来,注意 curl_easy_setopt
调用的数量。它们为特定的操作配置句柄。对于这些调用,您提供句柄、命令和选项。首先,本例使用 CURLOPT_URL
指定要获取的 URL。然后,它使用 CURL_WRITEDATA
提供一个上下文变量(在本例中,它是内部的 write 错误变量)。最后,它使用 CURLOPT_WRITEFUNCTION
指定数据可用时应该调用的函数。在启动 API 之后,API 将使用它读取的数据多次调用该函数。
要开始传输,调用 curl_easy_perform
。它的工作是根据之前的配置执行传输。调用该函数时,在完成传输或发生错误之前该函数不会返回。main
的最后一步是提交返回状态,提交页面读取,最后使用 curl_easy_cleanup
清除(当使用句柄执行完操作后)。
现在看看 write_data
函数。该函数是针对特定操作收到数据时调用的回调。注意,当您从网站读取数据时,将写入该数据(write_data
)。将向回调提供一个缓冲区(包含可用数据)、成员数量和大小(缓冲中可用数据总量)、上下文指针。第一个任务是确保缓冲区(wr_buf
)的空间足以写入数据。如果不够,它将设置上下文指针并返回 0,表示出现问题。否则,它将 cURL 缓冲区的数据复制到您的缓冲区,并增加索引,指向要写入的下一个位置。本例还终止字符串,稍后可以对其使用 printf
。最后,它返回 libcurl 操作的字节数量。这将告诉 libcurl 数据被提取,它也可以丢弃该数据。这就是从网站将文件读取到内存的相对简单的方法。
评论