LibCurl是一個開源的免費的多協議數據傳輸開源庫,該框架具備跨平臺性,開源免費,並提供了包括`HTTP`、`FTP`、`SMTP`、`POP3`等協議的功能,使用`libcurl`可以方便地進行網路數據傳輸操作,如發送`HTTP`請求、下載文件、發送電子郵件等。它被廣泛應用於各種網路應用開發中,... ...
LibCurl是一個開源的免費的多協議數據傳輸開源庫,該框架具備跨平臺性,開源免費,並提供了包括HTTP
、FTP
、SMTP
、POP3
等協議的功能,使用libcurl
可以方便地進行網路數據傳輸操作,如發送HTTP
請求、下載文件、發送電子郵件等。它被廣泛應用於各種網路應用開發中,特別是涉及到數據傳輸的場景。
首先讀者需要自行下載該庫,如下筆者選擇下載curl-8.0.1.zip
這個源代碼版本,讀者可找到如下頁面,並點擊對應版本完成下載,當下載好以後讀者可自行將其解壓縮到任意目錄下。
當讀者解壓縮後,可打開VS2013 開發人員命令提示
並切換帶該目錄中的curl-8.0.1\winbuild
目錄,通過執行如下兩條命令即可分別實現編譯靜態庫或動態庫,我們以靜態庫編譯為主,執行如下命令讀者可自行等待一段時間。
- 動態庫: nmake /f Makefile.vc mode=dll VC=13 MACHINE=x86 DEBUG=no
- 靜態庫: nmake / f Makefile.vc mode = static VC = 13 ENABLE_IDN = no MACHINE = x86 DEBUG = no
這個庫在編譯通過後會自動生成文件到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 許可協議。轉載請註明出處!