libcurl编程,主要采用callback function(回调函数) 的形式完成传输任务,用户在启动传输前设置好各类参数
和回调函数,当满足条件时 libcurl 将调用用户的回调函数实现特定功能。
下面是利用libcurl 完成传输任务流程:
1. curl_global_init(); //初始化libcurl 创造全局句柄
2. curl_easy_init(); //函数得到easy interface型指针 创造局部变量句柄
3. curl_easy_setopt(); //设置传输选项 给句柄设置一些参数
4. curl_easy_setopt(); //设置传输选项 实现回调函数以完成用户特定任务
5. curl_easy_perform(); //完成传输任务 提交句柄 阻塞等待
6. curl_easy_cleanup(); //释放内存
get请求例子:
//get请求
#include <stdio.h>
#include <stdlib.h>
#include <curl.h>
/*
* ptr 表示收到服务器返回数据的首地址
* size 表示返回每个数据的大小
* nmemb 表示返回数据的个数
* userdata 用户给该回调函数传递的形参 curl_easy_setopt(curl, CURLOPT_WRITEDATA, "abc"); 设置的字符串"abc"
* 这个可以用来标识传输命令 返回的数据 来自命令 "abc",根据这个命令来处理这个数据
*/
size_t write_callback(char *ptr, size_t size, size_t nmemb, void* userdata) {
long sizes = size * nmemb;
char* recv = new char[sizes];
memcpy(recv, (char*)ptr, sizes);//复制传过来的数据
printf("传过来的数据: %s", recv);
return sizes;
}
int main(void) {
//1. 创建一个curl句柄
CURL* curl = nullptr;
CURLcode res;
//2. 初始化一个curl句柄
curl = curl_easy_init();
//3. 给该句柄设定一些参数 (封装一个http请求消息)
curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:8888"); //http://www.baidu.com
//curl_easy_setopt(curl, CURLOPT_URL, "http:/101.200.190.150:8090/login?username=123&passwd=445");
//给当前句柄设置一个 处理从服务器返回数据的回调函数
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
//给回调函数传递一个形参
curl_easy_setopt(curl, CURLOPT_WRITEDATA, "abc");
//4. 将curl句柄 向远程服务器 提交请求 并得到一个返回值
res = curl_easy_perform(curl); //阻塞等待服务器返回
if(res != CURLE_OK) {
printf("curl easy perform error res = %d\n", res);
return 1;
}
//5. 处理服务器返回数据
//6. 清空 释放句柄内存空间
curl_easy_cleanup(curl);
return 0;
}
post请求例子:
#include <stdio.h>
#include <stdlib.h>
#include <curl.h>
#define POSTDATA "{\"username\" : \"gailun\", \"password\" : \"123123\"}"
int main(void) {
//1. 创建一个curl句柄
CURL* curl = nullptr;
CURLcode res;
//2. 初始化一个curl句柄
curl = curl_easy_init();
if(nullptr == curl) {
printf("curl init error");
return 0;
}
//3. 给该句柄设定一些参数 (封装一个http请求消息)
curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:8888"); //http://www.baidu.com
//给当前curl变成post请求
curl_easy_setopt(curl, CURLOPT_POST, 1);
//给当前curl设置需要传递post数据
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, POSTDATA);
//4. 将curl句柄 向远程服务器 提交请求 并得到一个返回值
res = curl_easy_perform(curl); //阻塞等待服务器返回
if(res != CURLE_OK) {
printf("curl easy perform error res = %d\n", res);
return 1;
}
//5. 处理服务器返回数据
//6. 清空 释放句柄内存空间
curl_easy_cleanup(curl);
return 0;
}
下面转载其他博客对函数进行详细说明:
1>: curl_global_init();//全局初始化 函数
应用程序在使用libcurl之前,必须先初始化libcurl。libcurl只需初始化一次。可以使用以下语句进行初始化:
curl_global_init();
curl_global_init()接收一个参数(三种),告诉libcurl如何初始化。
参数CURL_GLOBAL_ALL: 会使libcurl初始化所有的子模块和一些默认的选项,通常这是一个比较好的默认参数值。还有两个可选值:
参数CURL_GLOBAL_WIN32:只能应用于Windows平台。它告诉libcurl初始化winsock库。如果winsock库没有正确地初始化,应用程序就不能使用socket。在应用程序中,只要初始化一次即可。
参数CURL_GLOBAL_SSL:如果libcurl在编译时被设定支持SSL,那么该参数用于初始化相应的SSL库。同样,在应用程序中,只要初始化一次即可。
libcurl有默认的保护机制,如果在调用curl_easy_perform时它检测到还没有通过curl_global_init进行初始化,libcurl会根据当前的运行时环境,自动调用全局初始化函数。但必须清楚的是,让系统自已初始化不是一个好的选择。
当应用程序不再使用libcurl的时候,应该调用curl_global_cleanup来释放相关的资源。
在程序中,应当避免多次调用curl_global_init和curl_global_cleanup。它们只能被调用一次。
2>:curl_version_info() //返回的结构体来获取运行时的具体信息
curl_version()
//通常用法:
cout<< curl_version()<<endl;//当前版本的字符串描述
//当前版本的详细信息
curl_version_info_data *pversion = curl_version_info(CURLVERSION_NOW);
//---------------------------easy interface
首先介绍libcurl中被称为easy interface的api函数,所有这些函数都是有相同的前缀:curl_easy 。
3>:curl_easy_init();
要使用easy interface,首先必须创建一个easy handle,easy handle用于执行每次操作。基本上,每个线程都应该有自己的easy handle用于数据通信(如果需要的话)。千万不要在多线程之间共享同一个easy handle。下面的函数用于获取一个easy handle :
CURL *easy_handle = curl_easy_init();
在easy handle上可以设置属性和操作(action)。easy handle就像一个逻辑连接,用于接下来要进行的数据传输。
4>:curl_easy_setopt();
使用curl_easy_setopt函数可以设置easy handle的属性和操作,这些属性和操作控制libcurl如何与远程主机进行数据通信。一旦在easy handle中设置了相应的属性和操作,它们将一直作用该easy handle。也就是说,重复使用easy hanle向远程主机发出请求,先前设置的属性仍然生效。
easy handle的许多属性使用字符串(以/0结尾的字节数组)来设置。通过curl_easy_setopt函数设置字符串属性时,libcurl内部会自动拷贝这些字符串,所以在设置完相关属性之后,字符串可以直接被释放掉(如果需要的话)。
easy handle最基本、最常用的属性是URL。你应当通过CURLOPT_URL属性提供适当的URL:
curl_easy_setopt(easy_handle, CURLOPT_URL, "http://blog.csdn.net/JGood ");
假设你要获取URL所表示的远程主机上的资源。你需要写一段程序用来完成数据传输,你可能希望直接保存接收到的数据而不是简单的在输出窗口中打印它们。所以,你必须首先写一个回调函数用来保存接收到的数据。回调函数的原型如下:
size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp);
可以使用下面的语句来注册回调函数,回调函数将会在接收到数据的时候被调用:
curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, write_data);
可以给回调函数提供一个自定义参数,libcurl不处理该参数,只是简单的传递:
curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, &internal_struct);
如果你没有通过CURLOPT_WRITEFUNCTION属性给easy handle设置回调函数,libcurl会提供一个默认的回调函数,它只是简单的将接收到的数据打印到标准输出。你也可以通过CURLOPT_WRITEDATA属性给默认回调函数传递一个已经打开的文件指针,用于将数据输出到文件里。
curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, *fp);
上传数据到远程站点
libcurl提供协议无关的方式进行数据传输。所以上传一个文件到FTP服务器,跟向HTTP服务器提交一个PUT请求的操作方式是类似的:
1. 创建easy handle或者重用先前创建的easy handle。
2. 设置CURLOPT_URL属性。
3. 编写回调函数。在执行上传的时候,libcurl通过回调函数读取要上传的数据。(如果要从远程服务器下载数据,可以通过回调来保存接收到的数据。)回调函数的原型如下:
size_t function(char *bufptr, size_t size, size_t nitems, void *userp);
bufptr指针表示缓冲区,用于保存要上传的数据,size * nitems是缓冲区数据的长度,userp是一个用户自定义指针,libcurl不对该指针作任何操作,它只是简单的传递该指针。可以使用该指针在应用程序与libcurl之间传递信息。
4. 注册回调函数,设置自定义指针。语法如下:
// 注册回调函数
curl_easy_setopt(easy_handle, CURLOPT_READFUNCTION, read_function);
// 设置自定义指针
curl_easy_setopt(easy_handle, CURLOPT_READDATA, &filedata);
5. 告诉libcurl,执行的是上传操作。
curl_easy_setopt(easy_handle, CURLOPT_UPLOAD, 1L);
有些协议在没有预先知道上传文件大小的情况下,可能无法正确判断上传是否结束,所以最好预先使用CURLOPT_INFILESIZE_LARGE属性:告诉它要上传文件的大小:
/* in this example, file_size must be an curl_off_t variable */
curl_easy_setopt(easy_handle, CURLOPT_INFILESIZE_LARGE, file_size);
6. 调用curl_easy_perform。
接下来,libcurl将会完成剩下的所有工作。在上传文件过程中,libcurl会不断调用先前设置的回调函数,用于将要上传的数据读入到缓冲区,并执行上传。
5>: curl_easy_perform(easy_handle);//将执行真正的数据通信:
curl_easy_perfrom将连接到远程主机,执行必要的命令,并接收数据。当接收到数据时,先前设置的回调函数将被调用。libcurl可能一次只接收到1字节的数据,也可能接收到好几K的数据,libcurl会尽可能多、及时的将数据传递给回调函数。回调函数返回接收的数据长度。如果回调函数返回的数据长度与传递给它的长度不一致(即返回长度 != size * nmemb),libcurl将会终止操作,并返回一个错误代码。
当数据传递结束的时候,curl_easy_perform将返回一个代码表示操作成功或失败。
easy handle在完成一次数据通信之后可以被重用。这里非常建议你重用一个已经存在的easy handle。如果在完成数据传输之后,你创建另一个easy handle来执行其他的数据通信,libcurl在内部会尝试着重用上一次创建的连接。