天天看點

libcurl教程

    本文檔介紹了在應用程式開發過程中,如何正确使用libcurl的基本方式和指導原則。文檔使用C語言來調用libcurl的接口,當然也适用于其他與C語言接近的語言。

    文檔主要針對使用libcurl來進行開發的人員。文檔所掼的應用程式泛指你寫的源代碼,這些代碼使用了libcurl進行資料傳輸。

    更多關于libcurl的功能和接口資訊,可以在相關的首頁上查閱。

    有很多種不同的方式來編譯C語言代碼。這裡使用UNIX平台下的編譯方式。即使你使用的是其他的作業系統,你仍然可以通過閱讀本文檔來擷取許多有用的資訊。

    你的編譯器必須知道libcurl頭檔案的位置。是以在編譯的時候,你要設定頭檔案的包含路徑。可以使用curl-config工具來擷取這方面的資訊:

    $ curl-config –cflags

    編譯完源碼(這時的源代碼不是指libcurl的源代碼,你是你自己寫的程式代碼)之後,你還必須把目标檔案連結成單個可執行檔案。你要連結libcurl庫,以及libcurl所依賴的其他庫,例如OpenSLL庫。當然可能還需要一些其他的作業系統庫。最後你還要設定一些編譯選項,當然可以使用curl-config工具簡化操作:

    $curl-config –libs

    定制編譯libcurl。與其他庫不同的是,libcurl可以定制編譯,根據實際需要是否支援某些特性,如是否支援SSL傳輸,像HTTPS和FTPS。如果決定需要支援SSL,必須在編譯時正确的設定。可以使用’curl-config’來判斷libcurl庫是否支援SSL:

    $ curl-config –feature

    當你編寫配置腳本來檢測libcurl及其相應設定時,你可以使用預定義宏。文檔docs/libcurl/libcurl.m4告訴你如何使用這些宏。

    libcurl的開發人員花費很大的努力,使libcurl盡可能在大多數平台上正常運作。

    應用程式在使用libcurl之前,必須先初始化libcurl。libcurl隻需初始化一次。可以使用以下語句進行初始化:

    curl_global_init()接收一個參數,告訴libcurl如何初始化。參數CURL_GLOBAL_ALL 會使libcurl初始化所有的子子產品和一些預設的選項,通常這是一個比較好的預設參數值。還有兩個可選值:

    隻能應用于Windows平台。它告訴libcurl初始化winsock庫。如果winsock庫沒有正确地初始化,應用程式就不能使用socket。在應用程式中,隻要初始化一次即可。

    如果libcurl在編譯時被設定支援SSL,那麼該參數用于初始化相應的SSL庫。同樣,在應用程式中,隻要初始化一次即可。

    libcurl有預設的保護機制,如果在調用curl_easy_perform時它檢測到還沒有通過curl_global_init進行初始化,libcurl會根據目前的運作時環境,自動調用全局初始化函數。但必須清楚的是,讓系統自已初始化不是一個好的選擇。

    當應用程式不再使用libcurl的時候,應該調用curl_global_cleanup來釋放相關的資源。

    在程式中,應當避免多次調用curl_global_init和curl_global_cleanup。它們隻能被調用一次。

    在運作時根據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時,應該滿足這些庫對多線程操作的一些要求。詳細可以參考:

    NSS: 宣稱是多線程安全的。

    傳輸失敗總是有原因的。你可能錯誤的設定了一些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:[email protected]/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中使用<form>标簽送出資料一樣,隻需向libcurl提供一個包含資料的字元串即可。下面是筆者學習過程中的一個demo程式:

    上面的代碼夠簡單吧~_~ 有時候,我們需要送出一些二進制資料到HTTP伺服器,使用方法一就不行了,因為方法一中實際送出的是一個字元串,字元串遇到/0就表示結束了。是以在上傳二進制資料的時候,必須明确的告訴libcurl要送出的資料的長度。在上傳二進制資料的時候,還應該設定送出的Content-Type頭資訊。下面的示例代碼:

               curl_global_init(CURL_GLOBAL_WIN32); 

               CURL *easy_handle = curl_easy_init();

     最後要說明的是,所有在easy handle上設定的屬性都是”sticky”的,什麼意思?就是說在easy handle上設定的屬性都将被儲存,即使執行完curl_easy_perform之後,這些屬性值仍然存在。通過将CURLOPT_HTTPGET設為1可以使easy handle回到最原始的狀态:

    libcurl支援通信過程中的進度控制。通過将CURLOPT_NOPROCESS設定為0開啟進度支援。該選項預設值為1。對大多數應用程式,我們需要提供一個進度顯示回調。libcurl會不定期的将目前傳輸的進度通過回調函數告訴你的程式。回調函數的原型如下:

    通過CURLOPT_PROGRESSFUNCTION注冊該回調函數。參數clientp是一個使用者自定義指針,應用程式通過CURLOPT_PROCESSDATA屬性将該自定義指定傳遞給libcurl。libcurl對該參數不作任何處理,隻是簡單将其傳遞給回調函數。

    在C++中使用libcurl跟在C語言中沒有任何差別,隻有一個地方要注意:回調函數不能是類的非靜态成員函數。例如:

    什麼是代理?Merrian-Webster的解釋是:一個通過驗證的使用者扮演另一個使用者。今天,代理已經被廣泛的使用。許多公司提供網絡代理伺服器,允許員工的網絡用戶端通路、下載下傳檔案。代理伺服器處理這些使用者的請求。

    libcurl支援SOCKS和HTTP代理。使用代理,libcurl會把使用者輸入的URL送出給代理伺服器,而不是直接根據URL去通路遠端資源。

    目前版本的libcurl并不支援SOCKS代理的所有功能。

    對于HTTP代理來說,即使請求的URL不是一個合法的HTTP URL(比方你提供了一個ftp的url),它仍然會先被送出到HTTP代理。

    CURLOPT_PROXY屬性用于設定libcurl使用的代理伺服器位址:

    可以把主機名與端口号分開設定:

    有些代理伺服器要求使用者通過驗證之後才允許接受其請求,此時應該先提供驗證資訊:

    還要告訴libcurl使用的代理類型(如果沒有提供,libcurl會認為是HTTP代理):

     對于有些協定,libcurl會自動檢測并使用一些環境變量,并根據這些環境變量來确定要使用的代理伺服器。這些環境變量的名稱格式一般是"[protocol]_proxy"(注意小寫)。例如輸入一個HTTP的URL,那麼名稱為"http_proxy"的環境變量就會被檢測是否存在,如果存在,libcurl會使用該環境變量指定的代理。相同的規則也适用于FTP。

    這些環境變量的值的格式必須是這樣的:"[protocol://][user:password@]machine[:port]"。libcurl會忽略掉[protocol://],如果沒有提供端口号,libcurl使用該協定的預設端口。 

    有兩個比較特殊的環境變量:'all_proxy'與'no_proxy'。如果一個URL所對應的協定,它的環境變量沒有設定,那麼'all_proxy'指定的代理将被使用。'no_proxy'則指定了一個不應被使用的代理主機的清單。例如:no_proxy的值是'192.168.1.10',即使存在http_proxy,它的值也是'192.168.1.10','192.168.1.10'也不會被作為代理。no_proxy=”*”表示不允許使用任何代理。

    顯式地将CURLOPT_PROXY屬性設定為空,可以禁止libcurl檢查并使用環境變量來使用代理。

    SSL為點到點通信提供安全保障。它包含一些強壯的加密措施和其他安全檢測,這使得上面講到的代理方式不适用于SSL。除非代理伺服器提供專用通道,對進出該代理伺服器的資料不作任何檢測或禁止。通過HTTP代理伺服器打開SSL連接配接,意味着代理伺服器要直接連接配接到目标主機的指定端口。因為代理伺服器對在專用通道上傳輸的資料的類型毫無所知,是以它往往會使某些機制失效,如緩存機制。許多組織隻允許在443端口上建立這種類型的資料通道。

    正如上面講到的,要使SSL工作必須在代理伺服器建立專用資料通道,通常專用通道隻被限制應用于HTTPS。通過HTTP代理在應用程式與目标之間建立一個專用資料通道,應該預防在該專有通道上執行非HTTP的操作,如進行FTP上傳或執行FTP指令。代理伺服器管理者應該禁止非法的操作。

    通過CURLOPT_HTTPPROXYTUNNEL屬性來告訴libcurl使用代理通道:

     有時候你想通過代理通道執行平常的HTTP操作,而實際上卻可能使你不經過代理伺服器而直接與遠端主機進行互動。libcurl不會代替這種新引入的行為。

    許多浏覽器支援自動配置代理,例如NetScape。libcurl并不支援這些。

    當需要發送多次請求時,應該重複使用easy handle。

    每次執行完curl_easy_perform,licurl會繼續保持與伺服器的連接配接。接下來的請求可以使用這個連接配接而不必建立新的連接配接(如果目标主機是同一個的話)。這樣可以減少網絡開銷。 

    即使連接配接被釋放了,libcurl也會緩存這些連接配接的會話資訊,這樣下次再連接配接到目标主機上時,就可以使用這些資訊,進而減少重新連接配接所需的時間。

    FTP連接配接可能會被儲存較長的時間。因為用戶端要與FTP伺服器進行頻繁的指令互動。對于有通路人數上限的FTP伺服器,保持一個長連接配接,可以使你不需要排除等待,就直接可以與FTP伺服器通信。

    libcurl會緩存DNS的解析結果。

    在今後的libcurl版本中,還會添加一些特性來提高資料通信的效率。 

    每個easy handle都會儲存最近使用的幾個連接配接,以備重用。預設是5個。可以通過CURLOPT_MAXCONNECTS屬性來設定儲存連接配接的數量。

    如果你不想重用連接配接,将CURLOPT_FRESH_CONNECT屬性設定為1。這樣每次送出請求時,libcurl都會先關閉以前建立的連接配接,然後重新建立一個新的連接配接。也可以将CURLOPT_FORBID_REUSE設定為1,這樣每次執行完請求,連接配接就會馬上關閉。

     當使用libcurl發送http請求時,它會自動添加一些http頭。我們可以通過CURLOPT_HTTPHEADER屬性手動替換、添加或删除相應的HTTP消息頭。

    http1.1(大部分http1.0)版本都要求用戶端請求提供這個資訊頭。

    "no-cache"。表示不要緩沖資料。

    "*/*"。表示允許接收任何類型的資料。

    以POST的方式向HTTP伺服器送出請求時,libcurl會設定該消息頭為"100-continue",它要求伺服器在正式處理該請求之前,傳回一個"OK"消息。如果POST的資料很小,libcurl可能不會設定該消息頭。

    目前越來越多的協定都建構在HTTP協定之上(如:soap),這主要歸功于HTTP的可靠性,以及被廣泛使用的代理支援(可以穿透大部分防火牆)。 這些協定的使用方式與傳統HTTP可能有很大的不同。對此,libcurl作了很好的支援。

    HTTP支援GET, HEAD或者POST送出請求。可以設定CURLOPT_CUSTOMREQUEST來設定自定義的請求方式,libcurl預設以GET方式送出請求:

    HTTP協定提供了消息頭,請求消息頭用于告訴伺服器如何處理請求;響應消息頭則告訴浏覽器如何處理接收到的資料。在libcurl中,你可以自由的添加這些消息頭:

    對于已經存在的消息頭,可以重新設定它的值:

    對于一個已經存在的消息頭,設定它的内容為空,libcurl在發送請求時就不會同時送出該消息頭:

    (這段文字了解可能有誤碼)以非GET的方式送出HTTP請求時,如果設定了自定義的消息頭”Transfer-Encoding:chunked”,libcurl會分塊送出資料,即使要上傳的資料量已經知道。在上傳資料大小未知的情況下,libcurl自動采用分塊上傳資料。(譯者注:非GET方式送出請求,送出的資料量往往比較大。)

    每一次http請求,都包含一個表示目前使用http版本的消息頭。libcurl預設使用HTTP 1.1。可以通過CURLOPT_HTTP_VERSION屬性來設定具體的版本号:

    并不是是以的協定都像HTTP那樣,通過消息頭來告訴伺服器如何處理請求。對于FTP,你就要使用另外的方式來處理。

    發送自定義的指令到ftp伺服器,意味着你發送的指令必須是能被ftp伺服器了解的指令(FTP協定中定義的指令,參考rfc959)。

    下面是一個簡單的例子,在檔案傳輸操作操作之前删除指定檔案:

    FTP伺服器執行指令的順序,同這些指令被添加到清單中順序是一緻的。發往伺服器的指令清單中,隻要有一個指令執行失敗,ftp伺服器就會傳回一個錯誤代碼,此時libcurl将直接傳回CURLE_QUOTE_ERROR,不再執行剩餘的FTP指令。

    将CURLOPT_HEADER設定為1,libcurl擷取目标檔案的資訊,并以HTTP消息頭的樣式來輸出消息頭。

    使用CURLOPT_CUSTOMREQUEST屬性,可以向FTP伺服器發送指令。"NLST"是ftp預設的列出檔案清單的指令。 下面的代碼用于列出FTP伺服器上的檔案清單:

     cookie是一個鍵值對的集合,HTTP伺服器發給用戶端的cookie,用戶端送出請求的時候,也會将cookie發送到伺服器。伺服器可以根據cookie來跟蹤使用者的會話資訊。cookie有過期時間,逾時後cookie就會失效。cookie有域名和路徑限制,cookie隻能發給指定域名和路徑的HTTP伺服器。

    cookie以消息頭”Set-Cookie”的形式從HTTP伺服器發送到用戶端;用戶端發以消息頭”Cookie”的形式将Cookie送出到HTTP伺服器。為了對這些東西有個直覺的概念,下圖是FireFox中,使用Firebug跟蹤到的cookie消息頭: 

    在libcurl中,可以通過CURLOPT_COOKIE屬性來設定發往伺服器的cookie:

    下面的例子示範了如何使用libcurl發送cookie資訊給HTTP伺服器,代碼非常的簡單:

     在實在的應用場景中,你可能需要儲存伺服器發送給你的cookie,并在接下來的請求中,把這些cookie一并發往伺服器。是以,可以把上次從伺服器收到的所有響應頭資訊儲存到文本檔案中,當下次需要向伺服器發送請求時,通過CURLOPT_COOKIEFILE屬性告訴libcurl從該檔案中讀取cookie資訊。 

    設定CURLOPT_COOKIEFILE屬性意味着激活libcurl的cookie parser。在cookie parser被激活之前,libcurl忽略是以之前接收到的cookie資訊。cookie parser被激活之後,cookie資訊将被儲存記憶體中,在接下來的請求中,libcurl會自動将這些cookie資訊添加到消息頭裡,你的應用程式不需要做任何事件。大多數情況下,這已經足夠了。需要注意的是,通過CURLOPT_COOKIEFILE屬性來激活cookie parser,給CURLOPT_COOKIEFILE屬性設定的一個儲存cookie資訊的文本檔案路徑,可能并不需要在磁盤上實體存在。 

    如果你需要使用NetScape或者FireFox浏覽器的cookie檔案,你隻要用這些浏覽器的cookie檔案的路徑來初始化CURLOPT_COOKIEFILE屬性,libcurl會自動分析cookie檔案,并在接下來的請求過程中使用這些cookie資訊。 

    libcurl甚至能夠把接收到的cookie資訊儲存成能被Netscape/Mozilla的浏覽器所識别的cookie檔案。通過把這些稱為cookie-jar。通過設定CURLOPT_COOKIEJAR選項,在調用curl_easy_cleanup釋放easy handle的時候,所有的這些cookie資訊都會儲存到cookie-jar檔案中。這就使得cookie資訊能在不同的easy handle甚至在浏覽器之間實作共享。

    (這段文字我了解的很模糊,請讀者參考原文)有些協定提供獨立于正常資料的 消息頭、meta-data。正常的資料流裡通常不包括 資訊頭和中繼資料。可以将CURLOPT_HEADER設定為1,使資訊頭、中繼資料也能出現在資料流中。libcurl的強大之處在于,它能夠從資料流中解析出消息頭,….

[ curl_easy_getinfo ]

    請參考原文,此處略。

     上面介紹的easy interface以同步的方式進行資料傳輸,curl_easy_perform會一直阻塞到資料傳輸完畢後傳回,且一次操作隻能發送一次請求,如果要同時發送多個請求,必須使用多線程。 

    而multi interface以一種簡單的、非阻塞的方式進行傳輸,它允許在一個線程中,同時送出多個相同類型的請求。 在使用multi interface之前,你應該掌握easy interface的基本使用。因為multi interface是建立在easy interface基礎之上的,它隻是簡單的将多個easy handler添加到一個multi stack,而後同時傳輸而已。 

    使用multi interface很簡單,首先使用curl_multi_init()函數建立一個multi handler,然後使用curl_easy_init()建立一個或多個easy handler,并按照上面幾章介紹的接口正常的設定相關的屬性,然後通過curl_multi_add_handler将這些easy handler添加到multi handler,最後調用curl_multi_perform進行資料傳輸。 

    curl_multi_perform是異步的、非阻塞的函數。如果它傳回CURLM_CALL_MULTI_PERFORM,表示資料通信正在進行。

    通過select()來操作multi interface将會使工作變得簡單(譯者注:其實每個easy handler在低層就是一個socket,通過select()來管理這些socket,在有資料可讀/可寫/異常的時候,通知應用程式)。在調用select()函數之前,應該使用curl_multi_fdset來初始化fd_set變量。

     select()函數傳回時,說明受管理的低層socket可以操作相應的操作(接收資料或發送資料,或者連接配接已經斷開),此時應該馬上調用curl_multi_perform,libcurl将會執行相應操作。使用select()時,應該設定一個較短的逾時時間。在調用select()之前,造成不要忘記通過curl_multi_fdset來初始化fd_set,因為每次操作,fd_set中的檔案描述符可能都不一樣。

    如果你想中止multi stack中某一個easy handle的資料通信,可以調用curl_multi_remove_handle函數将其從multi stack中取出。千萬另忘記釋放掉easy handle(通過curl_easy_cleanup()函數)。

    當multi stack中的一個eash handle完成資料傳輸的時候,同時運作的傳輸任務數量就會減少一個。當數量降到0的時候,說明所有的資料傳輸已經完成。

    curl_multi_info_read用于擷取目前已經完成的傳輸任務資訊,它傳回每一個easy handle的CURLcode狀态碼。可以根據這個狀态碼來判斷每個easy handle傳輸是否成功。

    下面的例子,示範了如何使用multi interface進行網頁抓取:

[ seeding, passwords, keys, certificates, ENGINE, ca certs ]

[fill in]

繼續閱讀