C++ LibCurl 庫的使用方法

来源:https://www.cnblogs.com/LyShark/archive/2023/08/23/17650548.html
-Advertisement-
Play Games

LibCurl是一個開源的免費的多協議數據傳輸開源庫,該框架具備跨平臺性,開源免費,並提供了包括`HTTP`、`FTP`、`SMTP`、`POP3`等協議的功能,使用`libcurl`可以方便地進行網路數據傳輸操作,如發送`HTTP`請求、下載文件、發送電子郵件等。它被廣泛應用於各種網路應用開發中,... ...


LibCurl是一個開源的免費的多協議數據傳輸開源庫,該框架具備跨平臺性,開源免費,並提供了包括HTTPFTPSMTPPOP3等協議的功能,使用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目錄內,讀者可自行打開該目錄,即可看到該目錄內的頭文件以及庫目錄文件,如下圖所示;

讀者可自行配置這個靜態庫,通常只需要配置includelib文件即可,該庫的使用很簡單,首先我們需要調用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 許可協議。轉載請註明出處!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、過濾器 1.什麼是過濾器? 過濾器是一種用於JavaWeb應用程式中的組件,它可以攔截HTTP請求和響應,以實現一些特定的功能。 過濾器可以對請求和響應進行修改,可以阻止請求進入Servlet,也可以修改響應返回給客戶端。 2.過濾器的主要作用 登錄驗證:檢查用戶是否已經登錄,如果沒有登錄則跳轉 ...
  • Goroutine 是 Golang 協程的實現。相比於其他語言,Goroutine 更加輕量,更加簡單。Goroutine 是學習 Golang 必須掌握的知識。本文介紹 Goroutine 的基礎知識,包含 基礎語法使用和 Channel。 ...
  • 這兩天給我們開發的Chrome插件:[Youtube中文配音](https://youtube-dubbing.com/)增加了賬戶註冊和登錄功能,其中有一步是郵箱驗證,所以這邊會在Spring Boot後臺給用戶的郵箱發個驗證信息。如果發郵件,之前的文章教程里就有,這裡就不說了,著重說說這兩天發現 ...
  • # Java將MySQL建表語句轉換為SQLite的建表語句 **源代碼**: ```java package com.fxsen.platform.core.util; import java.util.HashMap; import java.util.Map; import java.util ...
  • 本文主要講述通過MyBatis、JDBC等做大數據量數據插入的案例和結果。 ## 30萬條數據插入插入資料庫驗證 - 實體類、mapper和配置文件定義 - - User實體 - mapper介面 - mapper.xml文件 - jdbc.properties - sqlMapConfig.xml ...
  • 本文已收錄至GitHub,推薦閱讀 👉 [Java隨想錄](https://github.com/ZhengShuHai/JavaRecord) 微信公眾號:Java隨想錄 > 原創不易,註重版權。轉載請註明原作者和原文鏈接 [TOC] 某天,爪哇星球上,一個普通的房間,正在舉行一場秘密的面試: ...
  • ITGeeker技術奇客發佈的開源Word文字替換小工具更新到v1.0.1.0版本啦,現已支持Office Word文檔頁眉和頁腳的替換。 同時ITGeeker技術奇客修複了v1.0.0.0版本因替換數字引起的in ‘ requires string as left operand, not int ...
  • 本文已收錄至GitHub,推薦閱讀 👉 [Java隨想錄](https://github.com/ZhengShuHai/JavaRecord) 微信公眾號:Java隨想錄 > 原創不易,註重版權。轉載請註明原作者和原文鏈接 [TOC] 前面我們講了可達性分析和根節點枚舉,介紹完了GC的前置工作, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...