天天看點

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

libcurl是一個免費開源的網絡傳輸庫(the multiprotocol file transfer library),支援ftp、ftps、http、https、telnet、ldap、pop3、smtp、rtmp、rtsp、smb等多種協定。libcurl中封裝了支援這些協定的網絡通信子產品,支援跨平台,支援Windows,Unix,Linux等多個作業系統。libcurl提供了一套統一樣式的API接口,我們不用關注各種協定下網絡通信的實作細節,隻需要調用這些API就能輕松地實作基于這些協定的資料通信。本文我們來詳細講述一下如何編譯開源的libcurl庫。

1、引言

之前開發的一個工具軟體中有個發送告警郵件的功能,最開始試圖通過socket套接字去實作郵件的發送,先是和郵件伺服器建立連接配接,然後進行SMTP郵件的協商,協商完成後将郵件發送出去。結果聯調測試時遇到了一堆問題,各種網絡異常沒有得到很好的處理,導緻發送郵件功能根本沒法使用。      
【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

後來想到了libcurl開源庫,該庫中封裝了smtp郵件協定的所有功能,從連接配接到協定協商,再到郵件資料的發送,提供一套完整、可靠、穩定的郵件發送機制。于是想去下載下傳libcurl的開源代碼,手動将庫編譯出來,然後再拿到工具軟體中使用,同時也正好熟悉一下libcurl開源庫的編譯過程。

關于發送告警郵件的項目,可以檢視之前寫的文章:(下列文章中提供免費C++源碼項目下載下傳)

IPC攝像頭線上狀态ping檢測與告警郵件發送項目總結(提供源碼下載下傳)​

2、直接編譯libcurl工程,提示找不到ssh.h

到libcurl的官網上下載下傳libcurl源碼。我下載下傳的是2022年5月11日釋出的curl 7.83.1版本。解壓後,打開curl-7.83.1\projects\Windows目錄,該目錄中儲存了支援Widnows平台上的Visual Studio編譯的工程檔案,提供了對多個Visual Studio版本的支援:

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

此處我是用Visual Studio 2010編譯的,是以選擇VC10目錄。打開VC10目錄,輕按兩下curl-all.sln解決方案檔案啟動VS2010打開解決方案,該解決方案中包含了curl和libcurl兩個工程,libcurl是動态庫工程,我們編譯該工程就可以了。

在libcurl源代碼根路徑中提供了CMakeLists.txt檔案,如果開源庫中不提供支援Visual

Studio編譯的工程檔案,我們可以對CMakeLists.txt檔案使用CMAKE工具,生成指定版本的Visual

Studio工程檔案。注意在使用CMake工具時,開源代碼的所在路徑必須是全英文的,不能包含中文,否則CMake工具會将路徑中的中文字元識别位亂碼,導緻生成Visual

Studio工程檔案失敗。

啟動對libcurl工程的編譯後,就報錯了,如下所示:

1>------ 已啟動生成: 項目: libcurl, 配置: DLL Release - DLL OpenSSL Win32

------ 1>生成啟動時間為 2022/5/17 16:32:39。 1>InitializeBuildStatus: 1> 正在建立“........\build\Win32\VC10\DLL Release - DLL

OpenSSL\lib\libcurl.unsuccessfulbuild”,因為已指定“AlwaysCreate”。

1>ClCompile: 1> x509asn1.c 1> wolfssl.c 1> vtls.c

1>e:\開源庫集合\curl-7.83.1\lib\vssh/ssh.h(28): fatal error C1083:

無法打開包括檔案:“libssh2.h”: No such file or directory 1> sectransp.c

1>e:\開源庫集合\curl-7.83.1\lib\vssh/ssh.h(28): fatal error C1083:

無法打開包括檔案:“libssh2.h”: No such file or directory 1> schannel_verify.c

提示找不到ssh.h。從解決方案配置中看到了libssh2庫的身影,應該是目前的libcurl庫使用了開源的libssh2庫,于是去下載下傳libssh2開源庫代碼,一是拿到libssh2庫的頭檔案,二是将libssh2庫的庫檔案編譯出來,libcurl連結時肯定要用到libssh2庫的。

3、下載下傳并編譯libssh2開源代碼,部署到libcurl目錄中

SSH為Secure Shell的縮寫,專為遠端登入會話和其他網絡服務提供安全性的協定。利用 SSH 協定可以有效防止遠端管理過程中的資訊洩露問題。SSH1由于存在安全漏洞,是以産生了更為安全的SSH2,開源libssh2庫實作了SSH2協定。

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

