c/c++使用libcurl库做http客户端及封装(HTTP_GET和HTTP_POST)

由于项目需求需要发送http_post去请求数据,所以上网去寻找了一些发送http请求的方法,发现libcurl较为常用,然后根据官网上的例子,对libcurl做了一些简单的封装,支持多线程,并且有进度条显示,希望对大家有帮助,如有发现问题,欢迎批评指正~

代码(libcurl_http.h)如下:

#pragma once

#include "curl/curl.h"
#include <time.h>
#include <stdio.h>
#include <string>
#include <iostream>
#include <string>

using namespace std;

#define error(args) do{ \
						printf("[%s] - %s:%s:%d-----%s, %s\n" , "error", __FILE__, __FUNCTION__,  __LINE__, strerror(errno), args); \
} while(0)



namespace INTERNET_SERVICE {

	class curl_http_send
	{
	public:
		curl_http_send();
		~curl_http_send();

		// desc: 每次接收数据内容的回调函数(当数据长度长的时候,未必能一次接收成功)
		// param: contents/数据内容 size/每个元素的大小 nmemb/元素的个数 pcurl/为用户自定义指针 
		// return: 接收数据的总数
		static size_t receive_data(void * contents, size_t size, size_t nmemb, void *pcurl);

		// desc: 上传下载进度的回调函数
		// param: clientp/为用户自定义指针 dltotal/为需要下载的总数 dlnow/当前下载的总数 ultotal/为需要上传的总数 ulnow/当前上传的总数
		// return: 返回0,此函数返回非0值将会中断传输
		static int progress_callback(void * clientp,double dltotal,double dlnow,double ultotal,double ulnow);
		
		// desc: 接收报头数据的回调函数
		// param: buffer/数据内容 size/每个元素的大小 nitems/元素的个数 userdata/为用户自定义指针 
		// return: 接收报头数据的总数
		static size_t header_callback(char *buffer, size_t size, size_t nitems, void *userdata);

		// desc: httpget请求
		// param: strUrl/申请数据的地址 nTimeout/超时时间
		// return: 返回数据
		string HttpGet(const std::string & strUrl, const long nTimeout=10);

		// desc: httppost请求
		// param: strUrl/申请数据的地址 send_data/发送的数据 nTimeout/超时时间
		// return: 返回数据
		string HttpPost(const std::string & strUrl, const char * send_data, const long nTimeout=10);


	private:
		curl_http_send(curl_http_send &&);
		curl_http_send(const curl_http_send &);
		curl_http_send &operator=(curl_http_send &&);
		curl_http_send &operator=(const curl_http_send &);

	private:
		volatile double buf_size;				//接收到数据的总大小
		string data;							//接收到数据
	};
}

代码(libcurl_http.cpp)如下:

#include "curl_http_post.h"
#include <stdlib.h>
#include <string.h>

namespace INTERNET_SERVICE {

	curl_http_send::curl_http_send():buf_size(0){
	}

	curl_http_send::~curl_http_send()
	{
	}

	size_t curl_http_send::receive_data(void * contents, size_t size, size_t nmemb, void *pcurl)
	{
		FILE *f_p;
		curl_http_send *curl_p = (curl_http_send *)pcurl;

		//将接收到数据写到data.txt中记录
		f_p = fopen("data.txt", "a+");
		if (!f_p) {
			error("data.txt open failure\n");
		}
		fwrite(contents, size*nmemb, 1, f_p);
		fclose(f_p);

		//将接收到的数据写入data中
		curl_p->data.append((char*)contents, size * nmemb);

		return size * nmemb;
	}

	int curl_http_send::progress_callback(void * clientp, double dltotal, double dlnow, double ultotal, double ulnow)
	{
		curl_http_send *curl_p = (curl_http_send *)clientp;
		if(curl_p->buf_size)
			printf("\r[%u%%]", (unsigned int)((dlnow / curl_p->buf_size) * (double)100));
		return 0;
	}

	size_t curl_http_send::header_callback(char *buffer, size_t size, size_t nitems, void *userdata) {
		curl_http_send *curl_p = (curl_http_send *)userdata;
		//在报头中获取数据的总长度
		char *buf = NULL;
		char *buf_right = NULL;
		buf = strtok_s(buffer, ":", &buf_right);
		
		//将此次读取要读的长度获取出来,用于设置进度条
		if (!strncmp("data-length", buf, sizeof("data-length"))) {
			curl_p->buf_size = strtod(buf_right, NULL);	
		}

		return size * nitems;
	}

	string curl_http_send::HttpGet(const std::string & strUrl, const long nTimeout)
	{
		data.clear();
		CURLcode res;
		CURL* pCURL = curl_easy_init();

		if (!pCURL) {
			error("curl_easy_init error");
			curl_easy_cleanup(pCURL);
			return "";
		}

		//设置url
		curl_easy_setopt(pCURL, CURLOPT_URL, strUrl.c_str());
		//设置不忽略SIGPIPE信号
		curl_easy_setopt(pCURL, CURLOPT_NOSIGNAL, 1L);
		//设置超时
		curl_easy_setopt(pCURL, CURLOPT_TIMEOUT, nTimeout);
		//设置开启进度条
		curl_easy_setopt(pCURL, CURLOPT_NOPROGRESS, 0L);
		//设置用于接收数据的回调函数
		curl_easy_setopt(pCURL, CURLOPT_WRITEFUNCTION, curl_http_send::receive_data);
		//设置传递给接收函数的自定义指针
		curl_easy_setopt(pCURL, CURLOPT_WRITEDATA, this);
		//设置用于进度显示的回调函数
		curl_easy_setopt(pCURL, CURLOPT_PROGRESSFUNCTION, curl_http_send::progress_callback);
		//设置传递给进度显示函数的自定义指针
		curl_easy_setopt(pCURL, CURLOPT_PROGRESSDATA, this);
		//设置用于接收报头数据的回调函数
		curl_easy_setopt(pCURL, CURLOPT_HEADERFUNCTION, curl_http_send::header_callback);
		//设置传递给接收报头函数的自定义指针
		curl_easy_setopt(pCURL, CURLOPT_HEADERDATA, this);

		//发送数据
		res = curl_easy_perform(pCURL);

		//如果res != CURLE_OK则发送失败
		if (res != CURLE_OK) {
			error("curl_easy_perform error");
			data.clear();
		}
		else {
			//获取接收到的数据Status,如果为200则表示接收发正常,其余则不正常
			long response_code;
			curl_easy_getinfo(pCURL, CURLINFO_RESPONSE_CODE, &response_code);

			if (response_code == 200) {
				printf("Data status normal\n");
			}
			else {
				printf("Data status abnormal\n");
			}
		}

		curl_easy_cleanup(pCURL);
		return data;
	}

	string curl_http_send::HttpPost(const std::string & strUrl, const char * send_data, const long nTimeout)
	{
		data.clear();
		CURLcode res;

		CURL* pCURL = curl_easy_init();
		struct curl_slist* headers = NULL;

		if (!pCURL) {
			error("curl_easy_init error");
			curl_easy_cleanup(pCURL);
			return "";
		}

		this->buf_size = 0;
		CURLcode ret;
		ret = curl_easy_setopt(pCURL, CURLOPT_URL, strUrl.c_str());
		curl_easy_setopt(pCURL, CURLOPT_TIMEOUT, 10);
		ret = curl_easy_setopt(pCURL, CURLOPT_POST, 1L);
		//设置发送的数据为json数据
		headers = curl_slist_append(headers, "content-type:application/json");

		if (headers == NULL) {
			curl_slist_free_all(headers);
			error("curl_slist_append error");
			return "";
		}

		//设置要http头信息
		ret = curl_easy_setopt(pCURL, CURLOPT_HTTPHEADER, headers);
		//设置不忽略SIGPIPE信号
		curl_easy_setopt(pCURL, CURLOPT_NOSIGNAL, 1L);
		//设置开启进度条
		curl_easy_setopt(pCURL, CURLOPT_NOPROGRESS, 0L);
		//设置要发送的数据
		ret = curl_easy_setopt(pCURL, CURLOPT_POSTFIELDS, send_data);
		//设置超时
		ret = curl_easy_setopt(pCURL, CURLOPT_TIMEOUT, nTimeout);
		//设置用于接收数据内容的回调函数
		ret = curl_easy_setopt(pCURL, CURLOPT_WRITEFUNCTION, curl_http_send::receive_data);
		//设置传递给接收函数内容的自定义指针
		ret = curl_easy_setopt(pCURL, CURLOPT_WRITEDATA, this);
		//设置用于进度显示的回调函数
		ret = curl_easy_setopt(pCURL, CURLOPT_PROGRESSFUNCTION, curl_http_send::progress_callback);
		//设置传递给进度显示函数的自定义指针
		ret = curl_easy_setopt(pCURL, CURLOPT_PROGRESSDATA, this);
		//设置用于接收报头数据的回调函数
		ret = curl_easy_setopt(pCURL, CURLOPT_HEADERFUNCTION, curl_http_send::header_callback);
		//设置传递给接收报头函数的自定义指针
		ret = curl_easy_setopt(pCURL, CURLOPT_HEADERDATA, this);
		

		res = curl_easy_perform(pCURL);
		curl_slist_free_all(headers);

		//如果res != CURLE_OK则发送失败
		if (res != CURLE_OK) {
			error("curl_easy_perform error");
			data.clear();
		}
		else {
			//获取接收到的数据Status,如果为200则表示接收发正常,其余则不正常
			long response_code;
			curl_easy_getinfo(pCURL, CURLINFO_RESPONSE_CODE, &response_code);

			char *contentType = { 0 };
			CURLcode return_code;
			return_code = curl_easy_getinfo(pCURL, CURLINFO_CONTENT_TYPE, &contentType);
			if ((CURLE_OK == return_code) && contentType)
				cout << "请求的文件类型:" << contentType << endl;

			if (response_code == 200) {
				printf("Data status normal\n");
			}
			else {
				printf("Data status abnormal\n");
			}
		}
		curl_easy_cleanup(pCURL);
		return data;
	}
}

