C++ LibCurl 庫的使用方法
LibCurl是一個開源的免費的多協(xié)議數(shù)據(jù)傳輸開源庫,該框架具備跨平臺性,開源免費,并提供了包括HTTP
、FTP
、SMTP
、POP3
等協(xié)議的功能,使用libcurl
可以方便地進行網(wǎng)絡數(shù)據(jù)傳輸操作,如發(fā)送HTTP
請求、下載文件、發(fā)送電子郵件等。它被廣泛應用于各種網(wǎng)絡應用開發(fā)中,特別是涉及到數(shù)據(jù)傳輸?shù)膱鼍啊?/p>
??下載地址:https://curl.haxx.se/download.html
首先讀者需要自行下載該庫,如下筆者選擇下載curl-8.0.1.zip
這個源代碼版本,讀者可找到如下頁面,并點擊對應版本完成下載,當下載好以后讀者可自行將其解壓縮到任意目錄下。

當讀者解壓縮后,可打開VS2013 開發(fā)人員命令提示
并切換帶該目錄中的curl-8.0.1\winbuild
目錄,通過執(zhí)行如下兩條命令即可分別實現(xiàn)編譯靜態(tài)庫或動態(tài)庫,我們以靜態(tài)庫編譯為主,執(zhí)行如下命令讀者可自行等待一段時間。
??動態(tài)庫: nmake /f Makefile.vc mode=dll VC=13 MACHINE=x86 DEBUG=no
??靜態(tài)庫: nmake / f Makefile.vc mode = static VC = 13 ENABLE_IDN = no MACHINE = x86 DEBUG = no
這個庫在編譯通過后會自動生成文件到builds\libcurl-vc13-x86-release-static-ipv6-sspi-schannel
目錄內(nèi),讀者可自行打開該目錄,即可看到該目錄內(nèi)的頭文件以及庫目錄文件,如下圖所示;

讀者可自行配置這個靜態(tài)庫,通常只需要配置include
和lib
文件即可,該庫的使用很簡單,首先我們需要調(diào)用curl_easy_init()
函數(shù)對CURL
對象進行初始化,接著通過調(diào)用curl_easy_setopt()
并傳入一個訪問URL
鏈接,當訪問成功后則可調(diào)用curl_easy_perform()
函數(shù)得到訪問結果,這就是該庫基本使用方法,如下代碼。
using?namespace?std;
int?main(int?argc,?char?*argv[])
{
????CURL?*curl;
????CURLcode?res;
????curl?=?curl_easy_init();
????if?(curl)
????{
????????curl_easy_setopt(curl,?CURLOPT_URL,?"https://www.lyshark.com");
????????res?=?curl_easy_perform(curl);
????????curl_easy_cleanup(curl);
????}
????std::cout?<<?"返回狀態(tài):?"?<<?res?<<?std::endl;
????system("pause");
????return?0;
}
運行上述代碼,讀者可看到網(wǎng)站www.lyshark.com
的源代碼,如下圖所示;

