一、LibCurl库简介
libCurl是一个功能强大、跨平台的网络协议库,被广泛用于实现各种网络通信模块。
libCurl支持多种主流的网络协议,包括HTTP/HTTPS、FTP/FTPS、SMTP/POP3、SCP/SFTP、RTSP、LDAP、Gopher、Telnet等协议。
开发者可以基于libCurl实现HTTPS证书授权、HTTP POST/PUT请求、FTP文件上传下载、Proxy代理支持、Cookies管理和用户认证等功能。
基于LibCurl库可以实现以下功能:
1.HTTP/HTTPS通信
支持GET/POST/PUT/DELETE等HTTP方法。
支持基于SSL/TLS的HTTPS通信。
2.文件传输协议
支持FTP/SFTP实现的文件上传和下载功能。
基于CURLOPT_RESUME_FROM接口实现的断点续传功能。
3.数据处理和解析
基于curl_mime接口实现的多部分表单数据上传功能。
基于CURLOPT_WRITEFUNCTION回调函数实现的HTTP响应数据处理功能。
libCurl库的核心特性如下:
1.多协议支持:
libcurl支持8种以上主流网络协议,确保开发者能在不同场景下灵活处理数据交互。
通过以下C语言代码可以配置允许的网络协议,让代码的可读性更强:
curl_easy_setopt(handle,
CURLOPT_PROTOCOLS,
CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP);
2.安全通信机制:
支持SSL/TLS、内置HTTPS证书验证机制,保障了数据传输的安全性。
支持验证服务器证书,包括检查证书链和主机域名是否匹配,验证过程可通过CURLOPT_SSL_VERIFYPEER接口和CURLOPT_SSL_VERIFYHOST接口进行设置。
支持使用客户端证书进行双向认证,可通过CURLOPT_SSLCERT接口设置客户端证书,通过CURLOPT_SSLKEY为TLS/SSL客户端证书指定私钥文件,通过CURLOPT_CAINFO指定证书的路径。
3.支持数据传输
支持断点续传,通过设置CURLOPT_RESUME_FROM_LARGE可以指定从文件的某个位置开始传输,用于下载中断后恢复续传。
传输速度可配置,通过CURLOPT_MAX_SEND_SPEED_LARGE和CURLOPT_MAX_RECV_SPEED_LARGE可以限制上传和下载的最快速度。
支持HTTP表单上传、FTP文件上传和下载。
4.回调机制
libcurl通过回调函数将数据处理的控制权交给用户的应用程序。
写回调:用于处理接收到的数据,通过CURLOPT_WRITEFUNCTION设置。在C++中,该回调函数必须是静态成员函数或全局函数,因为非静态成员函数有隐含的this指针。
读回调:用于上传数据,通过CURLOPT_READFUNCTION设置。
其他回调:例如进度条显示回调CURLOPT_PROGRESSFUNCTION、Header头部回调CURLOPT_HEADERFUNCTION等。
5.多线程支持
通过设置CURLOPT_NOSIGNAL为1,可以避免在多线程中使用信号,因为信号处理是进程级别的,在多线程中不安全。
每个curl_easy句柄通常在一个线程内使用,多个线程可以同时使用各自的curl_easy句柄。
6.跨平台兼容
可在Windows、Linux、macOS等系统上编译运行,无需修改核心代码。
二、LibCurl核心接口分类
三、LibCurl开发流程
1.初始化libcurl库
调用curl_global_init()进行全局初始化。注意,此函数不是线程安全的,应仅在主线程中调用一次。虽然libCurl整体是线程安全的,但curl_global_init()必须在主线程中调用以避免竞态条件。
2.创建curl_easy句柄
使用curl_easy_init()获取一个easy interface指针。
3.设置curl选项
通过curl_easy_setopt()配置传输参数,比如URL、回调函数等。
4.实现读/写回调
定义回调函数处理数据传输,如接收数据或错误处理。
5.执行请求任务
调用curl_easy_perform()启动数据传输或网络请求。
6.清理资源
任务完成后,用curl_easy_cleanup()释放内存,进程退出时调用curl_global_cleanup()清理全局资源。
LibCurl实现FTP请求代码样例:
include
int main(void)
{
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
/*
* Make the URL end with a trailing slash
*/
curl_easy_setopt(curl, CURLOPT_URL, “ftp://ftp.example.com/”);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
if(CURLE_OK != res) {
/* we failed */
fprintf(stderr, "curl told us %d\n", res);
}
}
curl_global_cleanup();
return0;
}
四、LibCurl开发环境搭建
从LibCurl官网下载指定版本的源码进行编译和安装。
step.01:下载源码
wget https://curl.se/download/curl-7.74.0.tar.gz
step.02:解压源码
tar -zxvf curl-7.74.0.tar.gz
step.03:构建 & 编译
./configure –prefix=/usr/local
make && make install
安装完成以后,可以执行以下命令查看libcurl版本支持的特性:
五、LibCurl库常用接口
1.Easy Interface(简单接口)
Easy Interface是libcurl中最常用的同步传输接口。每个Easy Handle代表一个独立的传输会话。
CURL *curl_easy_init(void);
初始化一个简单的句柄(easy handle),用于后续的传输设置和执行。
CURLcode curl_easy_setopt(CURL *handle, CURLoption option, …);
设置传输选项(options)。这是配置easy handle行为的主要函数,有超过200个选项可用。
CURLcode curl_easy_perform(CURL *handle);
执行传输。这是一个阻塞调用,直到传输完成或失败。
void curl_easy_cleanup(CURL *handle);
清理并释放一个easy handle。
CURL *curl_easy_duphandle(CURL *handle);
复制一个已有的easy handle(包括所有设置)。
void curl_easy_reset(CURL *handle);
重置一个easy handle的所有选项,使其回到初始状态(但保持连接池)。
CURLcode curl_easy_pause(CURL *handle, int bitmask);
暂停或恢复一个正在进行的传输(通常用于回调函数中)。
CURLcode curl_easy_send(CURL *handle, const void *buffer, size_t buflen, size_t *n);
用于WebSocket或RTSP等,发送原始数据(非阻塞)。
CURLcode curl_easy_recv(CURL *handle, void *buffer, size_t buflen, size_t *n);
用于WebSocket或RTSP等,接收原始数据(非阻塞)。
C++代码样例:
/* 协议控制 */
// 目标URL
curl_easy_setopt(handle, CURLOPT_URL, “https://example.com”);
// 协议限制
curl_easy_setopt(handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
// 端口设置
curl_easy_setopt(handle, CURLOPT_PORT, 443L);
/* 回调函数 */
// 接收回调
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback);
// 头回调
curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, header_callback);
/* 超时控制 */
// 总超时
curl_easy_setopt(handle, CURLOPT_TIMEOUT, 30L);
// 连接超时
curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT, 10L);
2.Multi Interface(多接口)
Multi Interface允许同时进行多个传输(非阻塞方式),通常在事件循环中使用。
CURLM *curl_multi_init(void);
初始化一个multi handle。
CURLMcode curl_multi_add_handle(CURLM *multi_handle, CURL *easy_handle);
将一个easy handle添加到multi handle中,使其成为多传输的一部分。
CURLMcode curl_multi_remove_handle(CURLM *multi_handle, CURL *easy_handle);
从multi handle中移除一个easy handle。
CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles);
执行传输(非阻塞),返回当前仍在运行的传输数量。
CURLMcode curl_multi_poll(CURLM *multi_handle, struct curl_waitfd *extra_fds, unsigned int extra_nfds, int timeout_ms, int *numfds);
等待文件描述符上的活动(类似于poll函数)。
CURLMcode curl_multi_wait(CURLM *multi_handle, struct curl_waitfd *extra_fds, unsigned int extra_nfds, int timeout_ms, int *numfds);
等待multi handle上的活动(类似于select函数,但更简单)。
CURLMcode curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue);
读取已完成传输的信息(例如,哪些传输已完成)。
CURLMcode curl_multi_cleanup(CURLM *multi_handle);
清理multi handle。
CURLMcode curl_multi_setopt(CURLM *multi_handle, CURLMoption option, …);
设置multi handle的选项。
const char *curl_multi_strerror(CURLMcode errornum);
将multi接口的错误代码转换为字符串。
CURLMcode curl_multi_socket_action(CURLM *multi_handle, curl_socket_t sockfd, int ev_bitmask, int *running_handles);
在指定的socket上通知libcurl发生的事件(用于更精细的事件驱动)。
CURLMcode curl_multi_timeout(CURLM *multi_handle, long *timeout);
获取multi handle的超时时间(用于设置事件循环的等待时间)。
C++代码样例:
CURLM *multi = curl_multi_init();
// 添加多个easy handle…
int running;
do {
CURLMcode res = curl_multi_perform(multi, &running);
if (running) {
// 使用epoll集成
curl_multi_poll(multi, NULL, 0, 1000, NULL);
}
} while (running);
// 处理完成的任务
CURLMsg *msg;
while ((msg = curl_multi_info_read(multi, &remaining))) {
if (msg->msg == CURLMSG_DONE) {
// 处理完成传输
}
}
3.MIME Interface(MIME表单接口)
MIME接口用于构建和发送multipart表单数据(如文件上传)。
curl_mime *curl_mime_init(CURL *easy);
初始化一个mime句柄(与特定的easy handle关联)。
curl_mimepart *curl_mime_addpart(curl_mime *mime);
向mime表单中添加一个部分(part)。
CURLcode curl_mime_name(curl_mimepart *part, const char *name);
设置mime部分的名称(表单字段名)。
CURLcode curl_mime_filename(curl_mimepart *part, const char *filename);
设置mime部分的文件名(用于文件上传)。
CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype);
设置mime部分的MIME类型(如”text/plain”)。
CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding);
设置mime部分的内容编码(如”base64″)。
CURLcode curl_mime_data(curl_mimepart *part, const char *data, size_t datasize);
设置mime部分的数据(内存数据)。
CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename);
设置mime部分的数据来自文件(文件上传)。
CURLcode curl_mime_data_cb(curl_mimepart *part, curl_off_t datasize, curl_read_callback readfunc, curl_seek_callback seekfunc, curl_free_callback freefunc, void *arg);
通过回调函数设置mime部分的数据(允许自定义数据源)。
CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts);
设置mime部分的子部分(用于嵌套的multipart)。
void curl_mime_free(curl_mime *mime);
释放一个mime句柄及其所有部分。
C++代码样例:
curl_mime *form = curl_mime_init(curl);
curl_mimepart *field = curl_mime_addpart(form);
// 文本字段
curl_mime_name(field, “username”);
curl_mime_data(field, “testuser”, CURL_ZERO_TERMINATED);
// 文件字段
field = curl_mime_addpart(form);
curl_mime_name(field, “avatar”);
curl_mime_filedata(field, “/path/to/avatar.jpg”);
curl_mime_filename(field, “profile.jpg”);
// 设置到easy handle
curl_easy_setopt(curl, CURLOPT_MIMEPOST, form);
4.数据处理回调接口
size_t write_callback(char* ptr, size_t size, size_t nmemb, void* userdata)
数据接收回调。
size_t read_callback(char* buffer, size_t size, size_t nitems, void* instream)
数据上传回调。
5.实用工具接口
const char* curl_easy_strerror(CURLcode errornum)
将错误码转为可读信息(调试必备)。
CURLINFO curl_easy_getinfo(CURL* handle, CURLINFO info, …)
获取传输元数据,比如传输相关的信息(如响应代码、传输速度等)。
struct curl_slist* curl_slist_append(struct curl_slist* list, const char* string)
构建HTTP头部。
6.其他接口
libcurl还提供了一些其他接口,例如:
Share Interface:用于在多个easy handle之间共享数据,如cookies、DNS缓存。
URL Interface:用于解析和操作URL。
Version Information:获取库的版本信息。
代码样例:
// 共享DNS缓存
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
// 共享SSL会话缓存
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
// 自定义锁
curl_share_setopt(share, CURLSHOPT_LOCKFUNC, lock_callback);
六、LibCurl库C++编程实例
Demo1:简单网页请求
include
include
int main(void)
{
CURL *curl;
CURLcode result = curl_global_init(CURL_GLOBAL_ALL);
if(result != CURLE_OK)
return (int)result;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, “https://example.com”);
/* example.com is redirected, so we tell libcurl to follow redirection */
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
/* Perform the request, result gets the return code */
result = curl_easy_perform(curl);
/* Check for errors */
if(result != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(result));
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return0;
}
运行结果:
Example Domain
Example Domain
This domain is for use in documentation examples without needing permission. Avoid use in operations.
Demo2:SSL安全认证
应用场景:银行系统对接、医疗数据加密传输
// HTTPS双向证书认证
void SecureRequest() {
CURL* curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, “https://secure-api.com”);
// 客户端证书设置
curl_easy_setopt(curl, CURLOPT_SSLCERT, “/certs/client.pem”);
curl_easy_setopt(curl, CURLOPT_SSLKEY, “/certs/key.pem”);
curl_easy_setopt(curl, CURLOPT_KEYPASSWD, “mypassword”);
// CA证书验证
curl_easy_setopt(curl, CURLOPT_CAINFO, “/certs/cacert.pem”);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
CURLcode res = curl_easy_perform(curl);
// 错误处理...
}
}
Demo3:HTTP/HTTPS客户端通信
应用场景:实现Restful API调用或Web数据抓取
include
include
// 回调函数处理响应数据
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* data) {
data->append((char*)contents, size * nmemb);
return size * nmemb;
}
int main() {
CURL* curl = curl_easy_init();
std::string response;
if(curl) {
// 设置API端点
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/users");
// 设置HTTP头
struct curl_slist* headers = nullptr;
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
// 设置JSON请求体
constchar* json = R"({"name":"John","age":30})";
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json);
// 设置响应处理回调
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
CURLcode res = curl_easy_perform(curl);
if(res != CURLE_OK) {
std::cerr << "Request failed: " << curl_easy_strerror(res);
} else {
std::cout << "API Response: " << response;
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
return0;
}
Demo4:文件传输协议
应用场景:跨服务器文件同步、OTA固件升级、FTP/SFTP服务器文件上传/下载
include
int main() {
CURL* curl;
FILE* fp;
constchar* url = “ftp://example.com/file.txt”;
constchar* local_path = “file.txt”;
// 打开本地文件用于写入
fp = fopen(local_path, "wb");
if (!fp) return1;
curl = curl_easy_init();
if (curl) {
// 设置FTP地址
curl_easy_setopt(curl, CURLOPT_URL, url);
// 设置用户名密码
curl_easy_setopt(curl, CURLOPT_USERPWD, "username:password");
// 设置文件写入回调
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
// 执行下载
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK)
fprintf(stderr, "Download failed: %s\n", curl_easy_strerror(res));
// 清理
curl_easy_cleanup(curl);
}
fclose(fp);
return0;
}
Demo5:Form表单数据上传
include
int main() {
CURL* curl;
curl_mime* mime;
curl_mimepart* part;
curl = curl_easy_init();
if (curl) {
// 创建多部件表单
mime = curl_mime_init(curl);
// 添加文本字段
part = curl_mime_addpart(mime);
curl_mime_name(part, "text_field");
curl_mime_data(part, "Hello World", CURL_ZERO_TERMINATED);
// 添加文件字段
part = curl_mime_addpart(mime);
curl_mime_name(part, "file_field");
curl_mime_filedata(part, "/path/to/file.jpg");
// 设置表单和URL
curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/upload");
// 执行请求
curl_easy_perform(curl);
// 清理
curl_mime_free(mime);
curl_easy_cleanup(curl);
}
return0;
}
七、LibCurl库C++代码实战
Demo1:Web网页爬虫
基于LibCurl实现Web网页爬虫,设置Write回调函数来接收Web网页数据。
在多线程场景中,需要额外设置CURLOPT_NOSIGNAL以避免信号问题,但本例为单线程,可省略。
特殊代码:
1.用户代理设置(避免被屏蔽)
curl_easy_setopt(curl, CURLOPT_USERAGENT, “Mozilla/5.0…”);
2.自动跟随重定向
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
3.获取HTTP状态码
long http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
完整C++代码实现:
include
include
include
include
// 网页响应数据写入回调函数
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) {
size_t total_size = size * nmemb;
output->append(static_cast(contents), total_size);
return total_size;
}
// 提取网页标题的正则表达式
std::string ExtractTitle(const std::string& html) {
std::regex title_regex(“(.*?)”, std::regex_constants::icase);
std::smatch match;
if (std::regex_search(html, match, title_regex) && match.size() > 1) {
return match[1].str();
}
return"Title not found";
}
// 提取所有链接
void ExtractLinks(const std::string& html) {
std::regex link_regex(R”(<a\s+href=“‘[“‘])”, std::regex_constants::icase);
auto links_begin = std::sregex_iterator(html.begin(), html.end(), link_regex);
auto links_end = std::sregex_iterator();
std::cout << "\nFound links:\n";
for (std::sregex_iterator i = links_begin; i != links_end; ++i) {
std::smatch match = *i;
if (match.size() > 1) {
std::cout << " - " << match[1].str() << "\n";
}
}
}
// 爬虫函数
void WebCrawler(const std::string& url) {
CURL* curl = curl_easy_init();
std::string response;
if (!curl) {
std::cerr << "Curl initialization failed\n";
return;
}
// 设置cURL选项
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; MyCrawler/1.0)");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // 跟随重定向
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); // 10秒超时
// 执行请求
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK) {
std::cerr << "Request failed: " << curl_easy_strerror(res) << "\n";
} else {
// 获取HTTP状态码
long http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
if (http_code == 200) {
std::cout << "\n=== Crawling successful ===\n";
std::cout << "URL: " << url << "\n";
std::cout << "Page Title: " << ExtractTitle(response) << "\n";
std::cout << "Content Size: " << response.size() << " bytes\n";
// 提取链接
ExtractLinks(response);
} else {
std::cout << "HTTP Error: " << http_code << "\n";
}
}
curl_easy_cleanup(curl);
}
int main() {
curl_global_init(CURL_GLOBAL_DEFAULT);
// 爬取示例网站
WebCrawler("https://example.com");
// 可添加更多URL
// WebCrawler("https://www.wikipedia.org");
curl_global_cleanup();
return0;
}
编译运行:
g++ demo.cpp -lcurl
运行结果:
Demo2:多表单请求
include
include
include
include
int main(void)
{
CURL *curl;
CURLM *multi_handle;
int still_running = 0;
curl_mime *form = NULL;
curl_mimepart *field = NULL;
struct curl_slist *headerlist = NULL;
staticconstchar buf[] = “Expect:”;
curl = curl_easy_init();
multi_handle = curl_multi_init();
if(curl && multi_handle) {
/* Create the form */
form = curl_mime_init(curl);
/* Fill in the file upload field */
field = curl_mime_addpart(form);
curl_mime_name(field, "sendfile");
curl_mime_filedata(field, "multi-post.c");
/* Fill in the filename field */
field = curl_mime_addpart(form);
curl_mime_name(field, "filename");
curl_mime_data(field, "multi-post.c", CURL_ZERO_TERMINATED);
/* Fill in the submit field too, even if this is rarely needed */
field = curl_mime_addpart(form);
curl_mime_name(field, "submit");
curl_mime_data(field, "send", CURL_ZERO_TERMINATED);
/* initialize custom header list (stating that Expect: 100-continue is
not wanted */
headerlist = curl_slist_append(headerlist, buf);
/* what URL that receives this POST */
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/upload.cgi");
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
curl_easy_setopt(curl, CURLOPT_MIMEPOST, form);
curl_multi_add_handle(multi_handle, curl);
do {
CURLMcode mc = curl_multi_perform(multi_handle, &still_running);
if(still_running)
/* wait for activity, timeout or "nothing" */
mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);
if(mc)
break;
} while(still_running);
curl_multi_cleanup(multi_handle);
/* always cleanup */
curl_easy_cleanup(curl);
/* then cleanup the form */
curl_mime_free(form);
/* free slist */
curl_slist_free_all(headerlist);
}
return0;
}
运行结果:
参考阅读:
https://curl.se/libcurl/c/simple.html
https://curl.se/libcurl/c/libcurl-tutorial.html
声明:来自程序员与背包客,仅代表创作者观点。链接:https://eyangzhen.com/6462.html