到libssh2官網上下載下傳libssh2源碼,解壓後打開根路徑,發現根路徑下有個win32檔案夾,點進去後看到Visual Studio相關的工程檔案,如下所示:

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

工程檔案是VC++6.0版本的,直接将工程檔案拖到VS2010中,将工程檔案更新到VS2010版本,然後發起編譯,很快就編譯完成了。檢視了一下libssh2工程的屬性,發現該工程編譯出來的是靜态庫,不是動态庫,如下所示:

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

因為此處生成的是libssh2靜态庫,是以我們在libcurl的解決方案配置中選擇“DLL Release - DLL OpenSSL”工程選項:

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

這個選項是使用libssh2靜态庫的。而DLL Release - DLL OpenSSL - DLL LibSSH2選項中則使用的是libssh2動态庫,是以我們要使用前者。

于是将libssh2庫目錄中的include頭檔案以及編譯出來的靜态庫拷貝到libcurl的工程目錄curl-7.83.1中。我們在curl-7.83.1目錄中建立一個名叫libssh2檔案夾,然後在該檔案夾中建立一個用來存放頭檔案的include檔案夾和一個用來存放libssh2靜态庫的lib檔案夾,如下:

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

于是我們到libcurl的工程屬性中修改一下配置,首先修改引用libssh2\include下頭檔案的路徑(包含頭檔案):

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

然後修改引用libssh2\lib目錄下的libssh2.lib檔案的路徑(連結時引入的.lib檔案):

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

于是繼續編譯libcurl工程,結果又提示找不到ssl.h頭檔案:

1>------ 已啟動生成: 項目: libcurl, 配置: DLL Release - DLL OpenSSL Win32

------ 1>生成啟動時間為 2022/5/17 17:01:17。 1>InitializeBuildStatus: 1> 正在對“........\build\Win32\VC10\DLL Release - DLL

OpenSSL\lib\libcurl.unsuccessfulbuild”執行 Touch 任務。 1>ClCompile: 1>

x509asn1.c 1> wolfssl.c 1> vtls.c 1> sectransp.c 1>

schannel_verify.c 1> schannel.c 1> rustls.c 1> openssl.c

1>........\lib\vtls\openssl.c(63): fatal error C1083:

無法打開包括檔案:“openssl/ssl.h”: No such file or directory 1> nss.c 1>

mbedtls_threadlock.c

應該是和libssh2庫一樣,也需要擷取openssl開源庫的頭檔案和庫檔案。

4、擷取openssl開源庫的頭檔案和庫檔案,部署到libcurl目錄中

到curl-7.83.1目錄中去搜尋,也找不到ssl.h檔案。那應該是要去下載下傳openssl開源庫的源碼并編譯。到openssl官網上将源碼下載下傳下來,解壓後在根路徑中沒有找到Windows平台編譯專用的Visual Studio的工程檔案,而libcurl和libssh2源碼中是有專用的Visual Studio工程檔案的。如果要手動編譯openssl,則需要安裝perl工具,然後通過perl去執行代碼的編譯,這個過程略顯複雜。

我們可以到第三方網站上去下載下傳别人已經編譯好的openssl庫,比如:

Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions

​​​http://slproweb.com/products/Win32OpenSSL.html​​

打開頁面後,将頁面向下拉動找到下載下傳連結,我們的軟體是32位的,是以選擇32位版本的openssl即可。但下載下傳連結比較多,我們隻需要選擇Win32 OpenSSL v3.0.3 Light版本即可,點選MSI按鈕,下載下傳MSI安裝包:

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

然後執行該MSI安裝包,這樣就能把openssl頭檔案和庫檔案釋放到目标路徑中。然後打開安裝目錄檢視安裝的内容,從include路徑中取出所有頭檔案,從lib路徑中取出用于連結的.lib庫,從bin路徑中取出libssl-3.dll及其依賴的庫。lib路徑中的檔案比較多:

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

我們隻需要取出libcurl連結時要用的libssl.lib庫檔案即可。

bin路徑下的檔案也比較多,除了要拿openssl庫的主檔案libssl-3.dll,還要取出哪些libssl-3.dll庫依賴的dll檔案呢?其實很簡單,我們隻需要使用Dependency Walker工具打開libssl-3.dll檔案,看看libssl-3.dll依賴哪些dll庫,如下所示:

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

除去libssl-3.dll依賴的一些系統庫之外,我們看到了libssl-3.dll依賴了libcrypto-3.dll庫,

在這個地方有個細節需要注意一下,Dependency