上述代碼中的curl_easy_setopt()
函數(shù)第二個參數(shù)可以使用多種類型的變量定義,我們可以通過傳入不同的常量來定義請求頭中的參數(shù),例如當我們需要修改協(xié)議頭時,可以使用CURLOPT_HTTPHEADER
常量,并在其后第三個參數(shù)中傳入該常量所對應的結構即可,這個結構體定義有許多類型,具體如下下表所示;
常量名稱描述CURLINFO_EFFECTIVE_URL最后一個有效的URL地址CURLINFO_HTTP_CODE最后一個收到的HTTP代碼CURLINFO_FILETIME遠程獲取文檔的時間,如果無法獲取,則返回值為-1CURLINFO_TOTAL_TIME最后一次傳輸所消耗的時間CURLINFO_NAMELOOKUP_TIME名稱解析所消耗的時間CURLINFO_CONNECT_TIME建立連接所消耗的時間CURLINFO_PRETRANSFER_TIME從建立連接到準備傳輸所使用的時間CURLINFO_STARTTRANSFER_TIME從建立連接到傳輸開始所使用的時間CURLINFO_REDIRECT_TIME在事務傳輸開始前重定向所使用的時間CURLINFO_SIZE_UPLOAD以字節(jié)為單位返回上傳數(shù)據(jù)量的總值CURLINFO_SIZE_DOWNLOAD以字節(jié)為單位返回下載數(shù)據(jù)量的總值CURLINFO_SPEED_DOWNLOAD平均下載速度CURLINFO_SPEED_UPLOAD平均上傳速度CURLINFO_HEADER_SIZEheader部分的大小CURLINFO_HEADER_OUT發(fā)送請求的字符串CURLINFO_REQUEST_SIZE在HTTP請求中有問題的請求的大小CURLINFO_SSL_VERIFYRESULT通過設置CURLOPT_SSL_VERIFYPEER返回的SSL證書驗證請求的結果CURLINFO_CONTENT_LENGTH_DOWNLOAD從Content-Length: field中讀取的下載內(nèi)容長度CURLINFO_CONTENT_LENGTH_UPLOAD上傳內(nèi)容大小的說明CURLINFO_CONTENT_TYPE下載內(nèi)容的Content-Type:值,NULL表示服務器沒有發(fā)送有效的Content-Type:header
如下案例是一個簡單的GET
請求封裝,通過調(diào)用GetStatus()
函數(shù)實現(xiàn)對特定頁面發(fā)起請求的功能,其中curl_slist_append()
用于增加新的請求頭數(shù)據(jù),在調(diào)用curl_easy_setopt()
函數(shù)時,分別傳入了CURLOPT_HTTPHEADER
設置請求頭,CURLOPT_WRITEFUNCTION
設置回調(diào),CURLINFO_PRIMARY_IP
獲取目標IP
地址,CURLINFO_RESPONSE_CODE
獲取目標返回代碼,此處的write_data()
函數(shù)直接返回0則表示屏蔽所有的頁面輸出內(nèi)容。
using?namespace?std;
//?設置CURLOPT_WRITEFUNCTION回調(diào)函數(shù),返回為空屏蔽輸出
static?size_t?write_data(char?*d,?size_t?n,?size_t?l,?void?*p)
{
????return?0;
}
//?獲取網(wǎng)站返回值
void?GetStatus(char?*UrlPage)
{
????CURLcode?return_code;
????//?初始化模塊
????return_code?=?curl_global_init(CURL_GLOBAL_WIN32);
????if?(CURLE_OK?!=?return_code)
????{
????????return;
????}
????//?初始化填充請求頭
????struct?curl_slist?*headers?=?NULL;
????headers?=?curl_slist_append(headers,?"User-Agent:?Mozilla/5.0?(Windows?NT?10.0;?Win64;?x64;?rv:76.0)");
????headers?=?curl_slist_append(headers,?"Referer:?https://www.lyshark.com");
????//?初始化請求庫
????CURL?*easy_handle?=?curl_easy_init();
????if?(NULL?!=?easy_handle)
????{
????????//?CURLOPT_HTTPHEADER?自定義設置請求頭
????????curl_easy_setopt(easy_handle,?CURLOPT_HTTPHEADER,?headers);
????????//?CURLOPT_URL?自定義請求的網(wǎng)站
????????curl_easy_setopt(easy_handle,?CURLOPT_URL,?UrlPage);
????????//?CURLOPT_WRITEFUNCTION?設置回調(diào)函數(shù),屏蔽輸出
????????curl_easy_setopt(easy_handle,?CURLOPT_WRITEFUNCTION,?write_data);
????????//?執(zhí)行CURL訪問網(wǎng)站
????????return_code?=?curl_easy_perform(easy_handle);
????????char?*ipAddress?=?{?0?};
????????//?CURLINFO_PRIMARY_IP?獲取目標IP信息
????????return_code?=?curl_easy_getinfo(easy_handle,?CURLINFO_PRIMARY_IP,?&ipAddress);
????????if?((CURLE_OK?==?return_code)?&&?ipAddress)
????????{
????????????std::cout?<<?"目標IP:?"?<<?ipAddress?<<?std::endl;
????????}
????????long?retcode?=?0;
????????//?CURLINFO_RESPONSE_CODE?獲取目標返回狀態(tài)
????????return_code?=?curl_easy_getinfo(easy_handle,?CURLINFO_RESPONSE_CODE,?&retcode);
????????if?((CURLE_OK?==?return_code)?&&?retcode)
????????{
????????????std::cout?<<?"返回狀態(tài)碼:?"?<<?retcode?<<?std::endl;
????????}
????}
????curl_easy_cleanup(easy_handle);
????curl_global_cleanup();
}
int?main(int?argc,?char?*argv[])
{
????GetStatus("https://www.lyshark.com");
????system("pause");
????return?0;
}
運行上述代碼,則可以獲取到www.lyshark.com
目標主機的IP地址以及頁面返回狀態(tài),如下圖所示;

