LibCurl库使用介绍
libcurl是一个跨平台的开源网络传输库,它支持许多协议,包括HTTP、HTTPS、FTP、FTPS、SCP、SFTP、TFTP、LDAP、以及许多其他协议和文件传输方式。libcurl允许开发者使用C语言编写代码来进行网络通信,并提供了简单易用的API接口。
libcurl的主要特点包括:
- 跨平台性: 可以在各种操作系统上使用,包括Linux、Windows、macOS等。
- 多协议支持: 支持多种常用的网络协议,例如HTTP、FTP等,以及安全协议如HTTPS、FTPS等。
- 功能丰富: 提供了丰富的功能和选项,可以用于各种网络传输需求,如文件上传、下载、POST请求等。
- 高度可定制性: 提供了大量的选项和回调函数,使开发者能够定制和控制网络通信的细节。
- 易用性: libcurl的API设计简单直观,容易上手,同时也提供了丰富的文档和示例。
由于其强大的功能和易用性,libcurl被广泛应用于各种软件和项目中,包括网络爬虫、下载工具、Web服务等。
libcurl使用基本流程
-
初始化CURL库:
curl_global_init(CURL_GLOBAL_ALL);
需要初始化libcurl库,这可以通过调用curl_global_init()函数来完成。该函数在整个程序运行期间只需要调用一次。多线程下最好主动调用该函数以防止在线程中curl_easy_init时多次调用,不要在每个线程中都调用curl_global_init,应该将该函数的调用放在主线程中。CURLcode curl_global_init(long flags); //这个函数只能用一次。(调用curl_global_cleanup清理后可再次使用初始化) //参数:flags //CURL_GLOBAL_ALL: 初始化所有的 libcurl 功能。 //CURL_GLOBAL_SSL: 初始化 SSL 相关的功能,如支持 HTTPS 议。 //CURL_GLOBAL_WIN32: 在 Windows 平台上初始化一些特定的功能。 //CURL_GLOBAL_NOTHING: 不做任何初始化,这种情况下需要手动初始化特定的功能。 //返回值: CURLcode 类型的错误码,如果初始化成功则返回 CURLE_OK,否则返回其他错误码表示初始化失败
-
获取一个CURL句柄用于本次传输:
CURL *curl = curl_easy_init();
成功,则返回指向新创建的 CURL 句柄的指针;如果出现错误,则返回 NULL。 -
设置的传输配置选项: 使用curl_easy_setopt() 函数设置HTTP请求的选项,如URL、请求头、请求体等等。如:
curl_easy_setopt(curl, CURLOPT_URL, url.data()); // 设置要下载的URL地址 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); // 设置回调函数以将数据写入文件 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &downloadData); // 传递包含输出文件指针、进度等的结构体给回调函数
curl_easy_setopt() 函数详解:
CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter); //参数: // - handle: 指向 CURL 句柄的指针,表示要设置选项的 CURL 句柄。 // - option: 枚举类型 CURLoption,表示要设置的选项类型。 // - parameter: 选项对应的参数值,类型根据不同选项而定,既可以是个函数的指针,也可以是某个对象的指针,也可以是个long型的变量 常见CURLoption类型(更多CURLoption类型的选项都在curl.h库里有定义,man 也可以查看到): CURLOPT_URL: 设置请求的 URL。 CURLOPT_HTTPHEADER: 设置请求头信息。 CURLOPT_POSTFIELDS: 设置请求体数据。 CURLOPT_TIMEOUT: 设置请求超时时间。 CURLOPT_WRITEFUNCTION: 设置写数据回调函数。 CURLOPT_WRITEDATA: 设置写数据回调函数的用户数据。 CURLOPT_PROXY: 设置代理服务器地址。 CURLOPT_SSL_VERIFYPEER: 设置是否验证对等证书。 CURLOPT_SSL_VERIFYHOST: 设置是否验证主机名。 CURLOPT_CUSTOMREQUEST: 设置自定义请求方法。 CURLOPT_USERAGENT: 设置用户代理信息。 CURLOPT_VERBOSE: 设置是否输出详细的调试信息。 CURLOPT_FOLLOWLOCATION: 设置是否跟随重定向。 CURLOPT_COOKIE: 设置请求中的 Cookie。 CURLOPT_COOKIEFILE: 设置从文件中读取 Cookie。 CURLOPT_COOKIEJAR: 设置保存 Cookie 到文件。 CURLOPT_RESUME_FROM: 设置断点续传的起始位置。 CURLOPT_LOW_SPEED_LIMIT: 设置低速下载速度限制。 CURLOPT_LOW_SPEED_TIME: 设置低速下载持续时间。 CURLOPT_CONNECTTIMEOUT: 设置连接超时时间 例如: struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Content-Type: application/json"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); //设置请求头 curl_easy_setopt(curl, CURLOPT_URL, "http://example.com"); //设置 URL curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "key1=value1&key2=value2"); //设置请求体 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); // 设置请求超时时间,超时时间为10秒 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);//设置写数据回调函数 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);//设置回调函数用户数据 curl_easy_setopt(curl, CURLOPT_PROXY, "http://proxy.example.com:8080"); //设置代理 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); //设置 SSL 选项, 禁止验证对等证书
部分枚举类型选项对应参数值说明
CURLOPT_WRITEFUNCTION
和CURLOPT_WRITEDATA
: 是配合使用的选项,用于设置写数据回调函数和回调函数的用户数据。CURLOPT_WRITEFUNCTION
选项用于设置一个回调函数,该函数在 libcurl 接收到 HTTP 响应数据时被调用,用于处理接收到的数据。回调函数的原型如下(size_t function( void *ptr, size_t size,size_t nmemb, void *stream);
):size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata); //参数: - ptr 是接收到的数据缓冲区指针, - size 是单个数据块的大小, - nmemb 是数据块的数量, - userdata 是传递给 CURLOPT_WRITEDATA 的用户数据指针。 //返回值:回调函数返回接收到的数据大小。
CURLOPT_WRITEDATA
选项用于设置回调函数的用户数据指针,该指针将被传递给回调函数作为其第四个参数。通常情况下,该指针指向一个结构体或对象,用于在回调函数中存储处理过程中需要的状态信息。
CURLOPT_HEADERFUNCTION
和CURLOPT_HEADERDATA
是 libcurl 中用于处理 HTTP 响应头的选项,回调函数:size_t header_callback(char *buffer, size_t size, size_t nitems, void *userdata);
,具体用法同上。CURLOPT_READFUNCTION
和CURLOPT_READDATA
是 libcurl 中用于处理请求体数据的选项。回调函数:size_t read_callback(char *buffer, size_t size, size_t nitems, void *userdata);
用法同上。CURLOPT_NOPROGRESS
,CURLOPT_PROGRESSFUNCTION
,CURLOPT_PROGRESSDATA
用于处理下载进度的选项CURLOPT_NOPROGRESS
:该选项用于禁止或启用 libcurl 的内置下载进度条功能。设置为 1 表示禁止内置进度条, 0 表示启用内置进度条,默认0;CURLOPT_PROGRESSFUNCTION
:设置一个回调函数,用于在下载过程中更新下载进度。注意:若无需使用额外的用户数据,将 nullptr 作为CURLOPT_PROGRESSDATA
的参数传递给 libcurl;回调函数的原型如下:int progress_callback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow); //参数: - clientp 是传递给 `CURLOPT_PROGRESSDATA` 的用户数据指针, - dltotal 是预期下载总字节数, - dlnow 是当前已下载字节数, - ultotal 是预期上传总字节数, - ulnow 是当前已上传字节数。 //返回值:回调函数应该返回 0 以继续下载操作,返回非零值表示终止下载操作。
CURLOPT_PROGRESSDATA
:设置用户数据指针
CURLOPT_TIMEOUT
,`CURLOPT_CONNECTIONTIMEOUT,用于设置超时时间CURLOPT_TIMEOUT
:设置请求的总超时时间,即从发送请求到接收完整响应的总时间。如果在指定的时间内未完成请求,libcurl 将会放弃请求,并返回超时错误。该选项的参数是一个长整型值,表示超时时间的秒数;用于对完整响应时间比较关注的场景,例如下载文件、访问网页等。CURLOPT_CONNECTTIMEOUT
:该选项用于设置连接超时时间,即建立连接的时间。如果在指定的时间内未能建立连接,libcurl 将会放弃连接尝试,并返回连接超时错误。该选项的参数也是一个长整型值,表示超时时间的秒数;适用于对连接建立时间比较关注的场景,例如需要快速响应的短连接请求
CURLOPT_RANGE
和CURLOPT_RESUME_FROM
是 libcurl 中用于实现断点续传CURLOPT_RANGE
:设置 HTTP 请求的范围,即指定需要下载的文件的范围。它的参数是一个字符串,格式为 start-end,表示从文件的 start 字节处开始下载,直到 end 字节结束。如果 end 留空,则表示下载从 start 字节开始到文件末尾。用于指定下载文件的范围,允许下载文件的任意部分内容,不仅仅限于断点续传。- CURLOPT_RESUME_FROM:该选项用于设置断点续传的起始位置,即指定从文件的哪个字节处开始继续下载。它的参数是一个长整型值,表示断点续传的起始字节位置,主要用于实现断点续传,指定从文件的指定位置开始继续下载
CURLOPT_VERBOSE
是 libcurl 中的一个选项,选项值类型:long,用于控制是否打印详细的调试信息。设置为非零值时,libcurl 将打印出大量的调试信息,包括请求和响应的头部、数据传输的详细信息等。这些信息对于调试和排查问题非常有用,但在正常情况下可能会显得冗长。通常不会将CURLOPT_VERBOSE
设置为非零值,因为打印大量的调试信息会影响程序的性能并增加日志的大小。但在调试和开发阶段,可以将其设置为非零值来帮助排查问题。
-
执行请求:
CURLcode res = curl_easy_perform(curl); //执行HTTP请求
-
处理响应: 根据请求的结果进行相应的处理,如处理响应数据、错误处理等。
if (res != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } else { // 请求成功,处理响应数据 } //以下是一些常用的 CURLcode 枚举值,详细的错误描述字符串,可以通过const char *curl_easy_strerror(CURLcode errornum ) 这个函数取得 CURLE_OK (0): 操作成功完成。 CURLE_UNSUPPORTED_PROTOCOL (1): 不支持的协议。 CURLE_URL_MALFORMAT (3): URL 格式错误。 CURLE_COULDNT_RESOLVE_HOST (6): 无法解析主机名。 CURLE_COULDNT_CONNECT (7): 无法连接到主机或代理。 CURLE_OPERATION_TIMEDOUT (28): 操作超时。 CURLE_SSL_CONNECT_ERROR (35): SSL/TLS 连接错误。 CURLE_PEER_FAILED_VERIFICATION (51): 对等证书验证失败。 CURLE_GOT_NOTHING (52): 未收到任何数据。 CURLE_SEND_ERROR (55): 发送数据时出错。 CURLE_RECV_ERROR (56): 接收数据时出错。 CURLE_SSL_CERTPROBLEM (58): 证书问题。 CURLE_SSL_CIPHER (59): SSL/TLS 密码错误。 CURLE_SSL_CACERT (60): 未找到合适的 CA 证书。 CURLE_USE_SSL_FAILED (64): 使用 SSL/TLS 失败。
-
清理: 执行完HTTP请求后,需要清理资源,包括清理CURL句柄和释放libcurl相关的资源。
curl_easy_cleanup(curl);
-
全局清理: 在程序结束前,需要对libcurl进行全局清理,释放相关资源。
curl_global_cleanup();
代码示例:
#include <stdio.h>
#include <curl/curl.h>
struct DownloadData {
FILE *file = nullptr; // 声明为 null ;
s32 fileSize;
s32 downloaded;
std::string hash;
bool isInitialized = false;
};
// 定义写数据回调函数
size_t HttpDownloadService::WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
// size_t data_size = size * nmemb; // 计算本次写入的数据总大小(size是单个数据块大小,nmemb是块的数量)
// 获取传递进来的DownloadData结构体指针
struct DownloadData *downloadData = (struct DownloadData *) userp;
// 将数据块写入文件,返回实际写入的大小
size_t write_size = fwrite(contents, size, nmemb, downloadData->file);
downloadData->downloaded += write_size; // 更新已下载的总字节数
if (downloadData->fileSize > 0) {
downloaded = downloadData->downloaded;
double progress = (double) downloadData->downloaded / downloadData->fileSize * 100;
std::cout << " Download fileSize: " << downloadData->fileSize << " Download Progress: " << progress << "%" << std::endl;
}
return write_size;
}
int main() {
curl_global_init(CURL_GLOBAL_ALL);// 初始化CURL库
CURL *curl = curl_easy_init(); // 获取一个CURL句柄用于本次传输
if (curl) {
const char *outputFileName = "aaa.txt";
struct DownloadData downloadData;
// 以二进制写模式打开输出文件
downloadData.file = fopen(outputFileName, "wb"); // Open the file and get FILE* pointer
downloadData.fileSize = 7022; // 预期的总文件大小,字节数
downloadData.downloaded = 0; // 已下载的字节数
downloadData.hash = "8b32dbbdfcffc01ae7ff4cd29c9e4bfa973e9df29a629c80605d374d4237e2ba"; // 预期的下载文件SHA256哈希值
curl_easy_setopt(curl, CURLOPT_URL, “http://example.com”); // 设置要下载的URL地址
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); // 设置回调函数以将数据写入文件
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &downloadData); // 传递包含输出文件指针、进度等的结构体给回调函数
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); // 启用进度回调
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, nullptr); // 进度回调函数可选,这里不使用
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
CURLcode res = curl_easy_perform(curl); // 执行请求
if (res != CURLE_OK) {
// 请求失败,处理错误
std::cout << "Download failed: {}: " << curl_easy_strerror(res)<< std::endl;
} else {
// 请求成功,处理响应数据
logger->Info(SRC_LOC, "Download completed successfully.");
std::cout << "Download completed successfully. " << std::endl;
//……eg:对比文件大小和hash检查下载文件是否正常
}
curl_easy_cleanup(curl); // 清理CURL句柄资源
fclose(downloadData.file); // Close the file
}
curl_global_cleanup(); // 清理CURL库资源
return 0;
}