初入職常見問題: 封裝curl進行網絡通信 (windows可以直接使用,linux下大同小異可參考部分代碼)

最近嵌軟同事想接入第三方廠商的設備,在寫客戶端和服務端,用curl通信消息一直髮不過去,或者遇到頭和消息體如何分開獲取等問題。我就給他這個代碼了。基於這個他總算完成了基本的通信功能

curl本身的代碼庫自行去獲取吧都差不多的不用糾結,下面是封裝curl的一個頭文件和一個源文件,把他們加到你的工程就可以使用了

 

//HttpCurl.h

#pragma once
#include <string>
#include <map>
#include <list>
#include "curl/curl.h"
#include <algorithm>
#include <xfunctional>

class CHttpCurl
{
public:
    typedef std::map<std::string, std::string> headermap;

    /** response struct for queries */
    typedef struct
    {
        int            ncode;
        std::string strBody;
        headermap    mapHeaders;
    } response;

    /** struct used for uploading data */
    typedef struct
    {
        const char* pData;
        size_t        length;
    } upload_object;

    /** struct used for multipart/form-data upload*/
    typedef struct
    {
        std::string strCopyName;
        std::string strFileName;
        std::string strData;
    } FileUpLoad_obj;

private:
    CHttpCurl();
public:
    ~CHttpCurl();

    static CHttpCurl*        GetInstance();

    // Auth
    void            setAuth(const std::string& user, const std::string& password);
    void            clearAuth();

    // HTTP GET
    response        get(const std::string& url, const bool bCheckCertificate = false);
    // HTTP GET
    response        get(const std::string& url, const std::string& ctypeHeader);
    // HTTP GET
    response        get(const std::string& url, const std::list<std::string> & ctypeList);

    // HTTP POST
    response        post(const std::string& url, const std::string& ctype,
        const std::string& data);
    // HTTP POST
    response        post(const std::string& url, const std::list<std::string> & ctypeList,
        const std::string& data, const bool bCheckCertificate = true);
    // HTTP POST    FOR FILE UPLOAD
    response        post(const std::string& url, 
        const std::list<std::string> & ctypeList,
        const std::list<FileUpLoad_obj> & fileInfo,
        const std::list<FileUpLoad_obj> & CopyContent);

    // HTTP PUT
    response        put(const std::string& url, const std::list<std::string> & ctypeList,
        const std::string& data);
    // HTTP DELETE
    response        curlDelete(const std::string& url, const std::list<std::string> & ctypeList);

private:
    const char    *            m_pUser_Agent;
    std::string                m_strUser_Pass;

    void    Set_CurlPublicOpt(CURL *pCurl, const std::string& strUrl, CHttpCurl::response & ret);

    // writedata callback function
    static size_t write_callback(void *ptr, size_t size, size_t nmemb,
        void *userdata);

    // header callback function
    static size_t header_callback(void *ptr, size_t size, size_t nmemb,
        void *userdata);

    // read callback function
    static size_t read_callback(void *ptr, size_t size, size_t nmemb,
        void *userdata);

    // trim from start
    static inline std::string &ltrim(std::string &s) {
        s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(::isspace))));
        return s;
    }

    // trim from end
    static inline std::string &rtrim(std::string &s) {
        s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(::isspace))).base(), s.end());
        return s;
    }

    // trim from both ends
    static inline std::string &trim(std::string &s) {
        return ltrim(rtrim(s));
    }
};









 

//HttpCurl.cpp

#include "HttpCurl.h"
#include <functional>
#pragma comment(lib, "curl/libcurl.lib")


CHttpCurl::CHttpCurl()
:m_pUser_Agent("")
{
	curl_global_init(CURL_GLOBAL_ALL);
}

CHttpCurl::~CHttpCurl()
{
	curl_global_cleanup();
}

CHttpCurl* CHttpCurl::GetInstance()
{
	static CHttpCurl s_Curl;

	return &s_Curl;
}

void CHttpCurl::setAuth(const std::string& user, const std::string& password)
{
	m_strUser_Pass.clear();
	m_strUser_Pass += user + ":" + password;
}

void CHttpCurl::clearAuth()
{
	m_strUser_Pass.clear();
}


