libcurl原创: 冬冬他哥哥
链接: https://www.cnblogs.com/xietianjiao/p/13260021.html.
一、libcurl简介
libcurl是一个跨平台的网络协议库, 支持http, https, ftp, gopher, telnet, dict, file和dap协议。libcurl同样支持HTTPS证书授权, HTTP POST, HTTP PUT, FTP上传, HTTP基本表单上传, 代理, cookies和用户认证
库下载: https://github.com/curl/curl/releases/tag/curl-7_71_1
二、编译libcurl
首先去上面的链接下载libcurl库, 之后移动到ubuntu上面
- 解压资源包: tar xvf curl-7.71.1.tar.bz2
- ./configure --prefix=$PWD/_install 安装在当前路径下的_install文件夹
- make进行编译
- sudo make install进行安装, 安装完后, 当前所在的路径就会出现_install文件夹, 里面就有libcurl的头文件和libcurl的库文件
安装路径指令详解
–prefix: 指定安装路径
–host: 指定交叉编译
例如: ./configure --prefix=$PWD/_install --host=arm-linux(指定安装在当前路径下的_install文件夹, 指定交叉编译工具是arm-linux…)
三、libcurl的使用
调用curl_global_init()初始化libcurl
调用curl_easy_init()函数得到 easy interface型指针
调用curl_easy_setopt()设置传输选项
根据curl_easy_setopt()设置的传输选项,实现回调函数以完成用户特定任务
调用curl_easy_perform()函数完成传输任务
调用curl_easy_cleanup()释放内存
四、函数简介
1、CURLcode curl_global_init(long flags);函数只能用一次。(其实在调用curl_global_cleanup 函数后仍然可再用)
如果这个函数在curl_easy_init函数调用时还没调用,它将由libcurl库自动调用,所以多线程下最好主动调用该函数以防止在线程中curl_easy_init时多次调用。
注意:虽然libcurl是线程安全的,但curl_global_init是不能保证线程安全的,所以不要在每个线程中都调用curl_global_init,应该将该函数的调用放在主线程中。
参数:flags
CURL_GLOBAL_ALL //初始化所有的可能的调用。
CURL_GLOBAL_SSL //初始化支持 安全套接字层。
CURL_GLOBAL_WIN32 //初始化win32套接字库。
CURL_GLOBAL_NOTHING //没有额外的初始化。
2、void curl_global_cleanup(void);
结束libcurl使用的时候,用来对curl_global_init做的工作清理。类似于close的函数。
注意:虽然libcurl是线程安全的,但curl_global_cleanup是不能保证线程安全的,所以不要在每个线程中都调用curl_global_init,应该将该函数的调用放在主线程中。
3、char *curl_version( );
打印当前libcurl库的版本。
4、CURL curl_easy_init( );
curl_easy_init用来初始化一个CURL的指针(有些像返回FILE类型的指针一样). 相应的在调用结束时要用curl_easy_cleanup函数清理.
一般curl_easy_init意味着一个会话的开始. 它会返回一个easy_handle(CURL对象), 一般都用在easy系列的函数中.
5、void curl_easy_cleanup(CURL *handle);
这个调用用来结束一个会话.与curl_easy_init配合着用.
参数:
CURL类型的指针.
6、CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
这个函数最重要了.几乎所有的curl 程序都要频繁的使用它.它告诉curl库.程序将有如何的行为. 比如要查看一个网页的html代码等.(这个函数有些像ioctl函数)参数:
1 CURL类型的指针
2 各种CURLoption类型的选项.(都在curl.h库里有定义,man 也可以查看到)
3 parameter 这个参数 既可以是个函数的指针,也可以是某个对象的指针,也可以是个long型的变量.它用什么这取决于第二个参数.
CURLoption 这个参数的取值很多.具体的可以查看man手册.
7、CURLcode curl_easy_perform(CURL *handle);
在初始化CURL类型的指针 以及curl_easy_setopt完成后调用. 就像字面的意思所说perform就像是个舞台.让我们设置的
option 运作起来.参数:
CURL类型的指针.
五、curl_easy_setopt函数部分选项介绍
本节主要介绍curl_easy_setopt中跟http相关的参数。该函数是curl中非常重要的函数,curl所有设置都是在该函数中完成的,该函数的设置选项众多,注意本节的阐述的只是部分常见选项。
1、CURLOPT_URL
设置访问URL
2、CURLOPT_WRITEFUNCTION,CURLOPT_WRITEDATA
回调函数原型为:size_t function( void *ptr, size_t size, size_t nmemb, void *stream); 函数将在libcurl接收到数据后被调用,因此函数多做数据保存的功能,如处理下载文件。CURLOPT_WRITEDATA 用于表明CURLOPT_WRITEFUNCTION函数中的stream指针的来源。
如果你没有通过CURLOPT_WRITEFUNCTION属性给easy handle设置回调函数,libcurl会提供一个默认的回调函数,它只是简单的将接收到的数据打印到标准输出。你也可以通过 CURLOPT_WRITEDATA属性给默认回调函数传递一个已经打开的文件指针,用于将数据输出到文件里。
3、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指针的来源。
4、CURLOPT_READFUNCTION CURLOPT_READDATA
libCurl需要读取数据传递给远程主机时将调用CURLOPT_READFUNCTION指定的函数,函数原型是:size_t function(void *ptr, size_t size, size_t nmemb,void *stream). CURLOPT_READDATA 表明CURLOPT_READFUNCTION函数原型中的stream指针来源。
5、CURLOPT_NOPROGRESS,CURLOPT_PROGRESSFUNCTION,CURLOPT_PROGRESSDATA
跟数据传输进度相关的参数。CURLOPT_PROGRESSFUNCTION 指定的函数正常情况下每秒被libcurl调用一次,为了使CURLOPT_PROGRESSFUNCTION被调用,CURLOPT_NOPROGRESS必须被设置为false,CURLOPT_PROGRESSDATA指定的参数将作为CURLOPT_PROGRESSFUNCTION指定函数的第一个参数
6、CURLOPT_TIMEOUT,CURLOPT_CONNECTIONTIMEOUT:
CURLOPT_TIMEOUT 由于设置传输时间,CURLOPT_CONNECTIONTIMEOUT 设置连接等待时间
7、CURLOPT_FOLLOWLOCATION
设置重定位URL
8、CURLOPT_RANGE: CURLOPT_RESUME_FROM:
断点续传相关设置。CURLOPT_RANGE 指定char *参数传递给libcurl,用于指明http域的RANGE头域,例如:
表示头500个字节:bytes=0-499
表示第二个500字节:bytes=500-999
表示最后500个字节:bytes=-500
表示500字节以后的范围:bytes=500-
第一个和最后一个字节:bytes=0-0,-1
同时指定几个范围:bytes=500-600,601-999
CURLOPT_RESUME_FROM 传递一个long参数给libcurl,指定你希望开始传递的 偏移量。
六、 curl_easy_perform 函数说明(error 状态码)
该函数是完成curl_easy_setopt指定的所有选项,本节重点介绍curl_easy_perform的返回值。返回0意味一切ok,非0代表错误发生。主要错误码说明:
1、CURLE_OK
任务完成一切都好
2、CURLE_UNSUPPORTED_PROTOCOL
不支持的协议,由URL的头部指定
3、CURLE_COULDNT_CONNECT
不能连接到remote 主机或者代理
4、CURLE_REMOTE_ACCESS_DENIED
访问被拒绝
5、CURLE_HTTP_RETURNED_ERROR
Http返回错误
6、CURLE_READ_ERROR
读本地文件错误
要获取详细的错误描述字符串,可以通过const char *curl_easy_strerror(CURLcode errornum ) 这个函数取得.
七、基础代码片
下面展示一些 内联代码片
。
#include <stdio.h>
#include <curl/curl.h>
bool getUrl(char *filename)
{
CURL *curl;
CURLcode res;
FILE *fp;
if ((fp = fopen(filename, "w")) == NULL) // 返回结果用文件存储
return false;
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Accept: Agent-007");
curl = curl_easy_init(); // 初始化
if (curl)
{
//curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:8080");// 代理
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);// 改协议头
curl_easy_setopt(curl, CURLOPT_URL,"http://www.baidu.com");
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); //将返回的http头输出到fp指向的文件
curl_easy_setopt(curl, CURLOPT_HEADERDATA, fp); //将返回的html主体数据输出到fp指向的文件
res = curl_easy_perform(curl); // 执行
if (res != 0) {
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
fclose(fp);
return true;
}
}
bool postUrl(char *filename)
{
CURL *curl;
CURLcode res;
FILE *fp;
if ((fp = fopen(filename, "w")) == NULL)
return false;
curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt"); // 指定cookie文件
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "&logintype=uid&u=xieyan&psw=xxx86"); // 指定post内容
//curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:8080");
curl_easy_setopt(curl, CURLOPT_URL, " http://mail.sina.com.cn/cgi-bin/login.cgi "); // 指定url
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
fclose(fp);
return true;
}
int main(void)
{
getUrl("/tmp/get.html");
postUrl("/tmp/post.html");
}
八、人脸识别
首先进入curl包里面删除旧的_install包, 因为之前编译的包没用, 不支持https的访问, docs文件夹里面的INSTALL.md文件明确告诉我们如果想要支持SSL, 就要用./configure --with-ssl指令, 要有这个–with-ssl参数选项, 如果加了运行指令后出现configure: error: --with-ssl was given but OpenSSL could not be detected的提示错误, 两种情况, 一种是没有安装openSSL, 因为要驱动openSSL, 你得先有openSSL, 另一种是编译环境没有加, 默认路径是在/usr/local/ssl底下
HTTPS 协议是由 SSL+HTTP 协议构建的
要让curl支持http加密, 它有依赖库, 系统得先有SSL, 才能去支持SSL
wget, 一个小窍门, 让linux的开源工具去linux的源码下面去找
安装OpenSSL: wget https://www.openssl.org/source/openssl-1.1.1a.tar.gz, 安装前提, 得先能上网, 不然会提示无法解析, 安装完后继续执行
相关博文: https://blog.csdn.net/weixin_38184741/article/details/86554438
避免在安装curl的时候要配置ssl的路径, 直接把ssl默认安装到系统路径底下去
解压, 进入openssl文件夹, 之后./config, make编译, sudo make install安装, 安装的时候记得要sudo, 默认是安装到/usr/local底下去, 因为在工作目录底下才有权限, 在/usr/local底下没有
上面操作放到树莓派上面都一样
回到curl文件夹, 重新编译libcurl, 系统现在已经有了ssl, 输入./configure --prefix=$PWD/_install --with-ssl, 之后make, 再之后make install
安装完之后, 重新编译文件, 这时候就能进行人脸识别了
下面展示一些 内联代码片
。
/*人脸识别代码, 人脸是识别有两种方式, 一种是base64流, 一种是file文档, 这里我用到的是base64流*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <curl/curl.h>
#define true 1
#define false 0
typedef unsigned int bool;
char buf[1024] = {'\0'};
size_t readData( void *ptr, size_t size, size_t nmemb, void *stream)
{
strncpy(buf, ptr, 1024); //将运行完后的结果复制到buf中
//printf("==============================getData=============================\n");
//printf("%s\n",buf);
}
char *getPicBase64File(char *picFile)
{
char cmd[32] = {'\0'};
sprintf(cmd, "base64 %s > tmpFile", picFile); //base64指令, 运行完后将流放进tmpFile文件里
system(cmd); //运行指令
int fd = open("./tmpFile",O_RDWR); //打开流文件
int fileLen = lseek(fd, 0, SEEK_END); //计算文件大小
lseek(fd, 0, SEEK_SET); //计算完文件大小后, 光标的位置在最后, 我们接下来要做读操作, 所以把光标移到开头
char *bufPic = (char *)malloc(fileLen); //定义一个跟流文件同样大小的字符串
memset(bufPic, 0, fileLen);
read(fd, bufPic, fileLen); //将文件里面的内容写入新创建的字符串里
close(fd); //关闭文件
system("rm ./tmpFile"); //删除tmpFile文件, 用完就删除
return bufPic;
}
bool postUrl()
{
CURL *curl;
CURLcode res;
char *postString;
/*base64流有六个参数, 详细可以见翔云人脸识别API文档*/
char img1[32] = {'\0'}; //上传的文件(base64流, 用base64 ***.jpg生成)
char img2[32] = {'\0'}; //上传的文件(base64流, 用base64 ***.jpg生成)
char *key = ""; //ocrkey, 人脸识别需要用到的参数之一, 在翔云个人中心可以查到(我用的是翔云), 进行识别前记得先购买, 一毛一百次
char *secret = ""; //ocrsecret, 人脸识别需要用到的参数之一, 在翔云个人中心可以查到, 进行识别前记得先购买, 一毛一百次
(我用的是翔云)
int typeId = 21; //对应的类型, 人脸识别对应的id是21
char *format = "xml"; //返回格式有两种, 一种是xml, 一种是json, 我们用的是xml
char *bufPic1 = getPicBase64File("./reba1.jpg"); //调用函数, 获取图片的base64流
char *bufPic2 = getPicBase64File("./reba2.jpg"); //调用函数, 获取图片的base64流
int len = strlen(key)+strlen(secret)+strlen(bufPic1)+strlen(bufPic2)+128; //算出六个参数所需要用到的空间大小
postString = (char *)malloc(len);
memset(postString, 0, len);
sprintf(postString, "&img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s",
bufPic1,bufPic2,key,secret,21,format); //封装post内容
curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString); // 指定post内容
curl_easy_setopt(curl, CURLOPT_URL, "https://netocr.com/api/faceliu.do"); // 指定url
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, readData); //将返回的html主体数据用函数读出
res = curl_easy_perform(curl);
printf("OK:%d\n",res);
/*找出返回数据的内容中是否带有是或否, 进行判断*/
if(strstr(buf,"是") != NULL){
printf("the same person\n"); //是同一个人
}else{
printf("diff person\n"); //不是同一个人
}
curl_easy_cleanup(curl);
}
return true;
}
int main(void)
{
postUrl(); //post请求
}
九、车牌识别
车牌识别的代码跟上面人脸识别的代码一样, 只需要更换成对应参数即可实现
下面展示一些 内联代码片
。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <curl/curl.h>
#define true 1
#define false 0
typedef unsigned int bool;
char buf[1024] = {'\0'};
size_t readData( void *ptr, size_t size, size_t nmemb, void *stream)
{
strncpy(buf, ptr, 1024); //将运行完后的结果复制到buf中
printf("==============================getData=============================\n");
printf("%s\n",buf);
}
char *getPicBase64File(char *picFile)
{
char cmd[128] = {'\0'};
sprintf(cmd, "base64 %s > tmpFile", picFile); //base64指令, 运行完后将流放进tmpFile文件里
system(cmd); //运行指令
int fd = open("./tmpFile",O_RDWR); //打开流文件
int fileLen = lseek(fd, 0, SEEK_END); //计算文件大小
lseek(fd, 0, SEEK_SET); //计算完文件大小后, 光标的位置在最后, 我们接下来要做读操作, 所以把光标移到开头
char *bufPic = (char *)malloc(fileLen); //定义一个跟流文件同样大小的字符串
memset(bufPic, 0, fileLen);
read(fd, bufPic, fileLen); //将文件里面的内容写入新创建的字符串里
close(fd); //关闭文件
system("rm ./tmpFile"); //删除tmpFile文件
return bufPic;
}
bool postUrl()
{
CURL *curl;
CURLcode res;
char *postString;
/*车牌识别有五个参数, 详细可以见翔云人脸识别API文档*/
char *key = ""; //ocrkey, 车牌识别需要用到的参数之一, 在翔云个人中心可以查到(我用的是翔云), 进行识别前记得先购买, 一毛一百次
char *secret = ""; //ocrsecret, 车牌识别需要用到的参数之一, 在翔云个人中心可以查到, 进行识别前记得先购买, 一毛一百次
(我用的是翔云)
int typeId = 19; //对应的类型, 车牌识别对应的id是19
char *format = "xml"; //返回格式有两种, 一种是xml, 一种是json, 我们用的是xml
char *bufPic = getPicBase64File("./car.jpg"); //调用函数, 获取图片的base64流
int len = strlen(key)+strlen(secret)+strlen(bufPic)+128; //算出五个参数所需要用到的空间大小
postString = (char *)malloc(len);
memset(postString, 0, len);
sprintf(postString, "&img=%s&key=%s&secret=%s&typeId=%d&format=%s",
bufPic,key,secret,typeId,format); //封装post内容
curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString); // 指定post内容
curl_easy_setopt(curl, CURLOPT_URL, "https://netocr.com/api/recogliu.do"); // 指定url
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, readData); //将返回的html主体数据用函数读出
res = curl_easy_perform(curl);
printf("OK:%d\n",res);
/*找出返回数据的内容中是否带有是或否, 进行判断*/
if(strstr(buf,"是") != NULL){
//printf("the same person\n");
}else{
//printf("diff person\n");
}
curl_easy_cleanup(curl);
}
return true;
}
int main(void)
{
postUrl(); //post请求
}
运行结果:
获取车牌号代码
下面展示一些 内联代码片
。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int i = 0;
char *str = (char *)malloc(12);
memset(str, 0, 12);
int fd = open("./carfile",O_RDWR);
int len = lseek(fd, 0, SEEK_END);
char *buf = (char *)malloc(len);
memset(buf, 0, len);
lseek(fd, 0, SEEK_SET);
read(fd, buf, len);
close(fd);
/*返回的数据里面找到车牌号*/
if(strstr(buf,"车牌号") != NULL){
char *p = strstr(buf,"车牌号"); //strstr函数的作用是如果主字符串里包含子字符串, 它的返回值就是出现子字符串的位置
p = p+strlen("车牌号"); //把光标移到号字后, 没移之前的光标在车前面
p = p+11; //地址移位
for(i=0; i<9; i++){
str[i] = *p++;
}
printf("get data:%s\n",str);
}
return 0;
}
运行结果: