cURL 是一個命令行工具,可以對文件傳輸使用許多協議,包括 HTTP、FTP、Secure Copy (SCP)、Telnet 等等。但是,除了可以用命令行通過 Internet 與端點對話外,還可以使用 libcurl 編寫簡單或復雜的程序,以自動化執行應用層的協議任務。本文將介紹 cURL 命令行工具,然后向您展示如何使用 libcurl 以及 C 和 Python 構建一個 HTTP 客戶端。
開發 HTTP 和 FTP 之類依賴于應用層協議的應用程序并不復雜,但也不簡單。進一步講,這不是應用程序的重點,因為大部分情況下,協議之上的內容才是真正重要的內容。因 此,libcurl 引起了許多人的興趣,因為它的重點是應用程序而不是開發的各個方面。注意,很少有應用程序開發自己的 TCP/IP 堆棧,所以老話重提:盡可能重用以最小化開發安排并提高應用程序的可靠性。
本文首先簡單介紹應用層協議,然后介紹 cURL、libcurl 并解釋它們的用法。
如 今構建應用程序已與過去大不相同。現在的應用程序需要能夠通過網絡或 Internet 進行通訊(提供人類可用的網絡 API 或接口),還要能支持用戶腳本化以提高靈活性。現代應用程序通常使用 HTTP 公開 Web 接口,并通過 Simple Mail Transport Protocol (SMTP) 提供警告通知。這些協議允許您將 Web 瀏覽器指向設備以獲得配置或狀態信息,并從設備或常用的電子郵件客戶端接收標準電子郵件(分別通過 HTTP 和 SMTP)。
這些 Web 服務通常構建在網絡堆棧的套接字層上(見圖 1)。套接字層實現一個最先出現在 Berkeley Software Distribution (BSD) 操作系統上的 API,并提取底層傳輸和網絡層協議的詳細信息。
Web 服務發生在客戶端和服務器之間的協議對話中。在 HTTP 上下文中,服務器是終端設備,客戶端是位于端點上的瀏覽器。對于 SMTP,服務器是郵件網關或端點用戶,客戶端是終端設備。在某些情況下,協議對話發生在兩個步驟(請求和響應)中,但另一些情況下,需要協商和通訊的通 信量更多。這種協商可能增加了大量復雜性,這可以通過 API 進行抽象,比如 libcurl。
cURL 最初的設計初衷是使用不同的協議(比如 FTP、HTTP、SCP 等)在端點之間移動文件。它最初是一個命令行實用工具,但現在也是一個綁定了 30 多種語言的庫。因此,現在不僅可以通過 shell 使用 cURL,您還可以構建合并了這個重要功能的應用程序。libcurl 庫也是可以移植的,支持 Linux?、IBM?AIX?操作系統、BSD、Solaris 以及許多其他 UNIX?變體。
獲取和安裝 libcurl 非常簡單,取決于您所運行的 Linux 發行版。如果運行的是 Ubuntu,您可以使用
apt-get
輕松安裝這些包。以下行演示了如何為 libcurl 安裝 libcurl 和 Python 綁定:
$
sudo apt-get install libcurl3
$
sudo apt-get install python-pycurl
|
?
apt-get
實用工具確保該過程滿足所有的依賴關系。
cURL 最開始是一個命令行工具,可以使用 Uniform Resource Locator (URL) 語法執行數據傳輸。考慮到它在命令行上的流行度,后來創建了一個可以在應用程序中生成這些行為的庫。如今,命令行 cURL 是 cURL 庫的包裝器。本文首先研究 cURL 作為命令行的功能,然后深入探討如何將它作為庫使用。
cURL 的兩種常見用法是使用 HTTP 和 FTP 協議進行文件傳輸。cURL 為這些協議提供一個簡單的接口。要使用 HTTP 從網站獲取文件,只需告訴 cURL 您要將網頁寫入到其中的本地文件的文件名、網站的 URL 以及要獲取的文件。讓我們看一下清單 1 中的簡單命令行示例。
$
curl -o test html www.exampledomain.com
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 43320 100 43320 0 0 55831 0 --:--:-- --:--:-- --:--:-- 89299
$
|
?
注意,由于我指定了域而不是文件,我將獲得根文件(index.html)。要使用 cURL 將該文件移動到 FTP 站點,可以使用
-T
選項指定要上傳的文件,然后提供 FTP 站點的 URL 以及文件的路徑。
清單 2. 使用 cURL 將
文件上傳
到 FTP 站點的示例
$
curl -T test.html ftp://user:password@ftp.exampledomain.com/ftpdir/
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 43320 0 0 100 43320 0 38946 0:00:01 0:00:01 --:--:-- 124k
$
|
?
是不是很簡單?學習了一些模式之后您會發現,cURL 使用起來非常簡單。但是您可以使用的選項非常多 —在 cURL 命令行中請求幫助(使用
--help
)可以得到 129 行選項。如果您覺得這還不算太多,那么還有一大批其他控制選項(從詳細度到安全性),以及特定于協議的配置項。
從開發人員的角度看,這還不算是 cURL 最令人興奮的地方。讓我們深入了解 cURL 庫,學習如何向應用程序添加文件傳輸協議。
如 果您有 10 年以上的腳本語言經驗,您就會注意到它們的標記有很大的變化。Python、Ruby、Perl 等這些腳本語言不僅包含套接字層(C 或 C++ 中也有),還包含了應用層協議 API。這些腳本語言合并了高級功能,可以創建 HTTP 服務器或客戶端。libcurl 庫為 C 和 C++ 之類的語言添加了類似的功能,但是它可以在不同的語言之間移植。在所有它支持的語言中都能找到與 libcurl 相當的行為,但是由于這些語言的差異很大(設想一下 C 和 Scheme),提供這些行為的方式也很不相同。
libcurl 庫以 API 的形式封裝清單 1 和清單 2 中描述的行為,因此它可以被高級語言使用(如今已超過 30 種)。本文提供了 libcurl 的兩個示例。第一個示例研究使用 c 構建的簡單 HTTP 客戶端(適合構建 Web 爬行器),第二個示例是一個使用 Python 創建的簡單 HTTP 客戶端。
C API 在 libcurl 功能上提供了兩個 API。easy 接口是一個簡單的同步 API(意味著當您使用請求調用 libcurl 時,將能夠滿足您的請求,直到完成或發生錯誤)。多接口可以進一步控制 libcurl,您的應用程序可以執行多個同步傳輸,并控制 libcurl 何時何地移動數據。
該示例使用 easy 接口。該 API 還能控制數據移動過程(使用回調),但正如其名稱所示,使用起來非常簡單。清單 3 提供了 HTTP 的 C 語言示例。
清單 3. 使用 libcurl easy 接口的 C HTTP 客戶端
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
#define MAX_BUF 65536
char wr_buf[MAX_BUF+1];
int wr_index;
/*
* Write data callback function (called within the context of
* curl_easy_perform.
*/
size_t write_data( void *buffer, size_t size, size_t nmemb, void *userp )
{
int segsize = size * nmemb;
/* Check to see if this data exceeds the size of our buffer. If so,
* set the user-defined context value and return 0 to indicate a
* problem to curl.
*/
if ( wr_index + segsize > MAX_BUF ) {
*(int *)userp = 1;
return 0;
}
/* Copy the data from the curl buffer into our buffer */
memcpy( (void *)&wr_buf[wr_index], buffer, (size_t)segsize );
/* Update the write index */
wr_index += segsize;
/* Null terminate the buffer */
wr_buf[wr_index] = 0;
/* Return the number of bytes received, indicating to curl that all is okay */
return segsize;
}
/*
* Simple curl application to read the index.html file from a Web site.
*/
int main( void )
{
CURL *curl;
CURLcode ret;
int wr_error;
wr_error = 0;
wr_index = 0;
/* First step, init curl */
curl =
curl_easy_init
;
if (!curl) {
printf("couldn't init curl\n");
return 0;
}
/* Tell curl the URL of the file we're going to retrieve */
curl_easy_setopt
( curl, CURLOPT_URL, "www.exampledomain.com" );
/* Tell curl that we'll receive data to the function write_data, and
* also provide it with a context pointer for our error return.
*/
curl_easy_setopt
( curl, CURLOPT_WRITEDATA, (void *)&wr_error );
curl_easy_setopt
( curl, CURLOPT_WRITEFUNCTION, write_data );
/* Allow curl to perform the action */
ret =
curl_easy_perform
( curl );
printf( "ret = %d (write_error = %d)\n", ret, wr_error );
/* Emit the page if curl indicates that no errors occurred */
if ( ret == 0 ) printf( "%s\n", wr_buf );
curl_easy_cleanup
( curl );
return 0;
}
|
?
最上方是必需的
include
文件,包括 cURL 根文件。接下來,我定義了兩個用于傳輸的變量。第一個變量是
wr_buf
,表示將在其中寫入傳入數據的緩沖區。
wr_index
表示緩沖區的當前寫入索引。
轉到
main
函數,該函數使用 easy API 進行設置。所有 cURL 調用都通過維護特定請求狀態的句柄進行操作。這稱為
CURL
指針引用。本例還創建一個特殊的返回碼,稱為
CURLcode
。在使用任何 libcurl 函數之前,您需要調用
curl_easy_init
獲取
CURL
句柄。接下來,注意
curl_easy_setopt
調用的數量。它們為特定的操作配置句柄。對于這些調用,您提供句柄、命令和選項。首先,本例使用
CURLOPT_URL
指定要獲取的 URL。然后,它使用
CURL_WRITEDATA
提供一個上下文變量(在本例中,它是內部的 write 錯誤變量)。最后,它使用
CURLOPT_WRITEFUNCTION
指定數據可用時應該調用的函數。在啟動 API 之后,API 將使用它讀取的數據多次調用該函數。
要開始傳輸,調用
curl_easy_perform
。它的工作是根據之前的配置執行傳輸。調用該函數時,在完成傳輸或發生錯誤之前該函數不會返回。
main
的最后一步是提交返回狀態,提交頁面讀取,最后使用
curl_easy_cleanup
清除(當使用句柄執行完操作后)。
現在看看
write_data
函數。該函數是針對特定操作收到數據時調用的回調。注意,當您從網站讀取數據時,將寫入該數據(
write_data
)。將向回調提供一個緩沖區(包含可用數據)、成員數量和大小(緩沖中可用數據總量)、上下文指針。第一個任務是確保緩沖區(
wr_buf
)的空間足以寫入數據。如果不夠,它將設置上下文指針并返回 0,表示出現問題。否則,它將 cURL 緩沖區的數據復制到您的緩沖區,并增加索引,指向要寫入的下一個位置。本例還終止字符串,稍后可以對其使用
printf
。最后,它返回 libcurl 操作的字節數量。這將告訴 libcurl 數據被提取,它也可以丟棄該數據。這就是從網站將文件讀取到內存的相對簡單的方法。
本節提供的示例類似于基于 C 的 HTTP 客戶端,不過它使用的是 Python。Python 是一種非常有用的面向對象的腳本語言,在原型化和構建生產軟件方面非常突出。示例假設您較熟悉 Python,但使用不多,因此不要期望過高。
這個簡單的 Python HTTP 客戶端使用
pycurl
,如清單 4 所示。
清單 4. 使用 libcurl 的
pycurl
接口的 Python HTTP 客戶端
import sys
import pycurl
wr_buf = ''
def write_data( buf ):
global wr_buf
wr_buf += buf
def main:
c =
pycurl.Curl
c.
setopt
( pycurl.URL, 'http://www.exampledomain.com' )
c.
setopt
( pycurl.WRITEFUNCTION, write_data )
c.
perform
c.
close
main
sys.stdout.write(wr_buf)
這比 C 語言版本簡單的多。它首先導入必需的模塊(用于標準系統的
sys
和
pycurl
模塊)。接下來,它定義 write 緩沖區(
wr_buf
)。像 C 程序中一樣,我聲明一個
write_data
函數。注意,該函數只有一個參數:從 HTTP 服務器中讀取的數據緩沖區。我將該緩沖區連接到全局 write 緩沖區。
main
函數首先創建一個
Curl
句柄,然后使用
setopt
方法為傳輸定義
URL
和
WRITEFUNCTION
。它調用
perform
方法啟動傳輸并關閉句柄。最后,它調用
main
函數,并將 write 緩沖區提交到
stdout
。注意,在這種情況下,您不需要錯誤上下文指針,因為您使用了 Python 字符串連接,這就是說您不會使用大小固定的字符串。
本文僅僅簡單介紹了 libcurl,介紹了它支持的多種協議和語言。希望這能夠展示它如何輕松構建使用應用層協議(如 HTTP)的應用程序。libcurl 網站提供了很多示例和有用的文檔。下一次開發 Web 瀏覽器、爬行器或其他有應用層協議要求的應用程序時,試試 libcurl。它一定能大大減少您的開發時間,并找回編碼的樂趣。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