/**
* @brief HTTP GET method
*
* @param url to query
*
* @return response struct
*/
CHttpCurl::response CHttpCurl::get(const std::string& url, const bool bCheckCertificate)
{
	/** create return struct */
	CHttpCurl::response ret = {};

	// use libcurl
	CURL *curl = nullptr;
	CURLcode res = CURLE_OK;

	curl = curl_easy_init();
	if (curl)
	{
		Set_CurlPublicOpt(curl, url, ret);

		if (!bCheckCertificate)
		{
			curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);		//設定爲不驗證證書
		}

		/** perform the actual query */
		res = curl_easy_perform(curl);
		if (res != CURLE_OK)
		{
			ret.strBody = "Failed to query.";
			ret.ncode = -1;
			return ret;
		}
		long http_code = 0;
		curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
		ret.ncode = static_cast<int>(http_code);

		curl_easy_cleanup(curl);
	}

	return ret;
}

CHttpCurl::response CHttpCurl::get(const std::string& url, const std::string& ctypeHeader)
{
	/** create return struct */
	CHttpCurl::response ret = {};

	// use libcurl
	CURL *curl = nullptr;
	CURLcode res = CURLE_OK;

	curl = curl_easy_init();
	if (curl)
	{
		Set_CurlPublicOpt(curl, url, ret);

		//method設爲GET
		curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
		/** set content-type header */
		curl_slist* header = NULL;
		header = curl_slist_append(header, ctypeHeader.c_str());
		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);

		/** perform the actual query */
		res = curl_easy_perform(curl);
		if (res != CURLE_OK)
		{
			ret.strBody = "Failed to query.";
			ret.ncode = -1;
			return ret;
		}
		long http_code = 0;
		curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
		ret.ncode = static_cast<int>(http_code);

		curl_easy_cleanup(curl);
	}

	return ret;
}

CHttpCurl::response CHttpCurl::get(const std::string& url, const std::list<std::string> & ctypeList)
{
	/** create return struct */
	CHttpCurl::response ret = {};

	// use libcurl
	CURL *curl = nullptr;
	CURLcode res = CURLE_OK;

	curl = curl_easy_init();
	if (curl)
	{
		Set_CurlPublicOpt(curl, url, ret);

		//method設爲GET
		curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
		/** set content-type header */
		curl_slist* header = nullptr;
		for (auto & item : ctypeList)
		{
			header = curl_slist_append(header, item.c_str());
		}
		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);

		/** perform the actual query */
		res = curl_easy_perform(curl);
		if (res != CURLE_OK)
		{
			ret.strBody = "Failed to query.";
			ret.ncode = -1;
			return ret;
		}
		long http_code = 0;
		curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
		ret.ncode = static_cast<int>(http_code);

		curl_easy_cleanup(curl);
	}

	return ret;
}

/**
* @brief HTTP POST method
*
* @param url to query
* @param ctype content type as string
* @param data HTTP POST body
*
* @return response struct
*/
CHttpCurl::response CHttpCurl::post(const std::string& url,
	const std::string& ctype,
	const std::string& data)
{
	/** create return struct */
	CHttpCurl::response ret = {};

	/** build content-type header string */
	std::string ctype_header = "Content-Type: " + ctype + ";";//"; charset=UTF-8";

	// use libcurl
	CURL *curl = nullptr;
	CURLcode res = CURLE_OK;

	curl = curl_easy_init();
	if (curl)
	{
		Set_CurlPublicOpt(curl, url, ret);

		/** Now specify we want to POST data */
		curl_easy_setopt(curl, CURLOPT_POST, 1L);
		/** set post fields */
		curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
		curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, data.size());

		/** set content-type header */
		curl_slist* header = nullptr;
		header = curl_slist_append(header, ctype_header.c_str());
		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);
		/** perform the actual query */
		res = curl_easy_perform(curl);
		if (res != CURLE_OK)
		{
			ret.strBody = "Failed to query.";
			ret.ncode = -1;
			return ret;
		}
		long http_code = 0;
		curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
		ret.ncode = static_cast<int>(http_code);

		curl_slist_free_all(header);
		curl_easy_cleanup(curl);
	}

	return ret;
}

