關於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")
主要函數列表
- 全局初始化
描述: 這個函數只能用一次(其實在調用curl_global_cleanup 函數後仍然可再用),如果這個函數在curl_easy_init函數調用時還沒調用,它講由libcurl庫自動完成。CURLcode curl_global_init(long flags);
參數:flags
CURL_GLOBAL_ALL //初始化所有的可能的調用。
CURL_GLOBAL_SSL //初始化支持 安全套接字層。
CURL_GLOBAL_WIN32 //初始化win32套接字庫。
CURL_GLOBAL_NOTHING //沒有額外的初始化 - 全局清理
描述:在結束libcurl使用的時候,用來對curl_global_init做的工作清理。類似於close的函數。void curl_global_cleanup(void);
- 獲取Curl版本庫
描述: 打印當前libcurl庫的版本。char *curl_version();
- 初始化curl會話
描述: curl_easy_init用來初始化一個CURL的指針(有些像返回FILE類型的指針一樣),相應的在調用結束時要用curl_easy_cleanup函數清理。一般curl_easy_init意味着一個會話的開始,它的返回值一般都用在easy系列的函數。CURL *curl_easy_init();
- 清理cur會話
描述: 這個調用用來結束一個會話,與curl_easy_init配合着用。void curl_easy_cleanup(CURL *handle);
參數: CURL類型的指針。 - 設置curl會話參數
描述: 這個函數最重要了,幾乎所有的curl程序都要頻繁的使用它,用來設置參數。它告訴curl庫,程序將有如何的行爲,比如要查看一個網頁的html代碼等。CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
參數handle:CURL類型的指針
參數option:各種CURLoption類型的選項。(都在curl.h庫裏有定義,man 也可以查看到)
參數parameter: 這個參數既可以是個函數的指針,也可以是某個對象的指針,也可以是個long型的變量,它用什麼這取決於第二個參數。 - 執行curl會話
描述:這個函數在初始化CURL類型的指針以及curl_easy_setopt完成後調用,就像字面的意思所說perform就像是個舞臺,讓我們設置的option運作起來,執行curl所設置的動作。CURLcode curl_easy_perform(CURL *handle);
參數: 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最重要,用來指定參數