當然該庫同樣支持POST
請求方式,在使用POST
請求時我們可以通過CURLOPT_COOKIEFILE
參數(shù)指定Cookie
參數(shù),通過CURLOPT_POSTFIELDS
指定POST
的數(shù)據(jù)集,而如果需要使用代理模式則可以通過CURLOPT_PROXY
方式來指定代理地址,
using?namespace?std;
bool?SendPost(char?*Url,?char?*Cookie,?char?*PostVal)
{
????CURL?*curl;
????CURLcode?res;
????//?初始化庫
????curl?=?curl_easy_init();
????if?(curl)
????{
????????//?設置請求頭
????????struct?curl_slist?*headers?=?NULL;
????????headers?=?curl_slist_append(headers,?"User-Agent:?Mozilla/5.0?(Windows?NT?10.0;?Win64;?x64;?rv:76.0)");
????????headers?=?curl_slist_append(headers,?"Referer:?https://www.lyshark.com");
????????//?設置請求頭
????????curl_easy_setopt(curl,?CURLOPT_HTTPHEADER,?headers);
????????//?指定URL
????????curl_easy_setopt(curl,?CURLOPT_URL,?Url);
????????//?指定cookie參數(shù)
????????curl_easy_setopt(curl,?CURLOPT_COOKIEFILE,?Cookie);
????????//?指定post內(nèi)容
????????curl_easy_setopt(curl,?CURLOPT_POSTFIELDS,?PostVal);
????????//?是否代理
????????//?curl_easy_setopt(curl,?CURLOPT_PROXY,?"10.99.60.201:8080");
????????res?=?curl_easy_perform(curl);
????????curl_easy_cleanup(curl);
????}
????return?true;
}
int?main(int?argc,?char?*argv[])
{
????//?傳入網(wǎng)址?cookie?以及post參數(shù)
????SendPost("https://www.lyshark.com/post.php",?"1e12sde342r2",?"&logintype=uid&u=xieyan&psw=xxx86");
????system("pause");
????return?0;
}
該函數(shù)的調(diào)用需要有一個POST結構才可測試,此處由于我并沒有指定接口所有返回了頁面錯誤信息,如下圖所示;

接著繼續(xù)實現(xiàn)下載頁面到本地的功能,該功能實現(xiàn)的原理是利用write_data
回調(diào)函數(shù),當頁面數(shù)據(jù)被讀入到內(nèi)存時回調(diào)函數(shù)會被觸發(fā),在該回調(diào)函數(shù)的內(nèi)部通過調(diào)用fwrite
函數(shù)將ptr
指針中的數(shù)據(jù)保存本地,實現(xiàn)這段代碼如下所示;
using?namespace?std;
FILE?*fp;
size_t?write_data(void?*ptr,?size_t?size,?size_t?nmemb,?void?*stream)
{
????int?written?=?fwrite(ptr,?size,?nmemb,?(FILE?*)fp);
????return?written;
}
BOOL?GetUrl(char?*URL,?char?*FileName)
{
????CURL?*curl;
????curl_global_init(CURL_GLOBAL_ALL);
????curl?=?curl_easy_init();
????curl_easy_setopt(curl,?CURLOPT_URL,?URL);
????//?在屏幕打印請求連接過程和返回http數(shù)據(jù)
????curl_easy_setopt(curl,?CURLOPT_VERBOSE,?1L);
????//?查找次數(shù),防止查找太深
????curl_easy_setopt(curl,?CURLOPT_MAXREDIRS,?1);
????//?設置連接超時
????curl_easy_setopt(curl,?CURLOPT_CONNECTTIMEOUT,?3);
????//?接收數(shù)據(jù)時超時設置
????curl_easy_setopt(curl,?CURLOPT_TIMEOUT,?3);
????if?((fp?=?fopen(FileName,?"w"))?==?NULL)
????{
????????curl_easy_cleanup(curl);
????????return?FALSE;
????}
????//?CURLOPT_WRITEFUNCTION?將后繼的動作交給write_data函數(shù)處理
????curl_easy_setopt(curl,?CURLOPT_WRITEFUNCTION,?write_data);
????curl_easy_perform(curl);
????curl_easy_cleanup(curl);
????return?TRUE;
}
int?main(int?argc,?char?*argv[])
{
????//?下載網(wǎng)頁到本地
????GetUrl("https://www.lyshark.com",?"./lyshark.html");
????system("pause");
????return?0;
}
當讀者運行上述程序后,即可將www.lyshark.com
網(wǎng)站頁面源碼,下載到本地當前目錄下lyshark.html
,輸出效果如下圖所示;