CHttpCurl::response CHttpCurl::post(const std::string& url,
	const std::list<std::string> & ctypeList,
	const std::string& data,
	const bool bCheckCertificate)
{
	/** create return struct */
	CHttpCurl::response ret = {};

	// use libcurl
	CURL *curl = nullptr;
	CURLcode res = CURLE_OK;

	curl = curl_easy_init();
	if (curl)
	{
		Set_CurlPublicOpt(curl, url, ret);

		/** Now specify we want to POST data */
		curl_easy_setopt(curl, CURLOPT_POST, 1L);
		/** set post fields */
		curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
		curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, data.size());

		/** set content-type header */
		curl_slist* header = nullptr;
		for (auto & item : ctypeList)
		{
			header = curl_slist_append(header, item.c_str());
		}
		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);

		if (!bCheckCertificate)
		{
			curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);		//設定爲不驗證證書
		}

		/** perform the actual query */
		res = curl_easy_perform(curl);
		if (res != CURLE_OK)
		{
			ret.strBody = "Failed to query.";
			ret.ncode = -1;
			return ret;
		}
		long http_code = 0;
		curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
		ret.ncode = static_cast<int>(http_code);

		curl_slist_free_all(header);
		curl_easy_cleanup(curl);
	}

	return ret;
}

CHttpCurl::response	CHttpCurl::post(const std::string& url,
	const std::list<std::string> & ctypeList,
	const std::list<FileUpLoad_obj> & fileInfo,
	const std::list<FileUpLoad_obj> & CopyContent)
{
	CHttpCurl::response ret = {};

	// use libcurl
	CURL *curl = nullptr;
	CURLcode res = CURLE_OK;

	curl = curl_easy_init();
	if (curl)
	{
		Set_CurlPublicOpt(curl, url, ret);

		/** Now specify we want to POST data */
		curl_easy_setopt(curl, CURLOPT_POST, 1L);

		/** set content-type header */
		curl_slist* header = nullptr;
		for (auto & item : ctypeList)
		{
			header = curl_slist_append(header, item.c_str());
		}
		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);

		curl_httppost* pFormPost = NULL;
		curl_httppost* pLastElem = NULL;
		for (auto & item : fileInfo)
		{
			curl_formadd(&pFormPost, &pLastElem,
				CURLFORM_COPYNAME, item.strCopyName.c_str(),
				CURLFORM_BUFFER, item.strFileName.c_str(),
				CURLFORM_BUFFERPTR, item.strData.c_str(),
				CURLFORM_BUFFERLENGTH, item.strData.length(),
				CURLFORM_END);
		}

		for (auto & item : CopyContent)
		{
			curl_formadd(&pFormPost, &pLastElem,
				CURLFORM_COPYNAME, item.strCopyName.c_str(),
				CURLFORM_COPYCONTENTS, item.strData.c_str(),
				CURLFORM_END);
		}

		curl_easy_setopt(curl, CURLOPT_HTTPPOST, pFormPost);

		/** perform the actual query */
		res = curl_easy_perform(curl);
		curl_formfree(pFormPost);
		if (res != CURLE_OK)
		{
			ret.strBody = "Failed to query.";
			ret.ncode = -1;
			return ret;
		}
		long http_code = 0;
		curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
		ret.ncode = static_cast<int>(http_code);

		curl_slist_free_all(header);
		curl_easy_cleanup(curl);
	}

	return ret;
}

CHttpCurl::response CHttpCurl::put(const std::string& url, const std::list<std::string> & ctypeList,
	const std::string& data)
{
	/** create return struct */
	CHttpCurl::response ret = {};

	// use libcurl
	CURL *curl = nullptr;
	CURLcode res = CURLE_OK;

	curl = curl_easy_init();
	if (curl)
	{
		Set_CurlPublicOpt(curl, url, ret);

		//curl_easy_setopt(curl, CURLOPT_PUT, 1L); //沒用
		curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
		
		/** set post fields */
		curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
		curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, data.size());

		/** set content-type header */
		curl_slist* header = nullptr;
		for (auto & item : ctypeList)
		{
			header = curl_slist_append(header, item.c_str());
		}
		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);

		/** perform the actual query */
		res = curl_easy_perform(curl);
		if (res != CURLE_OK)
		{
			ret.strBody = "Failed to query.";
			ret.ncode = -1;
			return ret;
		}
		long http_code = 0;
		curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
		ret.ncode = static_cast<int>(http_code);

		curl_slist_free_all(header);
		curl_easy_cleanup(curl);
	}

	return ret;
}


