简介:
libcurl是一个开源的、跨平台的网络传输库,它支持多种协议,包括HTTP、HTTPS、FTP、FTPS、TFTP、SCP、SFTP、SMB、SMBS、TELNET、DICT、LDAP、LDAPS、FILE、POP3、IMAP、SMTP、RTMP和RTMPS。libcurl库以其灵活性和易用性而闻名,它允许开发者轻松地在其应用程序中集成网络通信功能,本章我们先对libcurl库进行一个详细的分析与学习。
一、初始libcurl库
1.1 什么是libcurl
libcurl 是一个强大的跨平台库,用于获取 URL 语法的数据传输。它支持多个协议并提供了丰富的功能,使开发者能够方便地进行网络请求,libcurl 是由 Daniel Stenberg 开发和维护的开源库,广泛用于各种应用程序中进行网络通信。libcurl 提供了简单易用的 API,使开发者能够轻松地进行各种网络操作。
libcurl的官网:curl
图1.1 libcurl库主页
库下载:Release 7.71.1 · curl/curl · GitHub
图1.2 所需要的压缩包
1.2 libcurl支持的网络协议
libcurl 支持多种网络协议,包括但不限于:
- HTTP(HyperText Transfer Protocol)
- HTTPS(HTTP Secure)
- FTP(File Transfer Protocol)
- FTPS(FTP Secure)
- Gopher
- Telnet
- DICT(Dictionary Server Protocol)
- FILE(文件协议)
- LDAP(Lightweight Directory Access Protocol)
- SMTP(Simple Mail Transfer Protocol)
- POP3(Post Office Protocol version 3)
- IMAP(Internet Message Access Protocol)
1.3 主要功能
- HTTPS 证书授权:
- 支持 SSL/TLS 证书验证,确保 HTTPS 请求的安全性。
- 可以指定 CA 证书文件或目录来进行服务器证书的验证。
- HTTP POST:
- 支持通过 HTTP POST 方法发送数据,常用于提交表单数据或上传文件。
- HTTP PUT:
- 支持通过 HTTP PUT 方法上传数据到服务器,通常用于文件上传。
- FTP 上传:
- 支持通过 FTP 协议上传文件到 FTP 服务器。
- HTTP 基本表单上传:
- 支持 multipart/form-data 编码的表单上传,适用于文件和数据混合上传。
- 代理支持:
支持通过代理服务器进行网络请求,可以设置 HTTP、HTTPS、FTP 等协议的代理。 - Cookies 管理:
- 支持自动处理和管理 Cookies,能够从请求中保存和加载 Cookies,以便维护会话状态。
- 用户认证:
- 支持多种认证方式,包括基本认证(Basic)、摘要认证(Digest)、NTLM、Negotiate(GSS-API/Kerberos)。
- 提供方便的方法设置用户名和密码进行认证。
二、 libcurl的使用
2.1 使用 libcurl 的基本步骤
- 初始化 libcurl 环境:
使用 curl_global_init() 初始化 libcurl 库。这是 libcurl 的全局初始化函数,通常在程序开始时调用一次。 - 获取 easy interface 指针:
使用 curl_easy_init() 函数获取一个 easy interface 指针,用于后续的配置和执行。 - 设置传输选项:
使用 curl_easy_setopt() 函数设置传输选项,例如 URL、回调函数等。这些选项用于指定请求的参数和行为。 - 实现回调函数:
根据需要,实现回调函数以处理数据。例如,使用 CURLOPT_WRITEFUNCTION 设置的回调函数用于处理接收到的数据。 - 执行传输任务:
使用 curl_easy_perform() 函数执行传输任务,发送请求并接收响应。 - 释放资源:
使用 curl_easy_cleanup() 释放 easy interface 相关的资源。
2.2 示例代码
以下是使用 libcurl 进行 HTTP GET 请求的示例代码(C 语言):
#include <curl/curl.h> #include <stdio.h> // 回调函数,用于处理接收到的数据 size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata) { fwrite(ptr, size, nmemb, (FILE *)userdata); return size * nmemb; } int main(void) { CURL *curl; CURLcode res; FILE *fp; // 初始化 libcurl 库 curl_global_init(CURL_GLOBAL_DEFAULT); // 获取 easy interface 指针 curl = curl_easy_init(); if(curl) { // 打开文件用于保存响应数据 fp = fopen("output.html", "wb"); // 设置 URL curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com"); // 设置回调函数,用于处理接收到的数据 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); // 设置回调函数的参数 curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); // 执行传输任务 res = curl_easy_perform(curl); // 检查传输是否成功 if(res != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } // 关闭文件 fclose(fp); // 释放 easy interface 相关资源 curl_easy_cleanup(curl); } // 释放 libcurl 全局资源 curl_global_cleanup(); return 0; }
2.3 详细介绍:
1. 初始化 libcurl 环境:这一步初始化了 libcurl 的全局环境,确保库能够正常工作。
curl_global_init(CURL_GLOBAL_DEFAULT);
2. 获取 easy interface 指针:curl_easy_init() 返回一个 CURL 类型的指针,用于后续的配置和传输操作。
curl = curl_easy_init();
3. 设置传输选项:这些设置选项指定了请求的 URL、用于处理响应数据的回调函数和传递给回调函数的用户数据。
// 设置 URL: 这个选项指定了要请求的 URL curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com"); // 设置回调函数: 这个选项指定了接收数据时要调用的回调函数 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); // 设置回调函数的参数: 这个选项指定了要传递给回调函数的数据,在这里是文件指针 curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
4. 实现回调函数:这个回调函数每次接收到数据时都会被调用,将数据写入到文件中。
size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata) { fwrite(ptr, size, nmemb, (FILE *)userdata); return size * nmemb; }
5. 执行传输任务:curl_easy_perform() 执行传输任务,发送请求并接收响应。如果传输失败,会返回错误码。
// 执行传输任务: 发送请求并接收响应数据 res = curl_easy_perform(curl);
6. 释放资源:这些函数用于释放 curl_easy_init() 和 curl_global_init() 分配的资源。
curl_easy_cleanup(curl); curl_global_cleanup();
你可以使用 libcurl 轻松地进行 HTTP 请求和处理响应数据。libcurl 提供了丰富的配置选项和回调机制,能够满足各种网络通信的需求。
四、libcurl 开发库的配置安装
4.1 下载上传压缩开发库
将libcurl开发库的压缩包curl-7.71.1.tar.gz下载并且上传到ubuntu的~/httpHandler中
打开README:
遇到问题一定要去看INSTALL.md,这里介绍了如何去安装
4.2 进行库的配置安装
查看开发库相关配置:
book@100ask:~/httpHandler/curl-7.71.1$ ./configure --help
用于交叉编译链的配置:
System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD]
进行安装配置:
book@100ask:~/httpHandler/curl-7.71.1$ ./configure --prefix=$PWD/_install
配置完成,但是没有安装部分库 ,使用make进行编译
此时编译成功,但是还是没有看到,_install,我们需要进行下一步,执行make install
编译完成后会生成一个_install
进入_install查看相关文件
五、 libcurl 开发库的编程实战
5.1 调用libcurl编程访问百度和邮箱
#include <stdio.h> #include <curl/curl.h> #include <stdbool.h> // 使用 libcurl 发送 GET 请求并将结果存储到文件中 bool getUrl(char *filename) { CURL *curl; CURLcode res; FILE *fp; // 打开文件用于存储结果 if ((fp = fopen(filename, "w")) == NULL) { return false; } // 初始化 HTTP 头列表 struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Accept: Agent-007"); // 添加自定义头 // 初始化 libcurl curl = curl_easy_init(); if (curl) { // 设置代理(如果需要) // curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:8080"); // 设置自定义 HTTP 头 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // 设置请求 URL curl_easy_setopt(curl, CURLOPT_URL, "http://www.baidu.com"); // 设置将返回的 HTTP 头和主体数据输出到文件 curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); // 输出 HTTP 头 curl_easy_setopt(curl, CURLOPT_HEADERDATA, fp); // 输出 HTML 主体数据 // 执行请求 res = curl_easy_perform(curl); // 检查请求是否成功 if (res != 0) { // 如果请求失败,释放资源 curl_slist_free_all(headers); curl_easy_cleanup(curl); } // 关闭文件 fclose(fp); return true; } // 如果初始化失败,关闭文件 fclose(fp); return false; } // 使用 libcurl 发送 POST 请求并将结果存储到文件中 bool postUrl(char *filename) { CURL *curl; CURLcode res; FILE *fp; // 打开文件用于存储结果 if ((fp = fopen(filename, "w")) == NULL) { return false; } // 初始化 libcurl curl = curl_easy_init(); if (curl) { // 设置 cookie 文件 curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt"); // 设置 POST 数据 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "&logintype=uid&u=xieyan&psw=xxx86"); // 设置代理(如果需要) // curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:8080"); // 设置请求 URL curl_easy_setopt(curl, CURLOPT_URL, "http://mail.sina.com.cn/cgi-bin/login.cgi"); // 设置将返回的数据输出到文件 curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); // 执行请求 res = curl_easy_perform(curl); // 清理 libcurl 资源 curl_easy_cleanup(curl); } // 关闭文件 fclose(fp); return true; } int main(void) { // 发送 GET 请求并将结果存储到 /tmp/get.html 文件 getUrl("/tmp/get.html"); // 发送 POST 请求并将结果存储到 /tmp/post.html 文件 postUrl("/tmp/post.html"); return 0; }
以上代码是C++代码,bool类型在C语言中没有定义,所以我们如果要想用gcc进行编译,我们需要将bool进行重新定义。
加上此定义即可用gcc进行编译
5 #define true 1 6 #define flase 0
book@100ask:~/httpHandler$ gcc demo1.c -I ./curl-7.71.1/_install/include/
此时又出现了新的问题,这些函数没有定义,不能使用。
要解决此问题,我们需要把curl库列出来
book@100ask:~/httpHandler$ gcc demo1.c -I ./curl-7.71.1/_install/include/ -L ./curl-7.71.1/_install/lib/ -lcurl
用a.out 运行执行程序,如果出现以下错误,则是没有配置环境变量
其中lib就是你的libx.so所依赖的第三方库。如果对方机器上没有对应共享库的话,此时就需要使用“export LD_LIBRARY_ PATH”进行设置。例:
exgort LD_LIBRARY_PATH=./curl-7.71.1/_install/lib/
这里我们就可以得到百度官网的代码了
book@100ask:~/httpHandler$ vi /tmp/get.html
这里我们也可以查看邮箱的访问信息
book@100ask:~/httpHandler$ vi /tmp/post.html
5.2 常用字段解读并设置数据读取回调函数
5.2.1.curl_global_init()
- 这个函数用于全局初始化 libcurl 库,确保库的所有内部数据结构和资源都已准备好。
- 需要注意的是,这个函数在整个程序生命周期中只能调用一次。虽然可以在调用 curl_global_cleanup 后再次调用,但在多线程环境中,应该在主线程中进行全局初始化,而不是在每个线程中都调用它。
- 如果在调用 curl_easy_init 函数之前没有调用 curl_global_init,libcurl 会自动调用它,但这在多线程环境中可能导致竞态条件,因此最好显式调用它。
参数 flags
- URL_GLOBAL_ALL: 初始化所有可能的调用。
- CURL_GLOBAL_SSL: 初始化对安全套接字层的支持。
- CURL_GLOBAL_WIN32: 初始化 Win32 套接字库。
- CURL_GLOBAL_NOTHING: 没有额外的初始化。
5.2.2. curl_global_cleanup( );
虽然 libcurl 是线程安全的,但 curl_global_init 和 curl_global_cleanup 这两个函数不是线程安全的,因此,这两个函数应该在主线程中调用,而不是在每个线程中调用。
在主线程中调用 curl_global_cleanup 进行全局清理:
curl_global_cleanup();
这释放了所有由 libcurl 库使用的全局资源。
可以确保在多线程环境中安全地使用 curl_global_init 和 curl_global_cleanup 函数。重要的是,避免在每个线程中调用这些全局初始化和清理函数,而是将它们的调用放在主线程中。这样可以防止竞态条件和不必要的资源争用。
5.2.3. char *curl_version( );
打印当前libcurl库的版本,curl_version 函数可以用来获取当前 libcurl 库的版本信息。这个函数返回一个字符串,该字符串包含了 libcurl 库的版本号以及其他相关信息。这个函数在调试和信息输出时非常有用,你可以轻松获取并打印出当前正在使用的 libcurl 库的版本信息。
5.2.4. CURL *curl_easy_init( );
curl_easy_init 是用于初始化一个 CURL 指针的函数,类似于文件操作中的 fopen 函数。它标志着一个 libcurl 会话的开始,并且在会话结束时需要使用 curl_easy_cleanup 函数来清理。初始化后的 CURL 指针通常被称为 easy_handle,并在后续的 easy 系列函数中使用, 如果初始化失败,返回 NULL。
5.2.5. void curl_easy_cleanup(CURL *handle);
这个函数用于清理并释放由 curl_easy_init 初始化的 CURL 对象,应该在会话结束时调用,以避免内存泄漏。
5.2.6. CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
curl_easy_setopt 是 libcurl 中最关键的配置函数,用于设置各种选项来决定 libcurl 的行为。该函数允许你定制化每个 CURL 会话的具体操作,比如设置请求的 URL、回调函数、超时时间等。
CURL *handle: 由 curl_easy_init 返回的 CURL 指针,用于标识一个特定的 libcurl 会话。
CURLoption option: 用于指定要设置的选项。所有可用的选项都在 curl.h 头文件中定义,可以通过查看 man 手册获取详细信息。
parameter: 根据 option 的不同,该参数可以是函数指针、对象指针、long 型变量或其他类型。具体类型取决于你设置的 CURLoption。
5.2.7. CURLcode curl_easy_perform(CURL *handle);
curl_easy_perform 是 libcurl 中用于执行已经配置好的请求的函数。这个函数会根据之前通过 curl_easy_setopt 设置的选项来执行请求,并处理其结果。调用 curl_easy_perform 后,libcurl 会发起网络连接并完成操作,直到请求结束或出现错误。
参数
- CURL *handle: 由 curl_easy_init 返回的 CURL 指针,用于标识一个特定的 libcurl 会话。
返回值
- CURLcode: 返回一个 CURLcode 类型的值,表示请求的结果。如果请求成功,返回 CURLE_OK,否则返回相应的错误码。
5.3 curl_easy_setopt函数部分选项介绍
curl_easy_setopt 是 libcurl 中用于设置各种选项的关键函数。libcurl 提供了丰富的选项来配置 HTTP 请求的行为。以下是一些常见的与 HTTP 相关的 CURLoption 选项及其详细说明:
5.3.1 CURLOPT_URL
- 描述: 设置请求的 URL。
- 示例
curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com");
5.3.2 CURLOPT_WRITEFUNCTION 和 CURLOPT_WRITEDATA
- 描述:
- CURLOPT_WRITEFUNCTION: 设置一个回调函数,用于处理接收到的数据。
- CURLOPT_WRITEDATA: 设置回调函数的数据参数。
- 回调函数原型:
size_t write_callback(void *ptr, size_t size, size_t nmemb, void *stream);
- 示例
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
如果没有设置 CURLOPT_WRITEFUNCTION,默认会将数据打印到标准输出。
5.3.3 CURLOPT_HEADERFUNCTION 和 CURLOPT_HEADERDATA
- 描述:
- CURLOPT_HEADERFUNCTION: 设置一个回调函数,用于处理接收到的 HTTP 头部数据。
- CURLOPT_HEADERDATA: 设置回调函数的数据参数。
- 回调函数原型:
size_t header_callback(void *ptr, size_t size, size_t nmemb, void *stream);
- 示例:
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback); curl_easy_setopt(curl, CURLOPT_HEADERDATA, fp);
5.3.4 CURLOPT_READFUNCTION 和 CURLOPT_READDATA
- 描述:
- CURLOPT_READFUNCTION: 设置一个回调函数,当 libcurl 需要读取数据上传到服务器时调用。
- CURLOPT_READDATA: 设置回调函数的数据参数。
- 回调函数原型:
size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream);
- 示例:
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); curl_easy_setopt(curl, CURLOPT_READDATA, fp);
5.3.5 CURLOPT_NOPROGRESS、CURLOPT_PROGRESSFUNCTION 和 CURLOPT_PROGRESSDATA
- 描述:
- CURLOPT_NOPROGRESS: 禁用或启用进度回调函数。
- CURLOPT_PROGRESSFUNCTION: 设置一个回调函数,用于报告数据传输的进度。
- CURLOPT_PROGRESSDATA: 设置回调函数的数据参数。
- 回调函数原型:
int progress_callback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow);
- 示例:
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback); curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, clientp);
5.3.6 CURLOPT_TIMEOUT 和 CURLOPT_CONNECTTIMEOUT
- 描述:
- CURLOPT_TIMEOUT: 设置整个传输的最长时长(秒)。
- CURLOPT_CONNECTTIMEOUT: 设置连接阶段的最长时长(秒)。
- 示例:
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
5.3.7 CURLOPT_FOLLOWLOCATION
- 描述: 设置是否自动跟随重定向(HTTP 3xx 状态码)。
- 示例:
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
5.3.8 CURLOPT_RANGE 和 CURLOPT_RESUME_FROM
- 描述: 设置断点续传相关选项。
- CURLOPT_RANGE: 设置 HTTP 请求的 Range 头域。
- CURLOPT_RESUME_FROM: 设置传输开始的偏移量。
- 示例:
curl_easy_setopt(curl, CURLOPT_RANGE, "bytes=500-999"); curl_easy_setopt(curl, CURLOPT_RESUME_FROM, 500L);
以下示例代码展示了如何使用上述选项配置并执行一个 HTTP GET 请求:
#include <stdio.h> #include <curl/curl.h> // 回调函数,用于处理接收到的数据 size_t write_callback(void *ptr, size_t size, size_t nmemb, void *stream) { return fwrite(ptr, size, nmemb, (FILE *)stream); } // 回调函数,用于处理接收到的 HTTP 头部数据 size_t header_callback(void *ptr, size_t size, size_t nmemb, void *stream) { return fwrite(ptr, size, nmemb, (FILE *)stream); } // 回调函数,用于报告数据传输的进度 int progress_callback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) { printf("Progress: %f%%\n", dlnow / dltotal * 100); return 0; // 返回非零值以中止传输 } int main(void) { CURL *curl; CURLcode res; FILE *fp = fopen("/tmp/output.html", "wb"); if (!fp) { perror("fopen"); return 1; } // 全局初始化 if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { fprintf(stderr, "curl_global_init() failed\n"); fclose(fp); return 1; } // 初始化一个CURL会话 curl = curl_easy_init(); if (curl) { // 设置请求 URL curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com"); // 设置回调函数,用于处理接收到的数据 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); // 设置回调函数,用于处理接收到的 HTTP 头部数据 curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback); curl_easy_setopt(curl, CURLOPT_HEADERDATA, stdout); // 设置用户代理字符串 curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0"); // 设置请求超时时间为30秒 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); // 设置连接超时时间为10秒 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); // 自动跟随重定向 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // 设置断点续传 curl_easy_setopt(curl, CURLOPT_RANGE, "bytes=500-999"); // 设置进度回调函数 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback); curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, NULL); // 执行请求 res = curl_easy_perform(curl); // 检查请求是否成功 if (res != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } // 清理 CURL 资源 curl_easy_cleanup(curl); } else { fprintf(stderr, "curl_easy_init() failed\n"); } // 关闭文件 fclose(fp); // 全局清理 curl_global_cleanup(); return 0; }
该程序展示了如何使用 libcurl 库进行 HTTP 请求,并通过设置各种选项和回调函数来定制请求的行为。具体功能包括:
- 设置请求 URL
- 处理接收到的响应数据和 HTTP 头部数据
- 设置用户代理字符串
- 设置请求和连接超时时间
- 自动跟随重定向
- 断点续传
- 显示传输进度