curl是Linux下一個很是著名的下載庫,經過這個庫,能夠很簡單的實現文件的下載等操做。
看一個簡單的例子:css
下面是轉載的curl詳細使用:html
curl->libcurl的手冊能夠查看node
http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTWRITEDATAapi
譯者:JGood(http://blog.csdn.net/JGood )數組
譯者注:這是一篇介紹如何使用libcurl的入門教程。文檔不是逐字逐句按原文翻譯,而是根據筆者對libcurl的理解,參考原文寫成。文中用到的一 些例子,可能不是出自原文,而是筆者在學習過程當中,寫的一些示例程序(筆者使用的libcurl版本是:7.19.6)。出如今這裏主要是爲了更好的說明 libcurl的某些api函數的使用。許多例子都參考libcurl提供的example代碼。原文example中的提供的示例程序徹底使用C語言, 而這裏筆者提供的例子使用C++語言。由於能力有限,對於libcurl的某些理解和使用可能有誤,歡迎批評指正。瀏覽器
目標本文檔介紹了在應用程序開發過程當中,如何正確使用libcurl的基本方式和指導原則。文檔使用C語言來調用libcurl的接口,固然也適用於其餘與C語言接近的語言。緩存
文檔主要針對使用libcurl來進行開發的人員。文檔所摜的應用程序泛指你寫的源代碼,這些代碼使用了libcurl進行數據傳輸。安全
更多關於libcurl的功能和接口信息,能夠在相關的主頁上查閱。服務器
編譯源碼有不少種不一樣的方式來編譯C語言代碼。這裏使用UNIX平臺下的編譯方式。即便你使用的是其餘的操做系統,你仍然能夠經過閱讀本文檔來獲取許多有用的信息。cookie
編譯你的編譯器必須知道libcurl頭文件的位置。因此在編譯的時候,你要設置頭文件的包含路徑。可使用curl-config工具來獲取這方面的信息:
$ curl-config –cflags
連接編譯完源碼(這時的源代碼不是指libcurl的源代碼,你是你本身寫的程序代碼)以後,你還必須把目標文件連接成單個可執行文件。你要連接 libcurl庫,以及libcurl所依賴的其餘庫,例如OpenSLL庫。固然可能還須要一些其餘的操做系統庫。最後你還要設置一些編譯選項,固然可 以使用curl-config工具簡化操做:
$curl-config –libs
是否使用SSL定製編譯libcurl。與其餘庫不一樣的是,libcurl能夠定製編譯,根據實際須要是否支持某些特性,如是否支持SSL傳輸,像HTTPS和 FTPS。若是決定須要支持SSL,必須在編譯時正確的設置。可使用’curl-config’來判斷libcurl庫是否支持SSL:
$ curl-config –feature
autoconf宏當你編寫配置腳原本檢測libcurl及其相應設置時,你可使用預約義宏。文檔docs/libcurl/libcurl.m4告訴你如何使用這些宏。
跨平臺的可移植的代碼libcurl的開發人員花費很大的努力,使libcurl儘量在大多數平臺上正常運行。
全局初始化應用程序在使用libcurl以前,必須先初始化libcurl。libcurl只需初始化一次。可使用如下語句進行初始化:
curl_global_init()接收一個參數,告訴libcurl如何初始化。參數CURL_GLOBAL_ALL 會使libcurl初始化全部的子模塊和一些默認的選項,一般這是一個比較好的默認參數值。還有兩個可選值:
CURL_GLOBAL_WIN32只能應用於Windows平臺。它告訴libcurl初始化winsock庫。若是winsock庫沒有正確地初始化,應用程序就不能使用socket。在應用程序中,只要初始化一次便可。
CURL_GLOBAL_SSL若是libcurl在編譯時被設定支持SSL,那麼該參數用於初始化相應的SSL庫。一樣,在應用程序中,只要初始化一次便可。
libcurl有默認的保護機制,若是在調用curl_easy_perform時它檢測到尚未經過curl_global_init進行初始 化,libcurl會根據當前的運行時環境,自動調用全局初始化函數。但必須清楚的是,讓系統自已初始化不是一個好的選擇。
當應用程序再也不使用libcurl的時候,應該調用curl_global_cleanup來釋放相關的資源。
在程序中,應當避免屢次調用curl_global_init和curl_global_cleanup。它們只能被調用一次。
libcurl提供的功能在運行時根據libcurl支持的特性來進行開發,一般比編譯時更好。能夠經過調用curl_version_info函數返回的結構體來獲取運行時的具 體信息,從而肯定當前環境下libcurl支持的一些特性。下面是筆者在visual studio2008中調用相關函數獲取libcurl版本信息的截圖:
首先介紹libcurl中被稱爲easy interface的api函數,全部這些函數都是有相同的前綴:curl_easy 。
當前版本的libcurl也提供了multi interface,關於這些接口的詳細使用,在下面的章節中會有介紹。在使用multi interface以前,你首先應該理解如何使用easy interface。
要使用easy interface,首先必須建立一個easy handle,easy handle用於執行每次操做。基本上,每一個線程都應該有本身的easy handle用於數據通訊(若是須要的話)。千萬不要在多線程之間共享同一個easy handle。下面的函數用於獲取一個easy handle :
CURL *easy_handle = curl_easy_init();
在easy handle上能夠設置屬性和操做(action)。easy handle就像一個邏輯鏈接,用於接下來要進行的數據傳輸。
使用curl_easy_setopt函數能夠設 置easy handle的屬性和操做,這些屬性和操做控制libcurl如何與遠程主機進行數據通訊。一旦在easy handle中設置了相應的屬性和操做,它們將一直做用該easy handle。也就是說,重複使用easy hanle向遠程主機發出請求,先前設置的屬性仍然生效。
easy handle的許多屬性使用字符串(以\0結尾的字節數組)來設置。經過curl_easy_setopt函數設置字符串屬性時,libcurl內部會自動拷貝這些字符串,因此在設置完相關屬性以後,字符串能夠直接被釋放掉(若是須要的話)。
easy handle最基本、最經常使用的屬性是URL。你應當經過CURLOPT_URL屬性提供適當的URL:
curl_easy_setopt(easy_handle, CURLOPT_URL, 「http://blog.csdn.net/JGood 「);
假設你要獲取URL所表示的遠程主機上的資源。你須要寫一段程序用來完成數據傳輸,你可能但願直接保存接收到的數據而不是簡單的在輸出窗口中打印它們。因此,你必須首先寫一個回調函數用來保存接收到的數據。回調函數的原型以下:
size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp);
可使用下面的語句來註冊回調函數,回調函數將會在接收到數據的時候被調用:
curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, write_data);
能夠給回調函數提供一個自定義參數,libcurl不處理該參數,只是簡單的傳遞:
curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, &internal_struct);
若是你沒有經過CURLOPT_WRITEFUNCTION屬性給easy handle設置回調函數,libcurl會提供一個默認的回調函數,它只是簡單的將接收到的數據打印到標準輸出。你也能夠經過 CURLOPT_WRITEDATA屬性給默認回調函數傳遞一個已經打開的文件指針,用於將數據輸出到文件裏。
下面是一些平臺相關的注意點。在一些平臺上,libcurl不能直接操做由應用程序打開的文件。因此,若是使用默認的回調函數,同時經過 CURLOPT_WRITEDATA屬性給easy handle傳遞一個文件指針,應用程序可能會執行失敗。若是你但願本身的程序能跑在任何系統上,你必須避免出現這種狀況。
若是以win32動態鏈接庫的形式來使用libcurl,在設置CURLOPT_WRITEDATA屬性時,你必須同時 使用CURLOPT_WRITEFUNCTION來註冊回調函數。不然程序會執行失敗(筆者嘗試只傳遞一個打開的文件指針而不顯式設置回調函數,程序並無崩潰。多是我使用的方式不正確。)。
固然,libcurl還支持許多其餘的屬性,在接下來的篇幅裏,你將會逐步地接觸到它們。調用下面的函數,將執行真正的數據通訊:
success = curl_easy_perform(easy_handle);
curl_easy_perfrom將鏈接到遠程主機,執行必要的命令,並接收數據。當接收到數據時,先前設置的回調函數將被調用。libcurl可能一 次只接收到1字節的數據,也可能接收到好幾K的數據,libcurl會盡量多、及時的將數據傳遞給回調函數。回調函數返回接收的數據長度。若是回調函數 返回的數據長度與傳遞給它的長度不一致(即返回長度 != size * nmemb),libcurl將會終止操做,並返回一個錯誤代碼。
當數據傳遞結束的時候,curl_easy_perform將返回一個代碼表示操做成功或失敗。若是須要獲取更多有關通訊細節的信息,你能夠設置CURLOPT_ERRORBUFFER屬性,讓libcurl緩存許多可讀的錯誤信息。
easy handle在完成一次數據通訊以後能夠被重用。這裏很是建議你重用一個已經存在的easy handle。若是在完成數據傳輸以後,你建立另外一個easy handle來執行其餘的數據通訊,libcurl在內部會嘗試着重用上一次建立的鏈接。
對於有些協議,下載文件可能包括許多複雜的子過程:日誌記錄、設置傳輸模式、選擇當前文件夾,最後下載文件數據。使用libcurl,你不須要關心這一切,你只需簡單地提供一個URL,libcurl會給你作剩餘全部的工做。
下面的這個例子演示瞭如何獲取網頁源碼,將其保存到本地文件,並同時將獲取的源碼輸出到控制檯上。
首先一個基本原則就是:絕對不該該在線程之間共享同一個libcurl handle,無論是easy handle仍是multi handle(將在下文中介紹)。一個線程每次只能使用一個handle。
libcurl是線程安全的,但有兩點例外:信號(signals)和SSL/TLS handler。 信號用於超時失效名字解析(timing out name resolves)。libcurl依賴其餘的庫來支持SSL/STL,因此用多線程的方式訪問HTTPS或FTPS的URL時,應該知足這些庫對多線程 操做的一些要求。詳細能夠參考:
OpenSSL: http://www.openssl.org/docs/crypto/threads.html#DESCRIPTION
GnuTLS: http://www.gnu.org/software/gnutls/manual/html_node/Multi_002dthreaded-applications.html
NSS: 宣稱是多線程安全的。
何時libcurl沒法正常工做傳輸失敗老是有緣由的。你可能錯誤的設置了一些libcurl的屬性或者沒有正確的理解某些屬性的含義,或者是遠程主機返回一些沒法被正確解析的內容。
這裏有一個黃金法則來處理這些問題:將CURLOPT_VERBOSE屬性設置爲1,libcurl會輸出通訊過程當中的一些細節。若是使用的是http協議,請求頭/響應頭也會被輸出。將CURLOPT_HEADER設爲1,這些頭信息將出如今消息的內容中。
固然不能否認的是,libcurl還存在bug。當你在使用libcurl的過程當中發現bug時,但願可以提交給咱們,好讓咱們可以修復這些bug。你在 提交bug時,請同時提供詳細的信息:經過CURLOPT_VERBOSE屬性跟蹤到的協議信息、libcurl版本、libcurl的客戶代碼、操做系 統名稱、版本、編譯器名稱、版本等等。
若是你對相關的協議瞭解越多,在使用libcurl時,就越不容易犯錯。
上傳數據到遠程站點libcurl提供協議無關的方式進行數據傳輸。因此上傳一個文件到FTP服務器,跟向HTTP服務器提交一個PUT請求的操做方式是相似的:
1. 建立easy handle或者重用先前建立的easy handle。
2. 設置CURLOPT_URL屬性。
3. 編寫回調函數。在執行上傳的時候,libcurl經過回調函數讀取要上傳的數據。(若是要從遠程服務器下載數據,能夠經過回調來保存接收到的數據。)回調函數的原型以下:
bufptr指針表示緩衝區,用於保存要上傳的數據,size * nitems是緩衝區數據的長度,userp是一個用戶自定義指針,libcurl不對該指針做任何操做,它只是簡單的傳遞該指針。可使用該指針在應用程序與libcurl之間傳遞信息。
4. 註冊回調函數,設置自定義指針。語法以下:
5. 告訴libcurl,執行的是上傳操做。
有些協議在沒有預先知道上傳文件大小的狀況下,可能沒法正確判斷上傳是否結束,因此最好預先使用CURLOPT_INFILESIZE_LARGE屬性:告訴它要上傳文件的大小:
6. 調用curl_easy_perform。
接下來,libcurl將會完成剩下的全部工做。在上傳文件過程當中,libcurl會不斷調用先前設置的回調函數,用於將要上傳的數據讀入到緩衝區,並執行上傳。
下面的例子演示如何將文件上傳到FTP服務器。筆者使用的是IIS自帶的FTP服務,同時在FTP上設置了可寫權限。
客戶端向服務器發送請求時,許多協議都要求提供用戶名與密碼。libcurl提供了多種方式來設置它們。
一些協議支持在URL中直接指定用戶名和密碼,相似於: protocol://user:password@example.com/path/。libcurl能正確的識別這種URL中的用戶名與密碼並執行 相應的操做。若是你提供的用戶名和密碼中有特殊字符,首先應該對其進行URL編碼。
也能夠經過CURLOPT_USERPWD屬性來設置用戶名與密碼。參數是格式如 「user:password 」的字符串:
(下面這幾段文字我理解地模模糊糊)有時候在訪問代理服務器的時候,可能時時要求提供用戶名和密碼進行用戶身份驗證。這種狀況下,libcurl提供了另外一個屬性CURLOPT_PROXYUSERPWD:
在UNIX平臺下,訪問FTP的用戶名和密碼可能會被保存在$HOME/.netrc文件中。libcurl支持直接從這個文件中獲取用戶名與密碼:
在使用SSL時,可能須要提供一個私鑰用於數據安全傳輸,經過CURLOPT_KEYPASSWD來設置私鑰:
上一章介紹瞭如何在libcurl中,對須要身份驗證的URL設置用戶名與密碼。在使用HTTP協議時,客戶端有不少種方式向服務器提供驗證信息。默認的 HTTP驗證方法是」Basic」,它將用戶名與密碼以明文的方式、經Base64編碼後保存在HTTP請求頭中,發往服務器。固然這不太安全。
當前版本的libcurl支持的驗證方法有:basic, Digest, NTLM, Negotiate, GSS-Negotiate and SPNEGO。(譯者感嘆:搞Web這麼多年,盡然不知道這些Http的驗證方式,實在慚愧。)能夠經過CURLOPT_HTTPAUTH屬性來設置具體 的驗證方式:
向代理服務器發送驗證信息時,能夠經過CURLOPT_PROXYAUTH設置驗證方式:
也能夠同時設置多種驗證方式(經過按位與), 使用‘CURLAUTH_ANY‘將容許libcurl能夠選擇任何它所支持的驗證方式。經過CURLOPT_HTTPAUTH或 CURLOPT_PROXYAUTH屬性設置的多種驗證方式,libcurl會在運行時選擇一種它認爲是最好的方式與服務器通訊:
這一章介紹如何使用libcurl以Post方式向HTTP服務器提交數據。
方法一,也是最簡單的方式,就像html中使用