為了能解析參數(shù),我們還是需要將頁面源代碼讀入到內(nèi)存中,要實現(xiàn)這個需求并不難,首先我們定義一個std::string
容器,然后當有新數(shù)據(jù)產(chǎn)生時觸發(fā)WriteCallback
在該函數(shù)內(nèi),我們直接將數(shù)據(jù)拷貝到一個內(nèi)存指針中,也就是存儲到read_buffer
內(nèi),并將該緩沖區(qū)返回給調(diào)用者即可,如下則是完整源代碼。
using?namespace?std;
//?存儲回調(diào)函數(shù)
size_t?WriteCallback(char?*contents,?size_t?size,?size_t?nmemb,?void?*userp)
{
????((std::string*)userp)->append((char*)contents,?size?*?nmemb);
????return?size?*?nmemb;
}
//?獲取數(shù)據(jù)并放入string中.
std::string?GetUrlPageOfString(std::string?url)
{
????std::string?read_buffer;
????CURL?*curl;
????curl_global_init(CURL_GLOBAL_ALL);
????curl?=?curl_easy_init();
????if?(curl)
????{
????????//?忽略證書檢查
????????curl_easy_setopt(curl,?CURLOPT_SSL_VERIFYPEER,?0L);
????????//?重定向
????????curl_easy_setopt(curl,?CURLOPT_FOLLOWLOCATION,?1);
????????//?URL路徑
????????curl_easy_setopt(curl,?CURLOPT_URL,?url);
????????//?查找次數(shù),防止查找太深
????????curl_easy_setopt(curl,?CURLOPT_MAXREDIRS,?1);
????????//?連接超時
????????curl_easy_setopt(curl,?CURLOPT_CONNECTTIMEOUT,?3);
????????//?接收數(shù)據(jù)時超時設置
????????curl_easy_setopt(curl,?CURLOPT_TIMEOUT,?3);
????????//?寫入回調(diào)函數(shù)
????????curl_easy_setopt(curl,?CURLOPT_WRITEFUNCTION,?WriteCallback);
????????curl_easy_setopt(curl,?CURLOPT_WRITEDATA,?&read_buffer);
????????curl_easy_perform(curl);
????????curl_easy_cleanup(curl);
????????return?read_buffer;
????}
????return?"None";
}
int?main(int?argc,?char?*argv[])
{
????std::string?urls?=?GetUrlPageOfString("https://www.lyshark.com");
????std::cout?<<?"接收長度:?"?<<?urls.length()?<<?"?bytes"?<<?std::endl;
????system("pause");
????return?0;
}
如下圖所示,則是運行后輸出內(nèi)存數(shù)據(jù)長度,當然我們也可以直接輸出urls
中的數(shù)據(jù),也就是網(wǎng)頁的源代碼;

本文作者: 王瑞 本文鏈接: https://www.lyshark.com/post/6aa9753b.html 版權聲明: 本博客所有文章除特別聲明外,均采用 BY-NC-SA 許可協(xié)議。轉載請注明出處!