代码(main.cpp)如下:

#include "curl_http_post.h"
#include <fstream>
#include <windows.h>
#include <pthread.h>

#define URL "http://xxx"					//目标url
#define OVER_TIME 500						//超时时间

//将Unicode的字符串转成UTF-8的字符串
static std::string GBKToUTF8(const char* strGBK)
{
	int len = MultiByteToWideChar(CP_ACP, 0, strGBK, -1, NULL, 0);
	wchar_t* wstr = new wchar_t[len + 1];
	memset(wstr, 0, len + 1);
	MultiByteToWideChar(CP_ACP, 0, strGBK, -1, wstr, len);
	len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
	char* str = new char[len + 1];
	memset(str, 0, len + 1);
	WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
	std::string strTemp = str;

	if (wstr) delete[] wstr;
	if (str) delete[] str;

	return strTemp;
}

//多线程测试
void * thread_run_function(void *ptr) {
	printf("thread_run_function\n");
	INTERNET_SERVICE::curl_http_send *p = new INTERNET_SERVICE::curl_http_send();
	string str2 = GBKToUTF8("{\"standardId\": \"1191537941206355970\",\"attachedInformation\": \"ncdisp\"}");
	string data = p->HttpPost(URL, str2.c_str(), OVER_TIME);
	char filename[20];
	sprintf(filename, "%s%u%s", "data", *(int *)ptr, ".txt");
	//将返回的结果写到对应的线程编号的文件中保存
	FILE *fp = fopen(filename, "w");
	fwrite(data.c_str(), data.size(), 1, fp);
	fclose(fp);
	fp = NULL;
	delete p;
	return NULL;
}

int main() {
	curl_global_init(CURL_GLOBAL_ALL);
	INTERNET_SERVICE::curl_http_send *p = new INTERNET_SERVICE::curl_http_send();
	
	pthread_t thread1, thread2,thread3, thread4;
	unsigned int index1 = 1, index2 = 2, index3 = 3, index4 = 4;
	pthread_create(&thread1, NULL, thread_run_function, &index1);
	pthread_create(&thread2, NULL, thread_run_function, &index2);
	pthread_create(&thread3, NULL, thread_run_function, &index3);
	pthread_create(&thread4, NULL, thread_run_function, &index4);	

	//http_get
	cout << "return data-> " << p->HttpGet("www.baidu.com") << endl;

	//http_post
	string str2 = GBKToUTF8("{\"standardId\": \"1191537941206355970\",\"attachedInformation\": \"ncdisp\"}");
	cout << "return data-> " << p->HttpPost(URL, str1.c_str(), OVER_TIME) << endl;

	void *aaa;
	pthread_join(thread1,&aaa);
	pthread_join(thread2,&aaa);
	pthread_join(thread3,&aaa);
	pthread_join(thread4,&aaa);

	delete p;
	
	curl_global_cleanup();
	system("pause");
	return 0;
}

参考:
1.https://curl.haxx.se/libcurl/c/
2.https://blog.csdn.net/FK2016/article/details/82226683

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值