网络编程之http客户端(基于libcurl发送POST、GET ...、请求、上传下载文件)


前言

基于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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

勤劳的搬运工zyh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值