libcurl库的使用
1. libcurl库的编译
下载
curl-7.77.0.tar.gz
,进行解压注意版本,否则可能存在编译问题
执行
./configure
注意指定项:
可以从docs目录下的install.md文件中查看具体详情
如果报错:
附加选项即可,比如
--with-wolfssl
- 接下来执行
make
和make install
出现_install目录即正确
总结:
./configure --prefix=$HOME
make
make install
2.libcurl库使用
通过libcurl的Easy interface API,开发者可以轻松地进行网络通信
主要使用:
#初始化和清理
curl_easy_init()
curl_easy_cleanup()
#设置url和选项
curl_easy_setopt()
#执行请求
curl_easy_perform()
#错误处理
curl_easy_strerror()
const char *curl_easy_strerror(CURLcode error);
curl_global_init()
函数原型
CURLcode curl_global_init(long flags)
函数返回值为
CURLcode
类型的错误代码- 返回值为
CURLE_OK
表示初始化成功 - 其他返回值表示初始化过程中出现了错误。
- 返回值为
参数
flags
是一个标志位,用于设置全局初始化的选项CURL_GLOBAL_ALL: 初始化所有的支持的功能
包括线程安全、DNS解析、SSL等
CURL_GLOBAL_SSL: 只初始化SSL相关的功能
CURL_GLOBAL_WIN32: 只适用于Windows平台
CURL_GLOBAL_NOTHING: 表示libcurl在全局初始化时不初始化任何功能。
- curl_easy_init()调用时自动调用curl_global_init()
- curl_global_cleanup()不是线程安全的,不要在每个线程都调用,在主线程调用
curl_global_cleanup()
void curl_global_cleanup(void);
该函数用于清理和释放libcurl库的全局资源。在程序退出之前,应调用该函数来确保释放libcurl占用的系统资源。
curl_global_cleanup()
函数应在最后一个curl_easy_cleanup()
函数调用之后进行调用。全局清理会关闭所有的libcurl句柄和资源
如果没有进行全局初始化
curl_global_init()
,则无需调用curl_global_cleanup()
全局清理是可选的
#include <stdio.h>
#include <curl/curl.h>
int main(){
//初始化
CURLcode res=curl_global_init(CURL_GLOBAL_ALL);
if(res != CURLE_OK){
//打印错误信息
fprintf(stderr,"curl_global_init() fails:%s\n",curl_easy_strerror(res));
}else{
printf("curl_global_init() successful\n");
}
//清理curl
curl_global_cleanup();
return 0;
}
curl_easy_init()
CURL *curl_easy_init(void);
- 该函数用于创建并返回一个CURL句柄,用于执行curl请求。
- 该句柄可以用于执行各种
curl请求
操作,如发送HTTP请求、获取响应、设置请求选项等。
该函数返回一个指向CURL句柄的指针,如果初始化成功,将返回非NULL的指针;否则返回NULL指针。
curl_easy_cleanup()
void curl_easy_cleanup(CURL *handle);
- 参数
handle
是一个指向CURL句柄的指针,表示需要清理和释放的句柄。 - 如果在程序中有多个CURL句柄,每个句柄都需要调用
curl_easy_cleanup()
进行清理和释放。
该函数意味着会话的开始,后续所有操作都针对某个指针进行,该指针称为句柄。
curl_easy_setopt()
一个用C写的API函数,用于设置CURL会话的各种选项。
CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
通过该函数,可以设置URL、请求头、请求方法、超时时间等等。
该函数很重要,curl的所有设置都在该函数内完成
handle:CURL会话句柄,即通过curl_easy_init()函数初始化得到的指针。
option:要设置的选项,是一个预定义的枚举值,表示需要设置的具体选项。
一些常见的
CURLoption
选项的参数- URL相关参数
- 调试和信号处理参数
- SSL参数
- 请求头信息参数
- 分类:回调
parameter:选项的参数值,根据具体的选项而不同。
分类:请求头信息参数
CURLOPT_HEADER:
默认为0。指定写回调函数WRITEFUNCTION是否包含报文的头部信息。
CURLOPT_HTTPHEADER:
CURLOPT_HTTPHEADER是一个curl_slist结构体类型的参数,它允许用户自定义HTTP请求的头信息。HTTP头部用于在请求中传递额外的信息,如身份验证凭据、自定义标头字段等
设置:CURLOPT_HTTPHEADER
CURLOPT_HTTPHEADER
参数允许用户自定义HTTP请求的头信息- 通过设置
curl_slist
结构体来添加和管理自定义的头信息。
//curl_slist_append用于向curl_slist结构中添加新的字符串元素 struct curl_slist *curl_slist_append(struct curl_slist *list,const char *string); /* 返回值是一个指向更新后字符串列表的指针: 如果添加成功,返回的指针将指向新的字符串列表; 如果添加失败,返回的指针将与list参数相同。 */
//curl_slist是libcurl库中用于存储字符串列表的数据结构。它被定义为一个结构体 struct curl_slist { char *data; // 字符串数据 struct curl_slist *next; // 指向下一个元素的指针 }; //该函数用于释放整个字符串链表及其节点所占用的内存 void curl_slist_free_all(struct curl_slist *list);
使用完
curl_slist
结构后,应该使用curl_slist_free_all
函数释放链表内存,以避免内存泄漏。
struct curl_slist *headers = NULL; headers=curl_slist_append(headers,"ACCEPT: application/json"); headers=curl_slist_append(headers,"Content-Type: application/json"); struct curl_slist *cur=headers; while(cur){ printf("%s\n",cur->data); cur=cur->next; } //释放内存 curl_slist_free_all(headers);
curl_easy_setopt()
函数可以设置多个选项,以满足具体的请求需求。
curl_easy_perform
用于执行通过curl_easy_setopt()
函数设置的CURL请求。
CURLcode curl_easy_perform(CURL *handle);
函数返回值:函数返回一个CURLcode枚举类型的结果,用于指示请求执行的状态。
如果返回CURLE_OK
,表示请求执行成功;如果返回非零错误码,则表示发生了错误。
CURLE_OK
: 请求成功完成。CURLE_UNSUPPORTED_PROTOCOL
: 不支持的协议,由URL的头部指定。CURLE_COULDNT_CONNECT
: 无法连接到远程主机或代理。CURLE_REMOTE_ACCESS_DENIED
: 访问被拒绝。CURLE_HTTP_RETURNED_ERROR
: HTTP返回错误。CURLE_READ_ERROR
: 读取本地文件错误。
在调用curl_easy_perform()
之前,必须通过curl_easy_setopt()
函数设置所需的选项:如URL、请求头、请求方法、请求体等。
#include <stdio.h>
#include <curl/curl.h>
int main(){
//初始化
CURL *curl=curl_easy_init();
if(curl==NULL){
fprintf(stderr,"curl_easy_init() failed\n");
return 1;
}
//设置URL
curl_easy_setopt(curl,CURLOPT_URL,"http://www.baidu.com");
//执行
CURLcode res=curl_easy_perform(curl);
if(res != CURLE_OK){
fprintf(stderr,"curl_easy_perform() failed:%s\n",curl_easy_strerror(res));
curl_easy_cleanup(curl);
return 1;
}
//清理
curl_easy_cleanup(curl);
return 0;
}
总结:
- 调用curl_global_init()初始化libcurl,调用curl_global_cleanup()清理全局curl句柄
- 调用curl_easy_init()函数得到 easy interface型指针
- 调用curl_easy_setopt()设置传输选项
- 根据curl_easy_setopt()设置的传输选项,实现回调函数以完成用户特定任务
- 调用curl_easy_perform()函数完成传输任务
- 调用curl_easy_cleanup()释放内存
几个应用
get请求
#include <stdio.h>
#include <curl/curl.h>
//c语言不支持bool类型
typedef unsigned int bool;
#define true 1
#define false 0
bool getURL(char* filename){
//创建句柄
CURL* curl;
//文件句柄
FILE* fp;
//返回状态码
CURLcode res;
//打开文件失败
if((fp=fopen(filename,"w"))==NULL){
perror("open file fails!!\n");
return false;
}
//设置HTTP头
struct curl_slist* headers=NULL;
//Accept是标准的HTTP的头字段
curl_slist_append(headers,"Accept:Agent-007");
//初始化
curl=curl_easy_init();
if(curl){//非NULL
//设置opt
curl_easy_setopt(curl,CURLOPT_HEADER,headers);//改协议头
curl_easy_setopt(curl,CURLOPT_URL,"http://www.baidu.com");//指定HTTP请求的目标url
/* 设置HTTP响应的头部和正文都写入到同一个文件 */
//CURLOPT_WRITEDATA用来接收服务器响应的数据
curl_easy_setopt(curl,CURLOPT_WRITEDATA,fp);
//作用是将 HTTP 响应头部数据写入指定的位置,而不是响应的主体数据(body)
curl_easy_setopt(curl,CURLOPT_HEADERDATA,fp);
/*开始执行*/
res=curl_easy_perform(curl);
if(res!=CURLE_OK){
fprintf(stderr,"curl_easy_perform() fails:%s",curl_easy_strerror(res));
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
return false;
}
}else{
//打印错误
fprintf(stderr,"curl_easy_init() fails!\n");
fclose(fp);
return false;
}
fclose(fp);
return true;
}
int main(){
getURL("hello.html");
return 0;
}
#include <stdio.h>
#include <curl/curl.h>
int main(){
CURL *curl;
FILE *fp;
CURLcode res;
//全局初始化
curl_global_init(CURL_GLOBAL_DEFAULT);
//初始化句柄
curl=curl_easy_init();
//初始化文件
fp=fopen("mm.txt","wb");
if(fp==NULL){
perror("open files fails\n");
return 1;
}
//成功
if(curl){
struct curl_slist* headers=NULL;
curl_slist_append(headers,"Accept:text/html");
curl_easy_setopt(curl,CURLOPT_HEADER,headers);
curl_easy_setopt(curl,CURLOPT_URL,"http://cn.bing.com/");
//保存正文数据
curl_easy_setopt(curl,CURLOPT_WRITEDATA,fp);
//保存响应头部数据
curl_easy_setopt(curl,CURLOPT_HEADERDATA,fp);
/*执行*/
res=curl_easy_perform(curl);
if(res!=CURLE_OK){
//出错
fprintf(stderr,"curl_easy_perform() fails:%s\n",curl_easy_strerror(res));
}
/*清理*/
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
fclose(fp);
}else{
perror("curl_easy_init() fails\n");;
}
//全局关闭句柄
curl_global_cleanup();
return 0;
}
回调函数
CURLOPT_WRITEFUNCTION和CURLOPT_WRITEDATE
在使用 libcurl 进行网络请求时,CURLOPT_WRITEFUNCTION
和 CURLOPT_WRITEDATA
是用来接收和处理服务器返回数据的两个非常重要的设置项,而它们的核心概念是 回调函数(Callback Function)。
也就是说:回调函数是当网页有数据返回时进行数据处理的函数
回调函数:你把一个函数地址(指针)传给另一个函数,在特定事件发生时,这个函数会被“回调”执行。
- 通俗理解:你告诉libcurl,“当你收到数据时,请调用我这个函数来处理这些数据。”
- 回调函数一般由你自己定义,用于处理异步或框架层的数据。
选项 | 用途 |
---|---|
CURLOPT_WRITEFUNCTION |
设置接收数据的回调函数 |
CURLOPT_WRITEDATA |
设置传给回调函数的用户指针(一般用于传递保存数据的变量) |
CURLOPT_WRITEFUNCTION
这个选项设置 libcurl在收到数据时应该调用哪个函数。
size_t write_callback(char* ptr, size_t size, size_t nmemb, void* userdata);
//ptr: 指向返回的数据缓冲区的指针
//size: 每个元素的大小(通常是1)
//nmemb: 元素个数(所以实际大小是 size * nmemb)
//userdata: 你通过 CURLOPT_WRITEDATA 传入的自定义数据指针
/*返回值:必须返回实际处理的字节数(size * nmemb),否则libcurl会认为写入失败*/
CURLOPT_WRITEDATA
这个选项设置的是传给 write_callback
的最后一个参数 userdata
。
- 通常你会把一个
std::string*
或文件指针传进去,用于在回调中存储数据。 - 例如:你想把返回数据写入
std::string buffer;
,就把&buffer
作为WRITEDATA
。
CURLOPT_HEADERFUNCTION,CURLOPT_HEADERDATA
回调函数原型为 :
size_t function( void *ptr, size_t size,size_t nmemb, void *stream);
libcurl一旦接收到http头部数据后将调用该函数。
CURLOPT_WRITEDATA 传递指针给libcurl,该指针表明CURLOPT_HEADERFUNCTION函数的stream指针的来源。
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
typedef unsigned int bool;
#define true 1
#define false 0
/*回调函数固定格式*/
size_t datafun(char* ptr, size_t size, size_t nmemb, void* userdata) {
char buffer[1024]={'\0'};
strncpy(buffer,ptr,1024);
printf("====================get DATA=======================\n");
printf("%s\n",buffer);
}
bool getURL(const char* filename){
FILE *fp;
CURL *curl;
CURLcode res;
if((fp=fopen(filename,"w"))== NULL){
return false;
}
/*初始化*/
curl=curl_easy_init();
if(curl){
/*设置属性*/
struct curl_slist* headers=NULL;
headers=curl_slist_append(headers,"Accept:Agent-007");
//HTTPHEADER---别忘了HTTP
curl_easy_setopt(curl,CURLOPT_HTTPHEADER,headers);
//CURLOPT_URL
curl_easy_setopt(curl,CURLOPT_URL,"http://www.baidu.com");
//两个DATA
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,datafun);
/*执行*/
res=curl_easy_perform(curl);
if(res != CURLE_OK){
fprintf(stderr,"curl_easy_perform() fails:%s",curl_easy_strerror(res));
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
return false;
}
fclose(fp);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
return true;
}
}
int main(){
getURL("mm.html");
return 0;
}
size_t headfun(char* ptr, size_t size, size_t nmemb, void* userdata) {
char buffer[1024]={'\0'};
strncpy(buffer,ptr,1024);
printf("====================get head=======================\n");
printf("%s\n",buffer);
}
curl_easy_setopt(curl,CURLOPT_HEADERFUNCTION,headfun);
get head===
HTTP/1.1 200 OK