C++開源庫之Curl

關於Curl庫

curl 是一個利用URL語法在命令行方式下工作的文件傳輸工具。它支持很多協議:FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE 以及 LDAP。curl不但提供了一個可執行的工具庫,還提供了供程序開發的libcurl庫,該庫使用c語言編寫,支持跨平臺,libcurl的下載地址點這裏。下載的安裝包裏面有個Project文件夾,該目錄下全是已經對用戶提供的Visual Studio工程庫,包括Visual Studio的各個版本,提供代碼示例以及庫的編譯,而且還提供多種編譯方式,包括靜態庫,動態庫,調試版、發佈版,所謂真是業界良心的。提供的版本列表如下所示:



編譯安裝

編譯curl庫很簡單的,找到自己對應的VS目錄,然後打開工程,選擇所需要的版本即可編譯。我使用的是VS2010的靜態庫版本,因此選擇VC10目錄->LIB Debug版本,然後編譯,編譯後生成libcurld.lib和對應的調試信息文件libcurld.pdb,這樣我們開發調試的時候只需要把該庫文件和curl庫的頭文件文件夾curl加到我們的工程裏面就可以使用curl庫給我們提供的功能了。不過需要注意的是,因爲CURL的特殊性,需要預定義一些宏和添加特定的依賴庫,如下所示:

// 需要定義的宏,要不然會提示找不到某些函數的定義
#define BUILDING_LIBCURL
//#define HTTP_ONLY

// 依賴庫
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "wldap32.lib")
#pragma comment(lib, "libcurld.lib")

主要函數列表

  • 全局初始化
    CURLcode curl_global_init(long flags);
    描述: 這個函數只能用一次(其實在調用curl_global_cleanup 函數後仍然可再用),如果這個函數在curl_easy_init函數調用時還沒調用,它講由libcurl庫自動完成。   
    參數:flags
    CURL_GLOBAL_ALL         //初始化所有的可能的調用。     
    CURL_GLOBAL_SSL         //初始化支持 安全套接字層。     
    CURL_GLOBAL_WIN32       //初始化win32套接字庫。     
    CURL_GLOBAL_NOTHING     //沒有額外的初始化

  • 全局清理
    void curl_global_cleanup(void);
    描述:在結束libcurl使用的時候,用來對curl_global_init做的工作清理。類似於close的函數。

  • 獲取Curl版本庫
    char *curl_version();
    描述: 打印當前libcurl庫的版本。

  • 初始化curl會話
    CURL *curl_easy_init();
    描述: curl_easy_init用來初始化一個CURL的指針(有些像返回FILE類型的指針一樣),相應的在調用結束時要用curl_easy_cleanup函數清理。一般curl_easy_init意味着一個會話的開始,它的返回值一般都用在easy系列的函數。
  • 清理cur會話
    void curl_easy_cleanup(CURL *handle);
    描述: 這個調用用來結束一個會話,與curl_easy_init配合着用。

    參數: CURL類型的指針。
  • 設置curl會話參數
    CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
    描述: 這個函數最重要了,幾乎所有的curl程序都要頻繁的使用它,用來設置參數。它告訴curl庫,程序將有如何的行爲,比如要查看一個網頁的html代碼等。
    參數handle:CURL類型的指針 
    參數option:各種CURLoption類型的選項。(都在curl.h庫裏有定義,man 也可以查看到)     
    參數parameter: 這個參數既可以是個函數的指針,也可以是某個對象的指針,也可以是個long型的變量,它用什麼這取決於第二個參數。

  • 執行curl會話
    CURLcode curl_easy_perform(CURL *handle);
    描述:這個函數在初始化CURL類型的指針以及curl_easy_setopt完成後調用,就像字面的意思所說perform就像是個舞臺,讓我們設置的option運作起來,執行curl所設置的動作。
    參數: CURL類型的指針

