LibCurl是一個開源的免費的多協定資料傳輸開源庫,該框架具備跨平臺性,開源免費,並提供了包括HTTP
、FTP
、SMTP
、POP3
等協定的功能,使用libcurl
可以方便地進行網路資料傳輸操作,如傳送HTTP
請求、下載檔案、傳送電子郵件等。它被廣泛應用於各種網路應用開發中,特別是涉及到資料傳輸的場景。
首先讀者需要自行下載該庫,如下筆者選擇下載curl-8.0.1.zip
這個原始碼版本,讀者可找到如下頁面,並點選對應版本完成下載,當下載好以後讀者可自行將其解壓縮到任意目錄下。
當讀者解壓縮後,可開啟VS2013 開發人員命令提示
並切換帶該目錄中的curl-8.0.1\winbuild
目錄,通過執行如下兩條命令即可分別實現編譯靜態庫或動態庫,我們以靜態庫編譯為主,執行如下命令讀者可自行等待一段時間。
這個庫在編譯通過後會自動生成檔案到builds\libcurl-vc13-x86-release-static-ipv6-sspi-schannel
目錄內,讀者可自行開啟該目錄,即可看到該目錄內的標頭檔案以及庫目錄檔案,如下圖所示;
讀者可自行設定這個靜態庫,通常只需要設定include
和lib
檔案即可,該庫的使用很簡單,首先我們需要呼叫curl_easy_init()
函數對CURL
物件進行初始化,接著通過呼叫curl_easy_setopt()
並傳入一個存取URL
連結,當存取成功後則可呼叫curl_easy_perform()
函數得到存取結果,這就是該庫基本使用方法,如下程式碼。
#define CURL_STATICLIB
#define BUILDING_LIBCURL
#include <iostream>
#include "curl/curl.h"
#pragma comment (lib,"libcurl_a.lib")
#pragma comment (lib,"wldap32.lib")
#pragma comment (lib,"ws2_32.lib")
#pragma comment (lib,"Crypt32.lib")
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 << "返回狀態: " << res << std::endl;
system("pause");
return 0;
}
執行上述程式碼,讀者可看到網站www.lyshark.com
的原始碼,如下圖所示;
上述程式碼中的curl_easy_setopt()
函數第二個引數可以使用多種型別的變數定義,我們可以通過傳入不同的常數來定義請求頭中的引數,例如當我們需要修改協定頭時,可以使用CURLOPT_HTTPHEADER
常數,並在其後第三個引數中傳入該常數所對應的結構即可,這個結構體定義有許多型別,具體如下下表所示;
常數名稱 | 描述 |
---|---|
CURLINFO_EFFECTIVE_URL | 最後一個有效的URL地址 |
CURLINFO_HTTP_CODE | 最後一個收到的HTTP程式碼 |
CURLINFO_FILETIME | 遠端獲取檔案的時間,如果無法獲取,則返回值為-1 |
CURLINFO_TOTAL_TIME | 最後一次傳輸所消耗的時間 |
CURLINFO_NAMELOOKUP_TIME | 名稱解析所消耗的時間 |
CURLINFO_CONNECT_TIME | 建立連線所消耗的時間 |
CURLINFO_PRETRANSFER_TIME | 從建立連線到準備傳輸所使用的時間 |
CURLINFO_STARTTRANSFER_TIME | 從建立連線到傳輸開始所使用的時間 |
CURLINFO_REDIRECT_TIME | 在事務傳輸開始前重定向所使用的時間 |
CURLINFO_SIZE_UPLOAD | 以位元組為單位返回上傳資料量的總值 |
CURLINFO_SIZE_DOWNLOAD | 以位元組為單位返回下載資料量的總值 |
CURLINFO_SPEED_DOWNLOAD | 平均下載速度 |
CURLINFO_SPEED_UPLOAD | 平均上傳速度 |
CURLINFO_HEADER_SIZE | header部分的大小 |
CURLINFO_HEADER_OUT | 傳送請求的字串 |
CURLINFO_REQUEST_SIZE | 在HTTP請求中有問題的請求的大小 |
CURLINFO_SSL_VERIFYRESULT | 通過設定CURLOPT_SSL_VERIFYPEER返回的SSL證書驗證請求的結果 |
CURLINFO_CONTENT_LENGTH_DOWNLOAD | 從Content-Length: field中讀取的下載內容長度 |
CURLINFO_CONTENT_LENGTH_UPLOAD | 上傳內容大小的說明 |
CURLINFO_CONTENT_TYPE | 下載內容的Content-Type:值,NULL表示伺服器沒有傳送有效的Content-Type:header |
如下案例是一個簡單的GET
請求封裝,通過呼叫GetStatus()
函數實現對特定頁面發起請求的功能,其中curl_slist_append()
用於增加新的請求頭資料,在呼叫curl_easy_setopt()
函數時,分別傳入了CURLOPT_HTTPHEADER
設定請求頭,CURLOPT_WRITEFUNCTION
設定回撥,CURLINFO_PRIMARY_IP
獲取目標IP
地址,CURLINFO_RESPONSE_CODE
獲取目標返回程式碼,此處的write_data()
函數直接返回0則表示遮蔽所有的頁面輸出內容。
#define CURL_STATICLIB
#define BUILDING_LIBCURL
#include <iostream>
#include "curl/curl.h"
#pragma comment (lib,"libcurl_a.lib")
#pragma comment (lib,"wldap32.lib")
#pragma comment (lib,"ws2_32.lib")
#pragma comment (lib,"Crypt32.lib")
using namespace std;
// 設定CURLOPT_WRITEFUNCTION回撥函數,返回為空遮蔽輸出
static size_t write_data(char *d, size_t n, size_t l, void *p)
{
return 0;
}
// 獲取網站返回值
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 自定義請求的網站
curl_easy_setopt(easy_handle, CURLOPT_URL, UrlPage);
// CURLOPT_WRITEFUNCTION 設定回撥函數,遮蔽輸出
curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, write_data);
// 執行CURL存取網站
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 獲取目標返回狀態
return_code = curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &retcode);
if ((CURLE_OK == return_code) && retcode)
{
std::cout << "返回狀態碼: " << 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地址以及頁面返回狀態,如下圖所示;
當然該庫同樣支援POST
請求方式,在使用POST
請求時我們可以通過CURLOPT_COOKIEFILE
引數指定Cookie
引數,通過CURLOPT_POSTFIELDS
指定POST
的資料集,而如果需要使用代理模式則可以通過CURLOPT_PROXY
方式來指定代理地址,
#define CURL_STATICLIB
#define BUILDING_LIBCURL
#include <iostream>
#include "curl/curl.h"
#pragma comment (lib,"libcurl_a.lib")
#pragma comment (lib,"wldap32.lib")
#pragma comment (lib,"ws2_32.lib")
#pragma comment (lib,"Crypt32.lib")
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引數
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, Cookie);
// 指定post內容
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[])
{
// 傳入網址 cookie 以及post引數
SendPost("https://www.lyshark.com/post.php", "1e12sde342r2", "&logintype=uid&u=xieyan&psw=xxx86");
system("pause");
return 0;
}
該函數的呼叫需要有一個POST結構才可測試,此處由於我並沒有指定介面所有返回了頁面錯誤資訊,如下圖所示;
接著繼續實現下載頁面到原生的功能,該功能實現的原理是利用write_data
回撥函數,當頁面資料被讀入到記憶體時回撥函數會被觸發,在該回撥函數的內部通過呼叫fwrite
函數將ptr
指標中的資料儲存本地,實現這段程式碼如下所示;
#define CURL_STATICLIB
#define BUILDING_LIBCURL
#include <iostream>
#include "curl/curl.h"
#pragma comment (lib,"libcurl_a.lib")
#pragma comment (lib,"wldap32.lib")
#pragma comment (lib,"ws2_32.lib")
#pragma comment (lib,"Crypt32.lib")
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資料
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
// 查詢次數,防止查詢太深
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 1);
// 設定連線超時
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
// 接收資料時超時設定
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
if ((fp = fopen(FileName, "w")) == NULL)
{
curl_easy_cleanup(curl);
return FALSE;
}
// CURLOPT_WRITEFUNCTION 將後繼的動作交給write_data函數處理
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_perform(curl);
curl_easy_cleanup(curl);
return TRUE;
}
int main(int argc, char *argv[])
{
// 下載網頁到本地
GetUrl("https://www.lyshark.com", "./lyshark.html");
system("pause");
return 0;
}
當讀者執行上述程式後,即可將www.lyshark.com
網站頁面原始碼,下載到本地當前目錄下lyshark.html
,輸出效果如下圖所示;
為了能解析引數,我們還是需要將頁面原始碼讀入到記憶體中,要實現這個需求並不難,首先我們定義一個std::string
容器,然後當有新資料產生時觸發WriteCallback
在該函數內,我們直接將資料拷貝到一個記憶體指標中,也就是儲存到read_buffer
內,並將該緩衝區返回給呼叫者即可,如下則是完整原始碼。
#define CURL_STATICLIB
#define BUILDING_LIBCURL
#include <iostream>
#include <string>
#include "curl/curl.h"
#pragma comment (lib,"libcurl_a.lib")
#pragma comment (lib,"wldap32.lib")
#pragma comment (lib,"ws2_32.lib")
#pragma comment (lib,"Crypt32.lib")
using namespace std;
// 儲存回撥函數
size_t WriteCallback(char *contents, size_t size, size_t nmemb, void *userp)
{
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
// 獲取資料並放入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);
// 查詢次數,防止查詢太深
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 1);
// 連線超時
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
// 接收資料時超時設定
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
// 寫入回撥函數
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;
}
如下圖所示,則是執行後輸出記憶體資料長度,當然我們也可以直接輸出urls
中的資料,也就是網頁的原始碼;
本文作者: 王瑞
本文連結: https://www.lyshark.com/post/6aa9753b.html
版權宣告: 本部落格所有文章除特別宣告外,均採用 BY-NC-SA 許可協定。轉載請註明出處!