文章目录
前言
基于libcurl封装http客户端接口,发送POST、GET、PUT、DELETE请求,上传或下载文件,C、C++编程语言可用。
一、安装libcurl库
1. 方式1:ubuntu系统在线下载
apt-get install curl
apt-get install libcurl4-openssl-dev
apt-get install libssl-dev
2. 方式2:交叉编译
#下载源码
wget https://curl.haxx.se/download/curl-7.71.1.tar.gz
tar -zxf curl-7.71.1.tar.gz
cd curl-7.71.1
mkdir my_build
#路径需根据实际修改
//--prefix:【绝对路径】指定安装路径
// CC、CXX:你的交叉编译工具路径
//--with-ssl:openssl 头文件和库的路径
./configure --host=aarch64-linux \
CC=/home/share/06-cross_tools/01-Jeson-NANO/tool/bin/aarch64-linux-gnu-gcc \
CXX=/home/share/06-cross_tools/01-Jeson-NANO/tool/bin/aarch64-linux-gnu-g++ \
--prefix=$(pwd)/my_build \
--with-ssl=/home/zhouyihui/openssl-1.1.1a/my_build \
--enable-shared --enable-static
#编译安装
make clean
make -j4
make install
二、接口代码
1. curl_http_client.h
/********************************
func:基于libcurl,封装http客户端接口
author:zyh
data:2023.3.30
*********************************/
#ifndef _CURL_HTTP_CLIENT_H_
#define _CURL_HTTP_CLIENT_H_
#ifdef __cplusplus
extern "C" {
#endif
//#include "curl.h"
#define CONTENT_TYPE_FORM_DATA (char *)"Content-Type:multipart/form-data"
#define CONTENT_TYPE_APP_JSON (char *)"Content-Type:application/json"
#define CONTENT_TYPE_APP_JSON_UTF8 (char *)"Content-Type:application/json;charset=utf-8"
typedef struct {
char *buf;//返回的正文内容
size_t size;//正文内容大小
long code;//服务器返回的状态码
} response_msg_t;//响应返回的消息体
void free_response(response_msg_t *response);
/**
函数功能:http客户端发送请求
入参:
url:URL的地址
method:method请求方式("POST"、"GET"、"PUT"、"DELETE")
body:正文,若无, 则填NULL
header:HTTP头
timeOut_s:请求超时时间(秒)
出参:response:响应返回的消息,需要手动释放空间,见free_response()
返回:成功返回0,失败-1
**/
int http_client_request(char *url, char *method, char *body, char *header, int timeOut_s, response_msg_t *response);
/**
函数功能:下载文件
入参:
出参:无
返回:成功0,失败-1
**/
int http_client_download_file(char *url, char *file, int timeOut_s);
/**
函数功能:“表单方式”上传文件到服务器
入参:url:地址
method:method请求方式("POST"、"GET"、"PUT"、"DELETE")
header:HTTP头
timeOut_s:请求超时时间(秒)
copyName:上传文件的关键字段,对应file
file:为上传文件
n: 上传文件个数
出参:无
返回:成功返回0,失败-1
**/
int http_client_upload_files(char *url, char *method, char *header, int timeOut_s, char **copyName, char **file, int n);
#ifdef __cplusplus
}
#endif
#endif
2. curl_http_client.c
/********************************
func:基于libcurl,封装http客户端接口
author:zyh
data:2023.3.30
*********************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/stat.h>
//#include <stdbool.h>
#include "curl.h"
#include "curl_http_client.h"
//得到下载目标文件的大小的函数
long getDownloadFileLenth(const char *url)
{
long downloadFileLenth = 0;
CURL *handle = curl_easy_init();
curl_easy_setopt(handle, CURLOPT_URL, url);
curl_easy_setopt(handle, CURLOPT_HEADER, 1); //只需要header头
curl_easy_setopt(handle, CURLOPT_NOBODY, 1); //不需要body
if (curl_easy_perform(handle) == CURLE_OK) {
curl_easy_getinfo(handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &downloadFileLenth);
} else {
downloadFileLenth = -1;
}
curl_easy_cleanup(handle);
return downloadFileLenth;
}
static size_t write_file(void *ptr, size_t size, size_t nmemb, void *stream)
{
return fwrite(ptr, size, nmemb, (FILE *)stream);
}
/**
函数功能:下载文件
入参:
出参:无
返回:成功0,失败-1
**/
int http_client_download_file(char *url, char *file, int timeOut_s)
{
int ret = -1;
FILE *fp = NULL;
CURL *curl = NULL;
CURLcode res = CURLE_OK;
curl = curl_easy_init(); // 初始化CURL
if (NULL == curl) {
goto done;
}
fp = fopen(file, "wb"); // 二进制写模式打开文件
if (fp == NULL) {
fprintf(stderr, "Cannot open file %s\n", file);
goto done;
}
// 设置libcurl选项
curl_easy_setopt(curl, CURLOPT_URL, url);// 设置下载URL
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3); //设置连接超时,CURLOPT_CONNECTTIMEOUT秒、CURLOPT_CONNECTTIMEOUT_MS 毫秒
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeOut_s);// 整个CURL 执行的时间, CURLOPT_TIMEOUT秒、CURLOPT_TIMEOUT_MS毫秒
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_file);// 设置写数据的函数
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);// 设置写数据的文件指针
// SSL选项设置,如果是https的url则需要设置
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
res = curl_easy_perform(curl); // 执行下载
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
goto done;
}
ret = 0;
done:
if (fp) {
fclose(fp); // 关闭文件
if (0 > ret) {
remove(file);//失败则删除文件
}
}
if (curl) {
curl_easy_cleanup(curl); // 清理CURL
}
return ret;
}
void free_response(response_msg_t *response)
{
if (response->buf) {
free(response->buf);
response->buf = NULL;
}
}
static size_t write_buf(void *ptr, size_t size, size_t nmemb, void *userp)
{
response_msg_t *response = (response_msg_t *)userp;
size_t n = size * nmemb;
size_t new_size = response->size + n;
char *p = (char *)realloc(response->buf, new_size + 1);
if (!p) {
/* out of memory! */
printf("not enough memory (realloc returned NULL)\n");
return 0;
}
response->buf = p;
memcpy(response->buf + response->size, ptr, n);
response->buf[new_size] = '\0';
response->size = new_size;
return n;
}
/**
函数功能:http客户端发送请求
入参:
url:URL的地址
method:method请求方式("POST"、"GET"、"PUT"、"DELETE")
body:正文,若无, 则填NULL
header:HTTP头
timeOut_s:请求超时时间(秒)
出参:response:响应返回的消息,需要手动释放空间,见free_response()
返回:成功返回0,失败-1
**/
int http_client_request(char *url, char *method, char *body, char *header, int timeOut_s, response_msg_t *response)
{
int ret = -1;
CURL *curl = NULL;
struct curl_slist *headers = NULL;
response->size = 0;
response->code = 0;
response->buf = (char *)calloc(1, sizeof(char));
if (!response->buf) {
fprintf(stderr, "calloc failed\n");
goto done;
}
/*
*在使用libcurl库函数之前,应该先调用一次curl_global_init()函数来初始化libcurl库,
*没有调用的话curl_easy_init()会自动调用。
*该函数并不是线程安全的,所以在多线程模式下最好放在主线程中调用一次。
*与curl_global_cleanup()配对使用。
*/
//curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (NULL == curl) {
goto done;
}
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3); //设置连接超时,CURLOPT_CONNECTTIMEOUT秒、CURLOPT_CONNECTTIMEOUT_MS 毫秒
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeOut_s); // 整个CURL 执行的时间, CURLOPT_TIMEOUT秒、CURLOPT_TIMEOUT_MS毫秒
curl_easy_setopt(curl, CURLOPT_URL, url); //url地址
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);// 此选项设置为`1L`(或任何非零值)时,告诉libcurl在处理HTTP响应时遵循服务器返回的任何"Location:"头中定义的重定向
curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");//用于设定一个默认协议,这个协议将在没有明确指定协议的情况下使用
if (0 == strncmp(url, "https://", 8)) {//SSL选项设置,如果是https的url则需要设置
#if 1
// 方法1, 设定为不验证证书和HOST
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
#else
// 方法2, 设定一个SSL判别证书
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(curl,CURLOPT_CAINFO,"ca-cert.pem"); // TODO: 设置一个证书文件
#endif
}
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method);//方法
if (body) {
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(body));
}
headers = curl_slist_append(headers, header);//设置头,根据实际修改
if (NULL == headers) {
goto done;
}
//headers = curl_slist_append(headers, "Expect:");//再加,防止Expect:
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);//添加协议头
/* send all data to this function */
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_buf);//设置写数据的函数
/* we pass our 'chunk' struct to the callback function */
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)response);//设置写数据的指针
//curl_easy_setopt(curl, CURLOPT_VERBOSE, 0); // 是否打印调试信息,0不打印,1打印,不设置的话默认是0
//curl_easy_setopt(curl, CURLOPT_HEADER, 0); // 是否输出响应头
if (CURLE_OK != curl_easy_perform(curl)) {
goto done;
}
//获取网络请求响应信息
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response->code);//状态码
if (200 != response->code) {//判断一下http的状态码是否对
goto done;
}
ret = 0;
done:
if (headers) {
curl_slist_free_all(headers);
headers = NULL;
}
if (curl) {
curl_easy_cleanup(curl);
curl = NULL;
}
//curl_global_cleanup();
return ret;
}
/**
函数功能:“表单方式”上传文件到服务器
入参:url:地址
method:method请求方式("POST"、"GET"、"PUT"、"DELETE")
header:HTTP头
timeOut_s:请求超时时间(秒)
copyName:上传文件的关键字段,对应file
file:为上传文件
n: 上传文件个数
出参:无
返回:成功返回0,失败-1
**/
int http_client_upload_files(char *url, char *method, char *header, int timeOut_s, char **copyName, char **file, int n)
{
int ret = -1;
int i = 0;
CURL *curl = NULL;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method);
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeOut_s);// 整个CURL 执行的时间, CURLOPT_TIMEOUT秒, CURLOPT_TIMEOUT_MS毫秒
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, header);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_mime *mime = NULL;
curl_mimepart *part = NULL;
mime = curl_mime_init(curl);
if (mime) {
for (i = 0; i < n; i++) {
part = curl_mime_addpart(mime);
curl_mime_name(part, copyName[i]);
curl_mime_filedata(part, file[i]);
}
curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
if (CURLE_OK == curl_easy_perform(curl)) {
ret = 0;
}
}
if (mime) {
curl_mime_free(mime);
}
if (headers) {
curl_slist_free_all (headers);
}
curl_easy_cleanup(curl);
}
return ret;
}
3. demo.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "curl_http_client.h"
#include "curl.h"
#include <sys/stat.h>
int main(int argc, char *argv[])
{
int ret = 0;
char *method = (char *)"POST";
char *url = (char *)"http://10.10.70.95:87/roadIn";
char *body = (char *)"test test test!";
char *header = CONTENT_TYPE_APP_JSON;
int timeOut_s = 4;
response_msg_t response = {0};
if (argc > 1) {
method = argv[1];
}
if (argc > 2) {
url = argv[2];
}
if (argc > 3) {
body = argv[3];
}
if (argc > 4) {
header = argv[4];
}
printf("you can input: ./xxx {method} {'url'} {'body'} {'header'}\n\n");
printf("method=%s\n", method);//请求的方法
printf("url=%s\n", url);//请求的地址
printf("body=%s\n", body);//请求的body正文
printf("header=%s\n", header);//请求的http头,比如Content-Type:application/json
printf("\n");
/*
*在使用libcurl库函数之前,应该先调用一次curl_global_init()函数来初始化libcurl库,
*没有调用的话curl_easy_init()会自动调用。
*该函数并不是线程安全的,所以在多线程模式下最好放在主线程中调用一次。
*与curl_global_cleanup()配对使用。
*/
curl_global_init(CURL_GLOBAL_ALL);
/*发送请求1*/
ret = http_client_request(url, method, body, header, timeOut_s, &response);
if (0 == ret) {
printf("成功, code=%ld, size==%ld, buf=%s\n", response.code, response.size, response.buf);
} else {
printf("失败, code=%ld, size==%ld, buf=%s\n", response.code, response.size, response.buf);
}
#if 0 //返回内容保存到文件中
FILE *fp = fopen("1.png", "w");
fwrite(response.buf, 1, response.size, fp);
fclose(fp);
#endif
free_response(&response);
/*发送请求2*/
//...
curl_global_cleanup();//与curl_global_cleanup()配对使用
return 0;
}