代碼示例(參 C++使用libcurl做HttpClient

一個使用curl封裝Post和Get方法的類

#ifndef __MY_CURL_H__
#define __MY_CURL_H__

#define BUILDING_LIBCURL
//#define HTTP_ONLY

#include <string>

class CHttpClient
{
public:
	CHttpClient(void);
	~CHttpClient(void);

public:
	/**
	* @brief HTTP POST請求
	* @param strUrl 輸入參數,請求的Url地址,如:http://www.baidu.com
	* @param strPost 輸入參數,使用如下格式para1=val1¶2=val2&…
	* @param strResponse 輸出參數,返回的內容
	* @return 返回是否Post成功
	*/
	int Post(const std::string & strUrl, const std::string & strPost, std::string & strResponse);

	/**
	* @brief HTTP GET請求
	* @param strUrl 輸入參數,請求的Url地址,如:http://www.baidu.com
	* @param strResponse 輸出參數,返回的內容
	* @return 返回是否Post成功
	*/
	int Get(const std::string & strUrl, std::string & strResponse);

	/**
	* @brief HTTPS POST請求,無證書版本
	* @param strUrl 輸入參數,請求的Url地址,如:https://www.alipay.com
	* @param strPost 輸入參數,使用如下格式para1=val1¶2=val2&…
	* @param strResponse 輸出參數,返回的內容
	* @param pCaPath 輸入參數,爲CA證書的路徑.如果輸入爲NULL,則不驗證服務器端證書的有效性.
	* @return 返回是否Post成功
	*/
	int Posts(const std::string & strUrl, const std::string & strPost, std::string & strResponse, const char * pCaPath = NULL);

	/**
	* @brief HTTPS GET請求,無證書版本
	* @param strUrl 輸入參數,請求的Url地址,如:https://www.alipay.com
	* @param strResponse 輸出參數,返回的內容
	* @param pCaPath 輸入參數,爲CA證書的路徑.如果輸入爲NULL,則不驗證服務器端證書的有效性.
	* @return 返回是否Post成功
	*/
	int Gets(const std::string & strUrl, std::string & strResponse, const char * pCaPath = NULL);

public:
	void SetDebug(bool bDebug);

private:
	bool m_bDebug;
};

#endif

#include "my_curl.h"
#include "curl/curl.h"
#include <string>


#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "wldap32.lib")
#pragma comment(lib, "libcurld.lib")

CHttpClient::CHttpClient(void) : 
m_bDebug(false)
{

}

CHttpClient::~CHttpClient(void)
{

}

static int OnDebug(CURL *, curl_infotype itype, char * pData, size_t size, void *)
{
	if(itype == CURLINFO_TEXT)
	{
		//printf("[TEXT]%s\n", pData);
	}
	else if(itype == CURLINFO_HEADER_IN)
	{
		printf("[HEADER_IN]%s\n", pData);
	}
	else if(itype == CURLINFO_HEADER_OUT)
	{
		printf("[HEADER_OUT]%s\n", pData);
	}
	else if(itype == CURLINFO_DATA_IN)
	{
		printf("[DATA_IN]%s\n", pData);
	}
	else if(itype == CURLINFO_DATA_OUT)
	{
		printf("[DATA_OUT]%s\n", pData);
	}
	return 0;
}

static size_t OnWriteData(void* buffer, size_t size, size_t nmemb, void* lpVoid)
{
	std::string* str = dynamic_cast<std::string*>((std::string *)lpVoid);
	if( NULL == str || NULL == buffer )
	{
		return -1;
	}

    char* pData = (char*)buffer;
    str->append(pData, size * nmemb);
	return nmemb;
}

int CHttpClient::Post(const std::string & strUrl, const std::string & strPost, std::string & strResponse)
{
	CURLcode res;
	CURL* curl = curl_easy_init();
	if(NULL == curl)
	{
		return CURLE_FAILED_INIT;
	}
	if(m_bDebug)
	{
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
		curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, OnDebug);
	}
	curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());
	curl_easy_setopt(curl, CURLOPT_POST, 1);
	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, strPost.c_str());
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
	res = curl_easy_perform(curl);
	curl_easy_cleanup(curl);
	return res;
}

int CHttpClient::Get(const std::string & strUrl, std::string & strResponse)
{
	CURLcode res;
	CURL* curl = curl_easy_init();
	if(NULL == curl)
	{
		return CURLE_FAILED_INIT;
	}
	if(m_bDebug)
	{
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
		curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, OnDebug);
	}
	
	curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);
	/**
	* 當多個線程都使用超時處理的時候,同時主線程中有sleep或是wait等操作。
	* 如果不設置這個選項,libcurl將會發信號打斷這個wait從而導致程序退出。
	*/
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
	res = curl_easy_perform(curl);
	curl_easy_cleanup(curl);
	return res;
}

int CHttpClient::Posts(const std::string & strUrl, const std::string & strPost, std::string & strResponse, const char * pCaPath)
{
	CURLcode res;
	CURL* curl = curl_easy_init();
	if(NULL == curl)
	{
		return CURLE_FAILED_INIT;
	}
	if(m_bDebug)
	{
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
		curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, OnDebug);
	}
	curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());
	curl_easy_setopt(curl, CURLOPT_POST, 1);
	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, strPost.c_str());
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	if(NULL == pCaPath)
	{
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
	}
	else
	{
		//缺省情況就是PEM,所以無需設置,另外支持DER
		//curl_easy_setopt(curl,CURLOPT_SSLCERTTYPE,"PEM");
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
		curl_easy_setopt(curl, CURLOPT_CAINFO, pCaPath);
	}
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
	res = curl_easy_perform(curl);
	curl_easy_cleanup(curl);
	return res;
}

int CHttpClient::Gets(const std::string & strUrl, std::string & strResponse, const char * pCaPath)
{
	CURLcode res;
	CURL* curl = curl_easy_init();
	if(NULL == curl)
	{
		return CURLE_FAILED_INIT;
	}
	if(m_bDebug)
	{
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
		curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, OnDebug);
	}
	curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	if(NULL == pCaPath)
	{
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
	}
	else
	{
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
		curl_easy_setopt(curl, CURLOPT_CAINFO, pCaPath);
	}
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
	res = curl_easy_perform(curl);
	curl_easy_cleanup(curl);
	return res;
}

///////////////////////////////////////////////////////////////////////////////////////////////

void CHttpClient::SetDebug(bool bDebug)
{
	m_bDebug = bDebug;
}
從上述代碼可以看出,使用curl庫主要分爲四個步驟:init,setopt,perform和cleanup,其中setopt最重要,用來指定參數
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章