Walker工具在Win10系統中打開dll很慢,等待的時間會比較長,估計得等1分鐘左右,使用時要去耐心等待一會,等待Dependency

Walker打開dll庫完成,估計是因為Dependency Walker對Win10的相容性不好導緻打開dll檔案很慢的。

和libssh2庫處理類似,我們也在curl-7.83.1目錄的根目錄中建立一個openssl目錄,在該目錄再建立一個存放openssl頭檔案夾include,再建立一個存放.lib和.dll檔案的lib路徑。然後從之前Win32 OpenSSL v3.0.3 Light的安裝目錄中将相關檔案拷貝過去。

   接下來,我們到libcurl的工程屬性中修改一下配置,首先修改引用openssl\include下的頭檔案路徑(包含頭檔案):      
【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

然後修改引用openssl\lib目錄下的libssl.lib檔案的路徑(連結時引入的.lib檔案):

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

于是再編譯libcurl工程,結果還是報找不到ssl.h頭檔案的錯誤:

1>------ 已啟動生成: 項目: libcurl, 配置: DLL Release - DLL OpenSSL Win32

------ 1>生成啟動時間為 2022/5/17 17:01:17。 1>InitializeBuildStatus: 1> 正在對“........\build\Win32\VC10\DLL Release - DLL

OpenSSL\lib\libcurl.unsuccessfulbuild”執行 Touch 任務。 1>ClCompile: 1>

x509asn1.c 1> wolfssl.c 1> vtls.c 1> sectransp.c 1>

schannel_verify.c 1> schannel.c 1> rustls.c 1> openssl.c

1>........\lib\vtls\openssl.c(63): fatal error C1083:

無法打開包括檔案:“openssl/ssl.h”: No such file or directory 1> nss.c 1>

mbedtls_threadlock.c

後來查閱代碼得知,代碼中引用ssl.h頭檔案的地方,都加上了openssl相對路徑,如下:

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

為了解決這個問題,我們再在include中添加一個openssl子檔案夾,将所有openssl的頭檔案都搞到這個openssl子檔案夾中存放,然後libcurl就能編譯通過了。

5、将編譯好的libcurl拿到測試項目中測試

将編譯好的release版本的libcurl庫及其依賴的幾個庫,拷貝到之前編寫的包含發送郵件的程式中編譯,運作測試程式,打開郵箱參數設定視窗,如下所示:      
【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

在郵箱參數設定視窗中填寫好163郵箱SMTP伺服器位址及端口、發送郵件位址和接收郵件通知的接收郵件位址,點選“發送接收郵件”按鈕,發送測試郵件。經驗證,是可以将測試郵件發出去的:

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

目标郵箱中也是能收到郵件的,郵件内容也是正确的。

至于如何開通發送郵件中的SMTP服務,可以參見之前寫的這篇文章:

VC++調用libcurl開源庫實作發送郵件的功能(附源碼)​

6、使用CMake工具手動生成VS工程檔案,可以輕易地編譯出libcurl庫

libcurl開源代碼的根目錄中就有個CMakeLists.txt檔案,該檔案中配置了生成Windows平台上的Visual Studio工程檔案的相關内容,于是使用CMake工具嘗試自行生成VS工程檔案。      

CMake 是一個跨平台的開源建構系統,通過讀取放置在每個源碼根目錄中的配置檔案CMakeLists.txt中的内容,生成指定平台的标準建構檔案,比如為 Unix 平台生成makefiles檔案(用GCC編譯),為 Windows平台的MSVC(Microsoft Visual Studio C++)生成 projects/workspaces(用Visual Studio編譯)或makefile檔案(用nmake編譯)。

打開CMake工具後,先設定libcurl源碼的根路徑,然後再設定生成的Visual Studio工程檔案的目錄:

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

然後點選Generate按鈕,彈出選擇哪種解決方案:

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

此處我們需要使用VS2010,所生成VC++工程檔案的時間比較長,大概需要等待幾分鐘時間,生成完成後,我們打開對應的目錄,如下所示:

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

打開後編譯libcurl工程即可,編譯速度很快。根據編譯時列印的資訊得知,該種方式下使用的是wolfssl,沒有使用openssl,libcurl的目錄中也找不到openssl的頭檔案,是以通過CMake工具生成的VS工程編譯時不需要libssh2和openssl開源庫了,如下:

【C++進階】詳解C++開源網絡傳輸庫libcurl的編譯過程

根據編譯輸出的路徑,可以到對應的路徑中找到生成的libcurl庫檔案:libcurl_imp.lib和libcurl.dll。

7、總結