CHttpCurl::response CHttpCurl::curlDelete(const std::string& url, const std::list<std::string> & ctypeList)
{
	/** create return struct */
	CHttpCurl::response ret = {};

	// use libcurl
	CURL *curl = nullptr;
	CURLcode res = CURLE_OK;

	curl = curl_easy_init();
	if (curl)
	{
		Set_CurlPublicOpt(curl, url, ret);

		curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");


		/** set content-type header */
		curl_slist* header = nullptr;
		std::string strTemp;
		for (auto & item : ctypeList)
		{
			header = curl_slist_append(header, item.c_str());
		}
		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);

		/** perform the actual query */
		res = curl_easy_perform(curl);
		if (res != CURLE_OK)
		{
			ret.strBody = "Failed to query.";
			ret.ncode = -1;
			return ret;
		}
		long http_code = 0;
		curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
		ret.ncode = static_cast<int>(http_code);

		curl_slist_free_all(header);
		curl_easy_cleanup(curl);
	}

	return ret;
}

void CHttpCurl::Set_CurlPublicOpt(CURL *pCurl, const std::string& strUrl, CHttpCurl::response & ret)
{
	if (pCurl)
	{
		/** set basic authentication if present*/
		if (m_strUser_Pass.length() > 0){
			curl_easy_setopt(pCurl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
			curl_easy_setopt(pCurl, CURLOPT_USERPWD, m_strUser_Pass.c_str());
		}
		/** set user agent */
		curl_easy_setopt(pCurl, CURLOPT_USERAGENT, m_pUser_Agent);
		/** set query URL */
		curl_easy_setopt(pCurl, CURLOPT_URL, strUrl.c_str());
		/** set callback function */
		curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION,CHttpCurl::write_callback);
		/** set data object to pass to callback function */
		curl_easy_setopt(pCurl, CURLOPT_WRITEDATA, &ret);
		/** set the header callback function */
		curl_easy_setopt(pCurl, CURLOPT_HEADERFUNCTION,CHttpCurl::header_callback);
		/** callback object for headers */
		curl_easy_setopt(pCurl, CURLOPT_HEADERDATA, &ret);
		//連接超時時間
		curl_easy_setopt(pCurl, CURLOPT_TIMEOUT, 20);
		curl_easy_setopt(pCurl, CURLOPT_CONNECTTIMEOUT, 6);
	}

}

size_t CHttpCurl::write_callback(void *data, size_t size, size_t nmemb,
	void *userdata)
{
	CHttpCurl::response* r;
	r = reinterpret_cast<CHttpCurl::response*>(userdata);
	r->strBody.append(reinterpret_cast<char*>(data), size*nmemb);

	return (size * nmemb);
}

/**
* @brief header callback for libcurl
*
* @param data returned (header line)
* @param size of data
* @param nmemb memblock
* @param userdata pointer to user data object to save headr data
* @return size * nmemb;
*/
size_t CHttpCurl::header_callback(void *data, size_t size, size_t nmemb,
	void *userdata)
{
	CHttpCurl::response* r;
	r = reinterpret_cast<CHttpCurl::response*>(userdata);
	std::string header(reinterpret_cast<char*>(data), size*nmemb);
	size_t seperator = header.find_first_of(":");
	if (std::string::npos == seperator) {
		//roll with non seperated headers...
		trim(header);
		if (0 == header.length()){
			return (size * nmemb); //blank line;
		}
		r->mapHeaders[header] = "present";
	}
	else {
		std::string key = header.substr(0, seperator);
		trim(key);
		std::string value = header.substr(seperator + 1);
		trim(value);
		r->mapHeaders[key] = value;
	}

	return (size * nmemb);
}

/**
* @brief read callback function for libcurl
*
* @param pointer of max size (size*nmemb) to write data to
* @param size size parameter
* @param nmemb memblock parameter
* @param userdata pointer to user data to read data from
*
* @return (size * nmemb)
*/
size_t CHttpCurl::read_callback(void *data, size_t size, size_t nmemb,
	void *userdata)
{
	/** get upload struct */
	CHttpCurl::upload_object* u;
	u = reinterpret_cast<CHttpCurl::upload_object*>(userdata);
	/** set correct sizes */
	size_t curl_size = size * nmemb;
	size_t copy_size = (u->length < curl_size) ? u->length : curl_size;
	/** copy data to buffer */
	memcpy(data, u->pData, copy_size);
	/** decrement length and increment data pointer */
	u->length -= copy_size;
	u->pData += copy_size;
	/** return copied size */
	return copy_size;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章