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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...