青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

牽著老婆滿街逛

嚴(yán)以律己,寬以待人. 三思而后行.
GMail/GTalk: yanglinbo#google.com;
MSN/Email: tx7do#yahoo.com.cn;
QQ: 3 0 3 3 9 6 9 2 0 .

libcurl教程

轉(zhuǎn)載自:http://jishi.cntv.cn/xingyuaidejueze/classpage/video/20120212/100600.shtml

    原文地址:http://curl.haxx.se/libcurl/c/libcurl-tutorial.html

    譯者:JGood(http://blog.csdn.net/JGood )

    譯者注:這是一篇介紹如何使用libcurl的入門教程。文檔不是逐字逐句按原文翻譯,而是根據(jù)筆者對(duì)libcurl的理解,參考原文寫成。文中用到的一些例子,可能不是出自原文,而是筆者在學(xué)習(xí)過程中,寫的一些示例程序(筆者使用的libcurl版本是:7.19.6)。出現(xiàn)在這里主要是為了更好的說明libcurl的某些api函數(shù)的使用。許多例子都參考libcurl提供的example代碼。原文example中的提供的示例程序完全使用C語言,而這里筆者提供的例子使用C++語言。因?yàn)槟芰τ邢蓿瑢?duì)于libcurl的某些理解和使用可能有誤,歡迎批評(píng)指正。

 

目標(biāo)

    本文檔介紹了在應(yīng)用程序開發(fā)過程中,如何正確使用libcurl的基本方式和指導(dǎo)原則。文檔使用C語言來調(diào)用libcurl的接口,當(dāng)然也適用于其他與C語言接近的語言。

    文檔主要針對(duì)使用libcurl來進(jìn)行開發(fā)的人員。文檔所摜的應(yīng)用程序泛指你寫的源代碼,這些代碼使用了libcurl進(jìn)行數(shù)據(jù)傳輸。

    更多關(guān)于libcurl的功能和接口信息,可以在相關(guān)的主頁上查閱。

編譯源碼

    有很多種不同的方式來編譯C語言代碼。這里使用UNIX平臺(tái)下的編譯方式。即使你使用的是其他的操作系統(tǒng),你仍然可以通過閱讀本文檔來獲取許多有用的信息。

編譯

    你的編譯器必須知道libcurl頭文件的位置。所以在編譯的時(shí)候,你要設(shè)置頭文件的包含路徑。可以使用curl-config工具來獲取這方面的信息:

    $ curl-config –cflags

鏈接

    編譯完源碼(這時(shí)的源代碼不是指libcurl的源代碼,你是你自己寫的程序代碼)之后,你還必須把目標(biāo)文件鏈接成單個(gè)可執(zhí)行文件。你要鏈接libcurl庫,以及l(fā)ibcurl所依賴的其他庫,例如OpenSLL庫。當(dāng)然可能還需要一些其他的操作系統(tǒng)庫。最后你還要設(shè)置一些編譯選項(xiàng),當(dāng)然可以使用curl-config工具簡(jiǎn)化操作:

    $curl-config –libs

是否使用SSL

    定制編譯libcurl。與其他庫不同的是,libcurl可以定制編譯,根據(jù)實(shí)際需要是否支持某些特性,如是否支持SSL傳輸,像HTTPS和FTPS。如果決定需要支持SSL,必須在編譯時(shí)正確的設(shè)置。可以使用’curl-config’來判斷l(xiāng)ibcurl庫是否支持SSL:

    $ curl-config –feature

autoconf宏

    當(dāng)你編寫配置腳本來檢測(cè)libcurl及其相應(yīng)設(shè)置時(shí),你可以使用預(yù)定義宏。文檔docs/libcurl/libcurl.m4告訴你如何使用這些宏。

跨平臺(tái)的可移植的代碼

    libcurl的開發(fā)人員花費(fèi)很大的努力,使libcurl盡可能在大多數(shù)平臺(tái)上正常運(yùn)行。

全局初始化

    應(yīng)用程序在使用libcurl之前,必須先初始化libcurl。libcurl只需初始化一次。可以使用以下語句進(jìn)行初始化:

curl_global_init();

    curl_global_init()接收一個(gè)參數(shù),告訴libcurl如何初始化。參數(shù)CURL_GLOBAL_ALL 會(huì)使libcurl初始化所有的子模塊和一些默認(rèn)的選項(xiàng),通常這是一個(gè)比較好的默認(rèn)參數(shù)值。還有兩個(gè)可選值:

CURL_GLOBAL_WIN32

    只能應(yīng)用于Windows平臺(tái)。它告訴libcurl初始化winsock庫。如果winsock庫沒有正確地初始化,應(yīng)用程序就不能使用socket。在應(yīng)用程序中,只要初始化一次即可。

CURL_GLOBAL_SSL

    如果libcurl在編譯時(shí)被設(shè)定支持SSL,那么該參數(shù)用于初始化相應(yīng)的SSL庫。同樣,在應(yīng)用程序中,只要初始化一次即可。

    libcurl有默認(rèn)的保護(hù)機(jī)制,如果在調(diào)用curl_easy_perform時(shí)它檢測(cè)到還沒有通過curl_global_init進(jìn)行初始化,libcurl會(huì)根據(jù)當(dāng)前的運(yùn)行時(shí)環(huán)境,自動(dòng)調(diào)用全局初始化函數(shù)。但必須清楚的是,讓系統(tǒng)自已初始化不是一個(gè)好的選擇。

    當(dāng)應(yīng)用程序不再使用libcurl的時(shí)候,應(yīng)該調(diào)用curl_global_cleanup來釋放相關(guān)的資源。

    在程序中,應(yīng)當(dāng)避免多次調(diào)用curl_global_init和curl_global_cleanup。它們只能被調(diào)用一次。

libcurl提供的功能

    在運(yùn)行時(shí)根據(jù)libcurl支持的特性來進(jìn)行開發(fā),通常比編譯時(shí)更好。可以通過調(diào)用curl_version_info函數(shù)返回的結(jié)構(gòu)體來獲取運(yùn)行時(shí)的具體信息,從而確定當(dāng)前環(huán)境下libcurl支持的一些特性。下面是筆者在visual studio2008中調(diào)用相關(guān)函數(shù)獲取libcurl版本信息的截圖: 
pic1  

使用easy interface

    首先介紹libcurl中被稱為easy interface的api函數(shù),所有這些函數(shù)都是有相同的前綴:curl_easy 。

    當(dāng)前版本的libcurl也提供了multi interface,關(guān)于這些接口的詳細(xì)使用,在下面的章節(jié)中會(huì)有介紹。在使用multi interface之前,你首先應(yīng)該理解如何使用easy interface。

    要使用easy interface,首先必須創(chuàng)建一個(gè)easy handle,easy handle用于執(zhí)行每次操作。基本上,每個(gè)線程都應(yīng)該有自己的easy handle用于數(shù)據(jù)通信(如果需要的話)。千萬不要在多線程之間共享同一個(gè)easy handle。下面的函數(shù)用于獲取一個(gè)easy handle :

CURL *easy_handle = curl_easy_init();

    在easy handle上可以設(shè)置屬性和操作(action)。easy handle就像一個(gè)邏輯連接,用于接下來要進(jìn)行的數(shù)據(jù)傳輸。

    使用curl_easy_setopt函數(shù)可以設(shè)置easy handle的屬性和操作,這些屬性和操作控制libcurl如何與遠(yuǎn)程主機(jī)進(jìn)行數(shù)據(jù)通信。一旦在easy handle中設(shè)置了相應(yīng)的屬性和操作,它們將一直作用該easy handle。也就是說,重復(fù)使用easy hanle向遠(yuǎn)程主機(jī)發(fā)出請(qǐng)求,先前設(shè)置的屬性仍然生效。

    easy handle的許多屬性使用字符串(以/0結(jié)尾的字節(jié)數(shù)組)來設(shè)置。通過curl_easy_setopt函數(shù)設(shè)置字符串屬性時(shí),libcurl內(nèi)部會(huì)自動(dòng)拷貝這些字符串,所以在設(shè)置完相關(guān)屬性之后,字符串可以直接被釋放掉(如果需要的話)。

    easy handle最基本、最常用的屬性是URL。你應(yīng)當(dāng)通過CURLOPT_URL屬性提供適當(dāng)?shù)腢RL:

curl_easy_setopt(easy_handle, CURLOPT_URL, "http://blog.csdn.net/JGood ");

    假設(shè)你要獲取URL所表示的遠(yuǎn)程主機(jī)上的資源。你需要寫一段程序用來完成數(shù)據(jù)傳輸,你可能希望直接保存接收到的數(shù)據(jù)而不是簡(jiǎn)單的在輸出窗口中打印它們。所以,你必須首先寫一個(gè)回調(diào)函數(shù)用來保存接收到的數(shù)據(jù)。回調(diào)函數(shù)的原型如下:

size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp);

    可以使用下面的語句來注冊(cè)回調(diào)函數(shù),回調(diào)函數(shù)將會(huì)在接收到數(shù)據(jù)的時(shí)候被調(diào)用:

curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, write_data);

    可以給回調(diào)函數(shù)提供一個(gè)自定義參數(shù),libcurl不處理該參數(shù),只是簡(jiǎn)單的傳遞:

curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, &internal_struct);

    如果你沒有通過CURLOPT_WRITEFUNCTION屬性給easy handle設(shè)置回調(diào)函數(shù),libcurl會(huì)提供一個(gè)默認(rèn)的回調(diào)函數(shù),它只是簡(jiǎn)單的將接收到的數(shù)據(jù)打印到標(biāo)準(zhǔn)輸出。你也可以通過CURLOPT_WRITEDATA屬性給默認(rèn)回調(diào)函數(shù)傳遞一個(gè)已經(jīng)打開的文件指針,用于將數(shù)據(jù)輸出到文件里。

    下面是一些平臺(tái)相關(guān)的注意點(diǎn)。在一些平臺(tái)上,libcurl不能直接操作由應(yīng)用程序打開的文件。所以,如果使用默認(rèn)的回調(diào)函數(shù),同時(shí)通過CURLOPT_WRITEDATA屬性給easy handle傳遞一個(gè)文件指針,應(yīng)用程序可能會(huì)執(zhí)行失敗。如果你希望自己的程序能跑在任何系統(tǒng)上,你必須避免出現(xiàn)這種情況。

    如果以win32動(dòng)態(tài)連接庫的形式來使用libcurl,在設(shè)置CURLOPT_WRITEDATA屬性時(shí),你必須同時(shí) 使用CURLOPT_WRITEFUNCTION來注冊(cè)回調(diào)函數(shù)。否則程序會(huì)執(zhí)行失敗(筆者嘗試只傳遞一個(gè)打開的文件指針而不顯式設(shè)置回調(diào)函數(shù),程序并沒有崩潰。可能是我使用的方式不正確。)。

    當(dāng)然,libcurl還支持許多其他的屬性,在接下來的篇幅里,你將會(huì)逐步地接觸到它們。調(diào)用下面的函數(shù),將執(zhí)行真正的數(shù)據(jù)通信:

success = curl_easy_perform(easy_handle);

    curl_easy_perfrom將連接到遠(yuǎn)程主機(jī),執(zhí)行必要的命令,并接收數(shù)據(jù)。當(dāng)接收到數(shù)據(jù)時(shí),先前設(shè)置的回調(diào)函數(shù)將被調(diào)用。libcurl可能一次只接收到1字節(jié)的數(shù)據(jù),也可能接收到好幾K的數(shù)據(jù),libcurl會(huì)盡可能多、及時(shí)的將數(shù)據(jù)傳遞給回調(diào)函數(shù)。回調(diào)函數(shù)返回接收的數(shù)據(jù)長(zhǎng)度。如果回調(diào)函數(shù)返回的數(shù)據(jù)長(zhǎng)度與傳遞給它的長(zhǎng)度不一致(即返回長(zhǎng)度 != size * nmemb),libcurl將會(huì)終止操作,并返回一個(gè)錯(cuò)誤代碼。

    當(dāng)數(shù)據(jù)傳遞結(jié)束的時(shí)候,curl_easy_perform將返回一個(gè)代碼表示操作成功或失敗。如果需要獲取更多有關(guān)通信細(xì)節(jié)的信息,你可以設(shè)置CURLOPT_ERRORBUFFER屬性,讓libcurl緩存許多可讀的錯(cuò)誤信息。

    easy handle在完成一次數(shù)據(jù)通信之后可以被重用。這里非常建議你重用一個(gè)已經(jīng)存在的easy handle。如果在完成數(shù)據(jù)傳輸之后,你創(chuàng)建另一個(gè)easy handle來執(zhí)行其他的數(shù)據(jù)通信,libcurl在內(nèi)部會(huì)嘗試著重用上一次創(chuàng)建的連接。

    對(duì)于有些協(xié)議,下載文件可能包括許多復(fù)雜的子過程:日志記錄、設(shè)置傳輸模式、選擇當(dāng)前文件夾,最后下載文件數(shù)據(jù)。使用libcurl,你不需要關(guān)心這一切,你只需簡(jiǎn)單地提供一個(gè)URL,libcurl會(huì)給你做剩余所有的工作。

    下面的這個(gè)例子演示了如何獲取網(wǎng)頁源碼,將其保存到本地文件,并同時(shí)將獲取的源碼輸出到控制臺(tái)上。

/** *	@brief libcurl接收到數(shù)據(jù)時(shí)的回調(diào)函數(shù) * *	將接收到的數(shù)據(jù)保存到本地文件中,同時(shí)顯示在控制臺(tái)上。 *
* @param [in] buffer 接收到的數(shù)據(jù)所在緩沖區(qū)
* @param [in] size 數(shù)據(jù)長(zhǎng)度
* @param [in] nmemb 數(shù)據(jù)片數(shù)量
* @param [in/out] 用戶自定義指針
* @return 獲取的數(shù)據(jù)長(zhǎng)度
*/
 
size_t process_data(void *buffer, size_t size, size_t nmemb, void *user_p) { FILE *fp = (FILE *)user_p; size_t return_size = fwrite(buffer, size, nmemb, fp); cout << (char *)buffer << endl;
return return_size; }

int main(int argc, char **argv) { // 初始化libcurl CURLcode return_code; return_code = curl_global_init(CURL_GLOBAL_WIN32); if (CURLE_OK != return_code) { cerr << "init libcurl failed." << endl; return -1; }

// 獲取easy handle CURL *easy_handle = curl_easy_init();
if (NULL == easy_handle)
{
cerr << "get a easy handle failed." << endl;
curl_global_cleanup();
return -1;
}
FILE *fp = fopen("data.html", "ab+"); // // 設(shè)置easy handle屬性 curl_easy_setopt(easy_handle, CURLOPT_URL, http://blog.csdn.net/JGood);
curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, &process_data);
curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, fp);

// 執(zhí)行數(shù)據(jù)請(qǐng)求 curl_easy_perform(easy_handle);
// 釋放資源 
fclose(fp);
curl_easy_cleanup(easy_handle);
curl_global_cleanup();

return 0;
}

 

多線程問題

    首先一個(gè)基本原則就是:絕對(duì)不應(yīng)該在線程之間共享同一個(gè)libcurl handle,不管是easy handle還是multi handle(將在下文中介紹)。一個(gè)線程每次只能使用一個(gè)handle。

    libcurl是線程安全的,但有兩點(diǎn)例外:信號(hào)(signals)和SSL/TLS handler。 信號(hào)用于超時(shí)失效名字解析(timing out name resolves)。libcurl依賴其他的庫來支持SSL/STL,所以用多線程的方式訪問HTTPS或FTPS的URL時(shí),應(yīng)該滿足這些庫對(duì)多線程操作的一些要求。詳細(xì)可以參考:

    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: 宣稱是多線程安全的。

什么時(shí)候libcurl無法正常工作

    傳輸失敗總是有原因的。你可能錯(cuò)誤的設(shè)置了一些libcurl的屬性或者沒有正確的理解某些屬性的含義,或者是遠(yuǎn)程主機(jī)返回一些無法被正確解析的內(nèi)容。

    這里有一個(gè)黃金法則來處理這些問題:將CURLOPT_VERBOSE屬性設(shè)置為1,libcurl會(huì)輸出通信過程中的一些細(xì)節(jié)。如果使用的是http協(xié)議,請(qǐng)求頭/響應(yīng)頭也會(huì)被輸出。將CURLOPT_HEADER設(shè)為1,這些頭信息將出現(xiàn)在消息的內(nèi)容中。

    當(dāng)然不可否認(rèn)的是,libcurl還存在bug。當(dāng)你在使用libcurl的過程中發(fā)現(xiàn)bug時(shí),希望能夠提交給我們,好讓我們能夠修復(fù)這些bug。你在提交bug時(shí),請(qǐng)同時(shí)提供詳細(xì)的信息:通過CURLOPT_VERBOSE屬性跟蹤到的協(xié)議信息、libcurl版本、libcurl的客戶代碼、操作系統(tǒng)名稱、版本、編譯器名稱、版本等等。

    如果你對(duì)相關(guān)的協(xié)議了解越多,在使用libcurl時(shí),就越不容易犯錯(cuò)。

上傳數(shù)據(jù)到遠(yuǎn)程站點(diǎn)

    libcurl提供協(xié)議無關(guān)的方式進(jìn)行數(shù)據(jù)傳輸。所以上傳一個(gè)文件到FTP服務(wù)器,跟向HTTP服務(wù)器提交一個(gè)PUT請(qǐng)求的操作方式是類似的:

1. 創(chuàng)建easy handle或者重用先前創(chuàng)建的easy handle。

2. 設(shè)置CURLOPT_URL屬性。

3. 編寫回調(diào)函數(shù)。在執(zhí)行上傳的時(shí)候,libcurl通過回調(diào)函數(shù)讀取要上傳的數(shù)據(jù)。(如果要從遠(yuǎn)程服務(wù)器下載數(shù)據(jù),可以通過回調(diào)來保存接收到的數(shù)據(jù)。)回調(diào)函數(shù)的原型如下:

size_t function(char *bufptr, size_t size, size_t nitems, void *userp);

    bufptr指針表示緩沖區(qū),用于保存要上傳的數(shù)據(jù),size * nitems是緩沖區(qū)數(shù)據(jù)的長(zhǎng)度,userp是一個(gè)用戶自定義指針,libcurl不對(duì)該指針作任何操作,它只是簡(jiǎn)單的傳遞該指針。可以使用該指針在應(yīng)用程序與libcurl之間傳遞信息。

4. 注冊(cè)回調(diào)函數(shù),設(shè)置自定義指針。語法如下:

// 注冊(cè)回調(diào)函數(shù) curl_easy_setopt(easy_handle, CURLOPT_READFUNCTION, read_function); // 設(shè)置自定義指針 curl_easy_setopt(easy_handle, CURLOPT_READDATA, &filedata);

5. 告訴libcurl,執(zhí)行的是上傳操作。

curl_easy_setopt(easy_handle, CURLOPT_UPLOAD, 1L);

    有些協(xié)議在沒有預(yù)先知道上傳文件大小的情況下,可能無法正確判斷上傳是否結(jié)束,所以最好預(yù)先使用CURLOPT_INFILESIZE_LARGE屬性:告訴它要上傳文件的大小:

/* in this example, file_size must be an curl_off_t variable */ curl_easy_setopt(easy_handle, CURLOPT_INFILESIZE_LARGE, file_size);

6. 調(diào)用curl_easy_perform。

    接下來,libcurl將會(huì)完成剩下的所有工作。在上傳文件過程中,libcurl會(huì)不斷調(diào)用先前設(shè)置的回調(diào)函數(shù),用于將要上傳的數(shù)據(jù)讀入到緩沖區(qū),并執(zhí)行上傳。

    下面的例子演示如何將文件上傳到FTP服務(wù)器。筆者使用的是IIS自帶的FTP服務(wù),同時(shí)在FTP上設(shè)置了可寫權(quán)限。

/**
* @brief 讀取數(shù)據(jù)的回調(diào)。
*/
 size_t read_data(void *buffer, size_t size, size_t nmemb, void *user_p) { returnfread(buffer, size, nmemb, (FILE *)user_p); } 
int main(int argc, char **argv) { // 初始化libcurl CURLcode code;
code = curl_global_init(CURL_GLOBAL_WIN32); if (code != CURLE_OK) { cerr << "init libcurl failed." << endl; return -1; } 
FILE *fp = fopen("a.html", "rb"); if (NULL == fp) {
cout << "can't open file." << endl; curl_global_cleanup(); return -1; }

// 獲取文件大小 fseek(fp, 0, 2);
int file_size = ftell(fp); rewind(fp); 
// 獲取easy handle 
CURL *easy_handle = NULL; easy_handle = curl_easy_init(); if (NULL == easy_handle) { cerr << "get a easy handle failed." << endl; fclose(fp); curl_global_cleanup(); return -1;
}

// 設(shè)置eash handle屬性 curl_easy_setopt(easy_handle, CURLOPT_URL,ftp://127.0.0.1/upload.html); curl_easy_setopt(easy_handle, CURLOPT_UPLOAD, 1L); curl_easy_setopt(easy_handle, CURLOPT_READFUNCTION, &read_data); curl_easy_setopt(easy_handle, CURLOPT_READDATA, fp); curl_easy_setopt(easy_handle, CURLOPT_INFILESIZE_LARGE, file_size); 
// 執(zhí)行上傳操作 code = curl_easy_perform(easy_handle); if (code == CURLE_OK) { cout << "upload successfully." << endl; }

// 釋放資源 fclose(fp);
curl_easy_cleanup(easy_handle);
curl_global_cleanup();

return 0; }

 

關(guān)于密碼

    客戶端向服務(wù)器發(fā)送請(qǐng)求時(shí),許多協(xié)議都要求提供用戶名與密碼。libcurl提供了多種方式來設(shè)置它們。

    一些協(xié)議支持在URL中直接指定用戶名和密碼,類似于: protocol://user:password@example.com/path/。libcurl能正確的識(shí)別這種URL中的用戶名與密碼并執(zhí)行相應(yīng)的操作。如果你提供的用戶名和密碼中有特殊字符,首先應(yīng)該對(duì)其進(jìn)行URL編碼。

    也可以通過CURLOPT_USERPWD屬性來設(shè)置用戶名與密碼。參數(shù)是格式如 “user:password ”的字符串:

curl_easy_setopt(easy_handle, CURLOPT_USERPWD, "user_name:password");

    (下面這幾段文字我理解地模模糊糊)有時(shí)候在訪問代理服務(wù)器的時(shí)候,可能時(shí)時(shí)要求提供用戶名和密碼進(jìn)行用戶身份驗(yàn)證。這種情況下,libcurl提供了另一個(gè)屬性CURLOPT_PROXYUSERPWD:

curl_easy_setopt(easy_handle, CURLOPT_PROXYUSERPWD, "user_name:password");

    在UNIX平臺(tái)下,訪問FTP的用戶名和密碼可能會(huì)被保存在$HOME/.netrc文件中。libcurl支持直接從這個(gè)文件中獲取用戶名與密碼:

curl_easy_setopt(easy_handle, CURLOPT_NETRC, 1L);

    在使用SSL時(shí),可能需要提供一個(gè)私鑰用于數(shù)據(jù)安全傳輸,通過CURLOPT_KEYPASSWD來設(shè)置私鑰:

curl_easy_setopt(easy_handle, CURLOPT_KEYPASSWD, "keypassword");

 

HTTP驗(yàn)證

    上一章介紹了如何在libcurl中,對(duì)需要身份驗(yàn)證的URL設(shè)置用戶名與密碼。在使用HTTP協(xié)議時(shí),客戶端有很多種方式向服務(wù)器提供驗(yàn)證信息。默認(rèn)的HTTP驗(yàn)證方法是"Basic”,它將用戶名與密碼以明文的方式、經(jīng)Base64編碼后保存在HTTP請(qǐng)求頭中,發(fā)往服務(wù)器。當(dāng)然這不太安全。

    當(dāng)前版本的libcurl支持的驗(yàn)證方法有:basic, Digest, NTLM, Negotiate, GSS-Negotiate and SPNEGO。(譯者感嘆:搞Web這么多年,盡然不知道這些Http的驗(yàn)證方式,實(shí)在慚愧。)可以通過CURLOPT_HTTPAUTH屬性來設(shè)置具體的驗(yàn)證方式:

curl_easy_setopt(easy_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);

    向代理服務(wù)器發(fā)送驗(yàn)證信息時(shí),可以通過CURLOPT_PROXYAUTH設(shè)置驗(yàn)證方式:

curl_easy_setopt(easy_handle, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);

    也可以同時(shí)設(shè)置多種驗(yàn)證方式(通過按位與), 使用‘CURLAUTH_ANY‘將允許libcurl可以選擇任何它所支持的驗(yàn)證方式。通過CURLOPT_HTTPAUTH或CURLOPT_PROXYAUTH屬性設(shè)置的多種驗(yàn)證方式,libcurl會(huì)在運(yùn)行時(shí)選擇一種它認(rèn)為是最好的方式與服務(wù)器通信:

curl_easy_setopt(easy_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST|CURLAUTH_BASIC); 
// curl_easy_setopt(easy_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);

 

HTTP Post

    這一章介紹如何使用libcurl以Post方式向HTTP服務(wù)器提交數(shù)據(jù)。

    方法一,也是最簡(jiǎn)單的方式,就像html中使用<form>標(biāo)簽提交數(shù)據(jù)一樣,只需向libcurl提供一個(gè)包含數(shù)據(jù)的字符串即可。下面是筆者學(xué)習(xí)過程中的一個(gè)demo程序:

int main(int argc, char **argv) { code = curl_global_init(CURL_GLOBAL_WIN32); CURL *easy_handle = curl_easy_init(); 
curl_easy_setopt(easy_handle, CURLOPT_URL, http://localhost:2210/Default.aspx); // 單個(gè)域post curl_easy_setopt(easy_handle, CURLOPT_POSTFIELDS, "name=jgood&address=hangzhou");
code = curl_easy_perform(easy_handle);
curl_easy_cleanup(easy_handle);
curl_global_cleanup();

return 0; }

    在asp.net Web服務(wù)器上跟蹤調(diào)試,得到客戶程序提交上來的數(shù)據(jù),下面是截圖:pic1

    上面的代碼夠簡(jiǎn)單吧~_~ 有時(shí)候,我們需要提交一些二進(jìn)制數(shù)據(jù)到HTTP服務(wù)器,使用方法一就不行了,因?yàn)榉椒ㄒ恢袑?shí)際提交的是一個(gè)字符串,字符串遇到/0就表示結(jié)束了。所以在上傳二進(jìn)制數(shù)據(jù)的時(shí)候,必須明確的告訴libcurl要提交的數(shù)據(jù)的長(zhǎng)度。在上傳二進(jìn)制數(shù)據(jù)的時(shí)候,還應(yīng)該設(shè)置提交的Content-Type頭信息。下面的示例代碼:

int main(int argc, char **argv) { curl_global_init(CURL_GLOBAL_WIN32);
CURL *easy_handle = curl_easy_init();

// 上傳二進(jìn)制數(shù)據(jù) char data[] = { 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0 }; curl_slist *http_headers = NULL; http_headers = curl_slist_append(http_headers, "Content-Type: text/xml");
curl_easy_setopt(easy_handle, CURLOPT_HTTPHEADER, http_headers);
curl_easy_setopt(easy_handle, CURLOPT_URL, http://localhost:2210/Default.aspx); curl_easy_setopt(easy_handle, CURLOPT_POSTFIELDS, data); curl_easy_setopt(easy_handle, CURLOPT_POSTFIELDSIZE, sizeof(data)); 
curl_easy_perform(easy_handle); 
curl_slist_free_all(http_headers); curl_easy_cleanup(easy_handle); curl_global_cleanup();

return 0; }

    在asp.net Web服務(wù)器上跟蹤調(diào)試,得到客戶程序提交上來的二進(jìn)制數(shù)據(jù),下面是截圖:pic1

     上面介紹的兩種方式,可以完成大部分的HTTP POST操作。但上面的兩種方式都不支持multi-part formposts。Multi-part formposts被認(rèn)為是提交二進(jìn)制數(shù)據(jù)(或大量數(shù)據(jù))的更好方法,可以在RFC1867, RFC2388中找到他們的定義。何為Multi-part?其實(shí),就我理解,就是在Post提交的時(shí)候,有不同的數(shù)據(jù)單元,每個(gè)單元有自己的名稱與內(nèi)容,內(nèi)容可以是文本的,也可以是二進(jìn)制的。同時(shí),每個(gè)數(shù)據(jù)單元都可以有自己的消息頭,MIME類型,這些數(shù)據(jù)單元組成一個(gè)鏈表,提交到HTTP服務(wù)器。libcurl提供了方便的api用于支持multi-part formposts。使用curl_formadd函數(shù),可以添加不同的數(shù)據(jù)數(shù)據(jù)單元,然后提交到服務(wù)器。下面是一個(gè)multi-part formposts的例子(更詳細(xì)的使用,請(qǐng)參考:http://curl.haxx.se/libcurl/c/curl_formadd.html):

int main() {

               curl_global_init(CURL_GLOBAL_WIN32); 
               CURL *easy_handle = curl_easy_init();

// 使用multi-parts form post curl_easy_setopt(easy_handle, CURLOPT_URL,http://localhost:2210/Default.aspx);
curl_httppost *post = NULL;
curl_httppost *last = NULL;
// 文本數(shù)據(jù) curl_formadd(&post, &last, CURLFORM_COPYNAME, "name", CURLFORM_COPYCONTENTS, "JGood", CURLFORM_END);
curl_formadd(&post, &last, CURLFORM_COPYNAME, "address", CURLFORM_COPYCONTENTS, "HangZhou", CURLFORM_END);
// 文本文件中的數(shù)據(jù) curl_formadd(&post, &last, CURLFORM_COPYNAME, "file", CURLFORM_FILECONTENT, "ReadMe.txt", CURLFORM_END); curl_easy_setopt(easy_handle, CURLOPT_HTTPPOST, post); curl_easy_perform(easy_handle);
curl_formfree(post); curl_easy_cleanup(easy_handle); curl_global_cleanup(); 
return 0; }

     最后要說明的是,所有在easy handle上設(shè)置的屬性都是”sticky”的,什么意思?就是說在easy handle上設(shè)置的屬性都將被保存,即使執(zhí)行完curl_easy_perform之后,這些屬性值仍然存在。通過將CURLOPT_HTTPGET設(shè)為1可以使easy handle回到最原始的狀態(tài):

curl_easy_setopt(easy_handle, CURLOPT_HTTPGET, 1L);

 

顯示進(jìn)度

    libcurl支持通信過程中的進(jìn)度控制。通過將CURLOPT_NOPROCESS設(shè)置為0開啟進(jìn)度支持。該選項(xiàng)默認(rèn)值為1。對(duì)大多數(shù)應(yīng)用程序,我們需要提供一個(gè)進(jìn)度顯示回調(diào)。libcurl會(huì)不定期的將當(dāng)前傳輸?shù)倪M(jìn)度通過回調(diào)函數(shù)告訴你的程序。回調(diào)函數(shù)的原型如下:

int progress_callback(void *clientp, double dltotal, double dlnow, double ultotal, doubleulnow);

    通過CURLOPT_PROGRESSFUNCTION注冊(cè)該回調(diào)函數(shù)。參數(shù)clientp是一個(gè)用戶自定義指針,應(yīng)用程序通過CURLOPT_PROCESSDATA屬性將該自定義指定傳遞給libcurl。libcurl對(duì)該參數(shù)不作任何處理,只是簡(jiǎn)單將其傳遞給回調(diào)函數(shù)。

在C++中使用libcurl

    在C++中使用libcurl跟在C語言中沒有任何區(qū)別,只有一個(gè)地方要注意:回調(diào)函數(shù)不能是類的非靜態(tài)成員函數(shù)。例如:

class AClass { static size_t write_data(void *ptr, size_t size, size_t nmemb, void*ourpointer) { /* do what you want with the data */ } }

 

代理

    什么是代理?Merrian-Webster的解釋是:一個(gè)通過驗(yàn)證的用戶扮演另一個(gè)用戶。今天,代理已經(jīng)被廣泛的使用。許多公司提供網(wǎng)絡(luò)代理服務(wù)器,允許員工的網(wǎng)絡(luò)客戶端訪問、下載文件。代理服務(wù)器處理這些用戶的請(qǐng)求。

    libcurl支持SOCKS和HTTP代理。使用代理,libcurl會(huì)把用戶輸入的URL提交給代理服務(wù)器,而不是直接根據(jù)URL去訪問遠(yuǎn)程資源。

    當(dāng)前版本的libcurl并不支持SOCKS代理的所有功能。

    對(duì)于HTTP代理來說,即使請(qǐng)求的URL不是一個(gè)合法的HTTP URL(比方你提供了一個(gè)ftp的url),它仍然會(huì)先被提交到HTTP代理。

代理選項(xiàng)

    CURLOPT_PROXY屬性用于設(shè)置libcurl使用的代理服務(wù)器地址:

curl_easy_setopt(easy_handle, CURLOPT_PROXY, "proxy-host.com:8080");

    可以把主機(jī)名與端口號(hào)分開設(shè)置:

curl_easy_setopt(easy_handle, CURLOPT_PROXY, "proxy-host.com"); curl_easy_setopt(easy_handle, CURLOPT_PROXYPORT, "8080"); // 端口號(hào)是用字符串還是整數(shù)??

    有些代理服務(wù)器要求用戶通過驗(yàn)證之后才允許接受其請(qǐng)求,此時(shí)應(yīng)該先提供驗(yàn)證信息:

curl_easy_setopt(easy_handle, CURLOPT_PROXYUSERPWD, "user:password");

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

curl_easy_setopt(easy_handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
 
環(huán)境變量

     對(duì)于有些協(xié)議,libcurl會(huì)自動(dòng)檢測(cè)并使用一些環(huán)境變量,并根據(jù)這些環(huán)境變量來確定要使用的代理服務(wù)器。這些環(huán)境變量的名稱格式一般是"[protocol]_proxy"(注意小寫)。例如輸入一個(gè)HTTP的URL,那么名稱為"http_proxy"的環(huán)境變量就會(huì)被檢測(cè)是否存在,如果存在,libcurl會(huì)使用該環(huán)境變量指定的代理。相同的規(guī)則也適用于FTP。

    這些環(huán)境變量的值的格式必須是這樣的:"[protocol://][user:password@]machine[:port]"。libcurl會(huì)忽略掉[protocol://],如果沒有提供端口號(hào),libcurl使用該協(xié)議的默認(rèn)端口。 

    有兩個(gè)比較特殊的環(huán)境變量:'all_proxy'與'no_proxy'。如果一個(gè)URL所對(duì)應(yīng)的協(xié)議,它的環(huán)境變量沒有設(shè)置,那么'all_proxy'指定的代理將被使用。'no_proxy'則指定了一個(gè)不應(yīng)被使用的代理主機(jī)的列表。例如:no_proxy的值是'192.168.1.10',即使存在http_proxy,它的值也是'192.168.1.10','192.168.1.10'也不會(huì)被作為代理。no_proxy=”*”表示不允許使用任何代理。

    顯式地將CURLOPT_PROXY屬性設(shè)置為空,可以禁止libcurl檢查并使用環(huán)境變量來使用代理。

SSL和代理

    SSL為點(diǎn)到點(diǎn)通信提供安全保障。它包含一些強(qiáng)壯的加密措施和其他安全檢測(cè),這使得上面講到的代理方式不適用于SSL。除非代理服務(wù)器提供專用通道,對(duì)進(jìn)出該代理服務(wù)器的數(shù)據(jù)不作任何檢測(cè)或禁止。通過HTTP代理服務(wù)器打開SSL連接,意味著代理服務(wù)器要直接連接到目標(biāo)主機(jī)的指定端口。因?yàn)榇矸?wù)器對(duì)在專用通道上傳輸?shù)臄?shù)據(jù)的類型毫無所知,所以它往往會(huì)使某些機(jī)制失效,如緩存機(jī)制。許多組織只允許在443端口上創(chuàng)建這種類型的數(shù)據(jù)通道。

代理通道(Tunneling Through Proxy)

    正如上面講到的,要使SSL工作必須在代理服務(wù)器創(chuàng)建專用數(shù)據(jù)通道,通常專用通道只被限制應(yīng)用于HTTPS。通過HTTP代理在應(yīng)用程序與目標(biāo)之間創(chuàng)建一個(gè)專用數(shù)據(jù)通道,應(yīng)該預(yù)防在該專有通道上執(zhí)行非HTTP的操作,如進(jìn)行FTP上傳或執(zhí)行FTP命令。代理服務(wù)器管理員應(yīng)該禁止非法的操作。

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

curl_easy_setopt(easy_handle, CURLOPT_HTTPPROXYTUNNEL, 1L);

     有時(shí)候你想通過代理通道執(zhí)行平常的HTTP操作,而實(shí)際上卻可能使你不經(jīng)過代理服務(wù)器而直接與遠(yuǎn)程主機(jī)進(jìn)行交互。libcurl不會(huì)代替這種新引入的行為。

自動(dòng)配置代理

    許多瀏覽器支持自動(dòng)配置代理,例如NetScape。libcurl并不支持這些。

持久化的好處(Persistence Is The Way to Happiness)

    當(dāng)需要發(fā)送多次請(qǐng)求時(shí),應(yīng)該重復(fù)使用easy handle。

    每次執(zhí)行完curl_easy_perform,licurl會(huì)繼續(xù)保持與服務(wù)器的連接。接下來的請(qǐng)求可以使用這個(gè)連接而不必創(chuàng)建新的連接(如果目標(biāo)主機(jī)是同一個(gè)的話)。這樣可以減少網(wǎng)絡(luò)開銷。 
    即使連接被釋放了,libcurl也會(huì)緩存這些連接的會(huì)話信息,這樣下次再連接到目標(biāo)主機(jī)上時(shí),就可以使用這些信息,從而減少重新連接所需的時(shí)間。

    FTP連接可能會(huì)被保存較長(zhǎng)的時(shí)間。因?yàn)榭蛻舳艘cFTP服務(wù)器進(jìn)行頻繁的命令交互。對(duì)于有訪問人數(shù)上限的FTP服務(wù)器,保持一個(gè)長(zhǎng)連接,可以使你不需要排除等待,就直接可以與FTP服務(wù)器通信。

    libcurl會(huì)緩存DNS的解析結(jié)果。

    在今后的libcurl版本中,還會(huì)添加一些特性來提高數(shù)據(jù)通信的效率。 
    每個(gè)easy handle都會(huì)保存最近使用的幾個(gè)連接,以備重用。默認(rèn)是5個(gè)。可以通過CURLOPT_MAXCONNECTS屬性來設(shè)置保存連接的數(shù)量。

    如果你不想重用連接,將CURLOPT_FRESH_CONNECT屬性設(shè)置為1。這樣每次提交請(qǐng)求時(shí),libcurl都會(huì)先關(guān)閉以前創(chuàng)建的連接,然后重新創(chuàng)建一個(gè)新的連接。也可以將CURLOPT_FORBID_REUSE設(shè)置為1,這樣每次執(zhí)行完請(qǐng)求,連接就會(huì)馬上關(guān)閉。

libcurl使用的HTTP消息頭

     當(dāng)使用libcurl發(fā)送http請(qǐng)求時(shí),它會(huì)自動(dòng)添加一些http頭。我們可以通過CURLOPT_HTTPHEADER屬性手動(dòng)替換、添加或刪除相應(yīng)的HTTP消息頭。

Host

    http1.1(大部分http1.0)版本都要求客戶端請(qǐng)求提供這個(gè)信息頭。

Pragma

    "no-cache"。表示不要緩沖數(shù)據(jù)。

Accept

    "*/*"。表示允許接收任何類型的數(shù)據(jù)。

Expect

    以POST的方式向HTTP服務(wù)器提交請(qǐng)求時(shí),libcurl會(huì)設(shè)置該消息頭為"100-continue",它要求服務(wù)器在正式處理該請(qǐng)求之前,返回一個(gè)"OK"消息。如果POST的數(shù)據(jù)很小,libcurl可能不會(huì)設(shè)置該消息頭。

自定義選項(xiàng)

    當(dāng)前越來越多的協(xié)議都構(gòu)建在HTTP協(xié)議之上(如:soap),這主要?dú)w功于HTTP的可靠性,以及被廣泛使用的代理支持(可以穿透大部分防火墻)。 這些協(xié)議的使用方式與傳統(tǒng)HTTP可能有很大的不同。對(duì)此,libcurl作了很好的支持。

自定義請(qǐng)求方式(CustomRequest)

    HTTP支持GET, HEAD或者POST提交請(qǐng)求。可以設(shè)置CURLOPT_CUSTOMREQUEST來設(shè)置自定義的請(qǐng)求方式,libcurl默認(rèn)以GET方式提交請(qǐng)求:

curl_easy_setopt(easy_handle, CURLOPT_CUSTOMREQUEST, "MYOWNREQUEST");
 
修改消息頭

    HTTP協(xié)議提供了消息頭,請(qǐng)求消息頭用于告訴服務(wù)器如何處理請(qǐng)求;響應(yīng)消息頭則告訴瀏覽器如何處理接收到的數(shù)據(jù)。在libcurl中,你可以自由的添加這些消息頭:

struct curl_slist *headers=NULL; /* init to NULL is important */ headers = curl_slist_append(headers, "Hey-server-hey: how are you?"); headers = curl_slist_append(headers, "X-silly-content: yes"); /* pass our list of custom made headers */curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers); curl_easy_perform(easyhandle); /* transfer http */ curl_slist_free_all(headers); /* free the header list */

    對(duì)于已經(jīng)存在的消息頭,可以重新設(shè)置它的值:

headers = curl_slist_append(headers, "Accept: Agent-007"); headers = curl_slist_append(headers, "Host: munged.host.line");
 
刪除消息頭

    對(duì)于一個(gè)已經(jīng)存在的消息頭,設(shè)置它的內(nèi)容為空,libcurl在發(fā)送請(qǐng)求時(shí)就不會(huì)同時(shí)提交該消息頭:

headers = curl_slist_append(headers, "Accept:");

 

強(qiáng)制分塊傳輸(Enforcing chunked transfer-encoding)

    (這段文字理解可能有誤碼)以非GET的方式提交HTTP請(qǐng)求時(shí),如果設(shè)置了自定義的消息頭”Transfer-Encoding:chunked”,libcurl會(huì)分塊提交數(shù)據(jù),即使要上傳的數(shù)據(jù)量已經(jīng)知道。在上傳數(shù)據(jù)大小未知的情況下,libcurl自動(dòng)采用分塊上傳數(shù)據(jù)。(譯者注:非GET方式提交請(qǐng)求,提交的數(shù)據(jù)量往往比較大。)

HTTP版本

    每一次http請(qǐng)求,都包含一個(gè)表示當(dāng)前使用http版本的消息頭。libcurl默認(rèn)使用HTTP 1.1。可以通過CURLOPT_HTTP_VERSION屬性來設(shè)置具體的版本號(hào):

curl_easy_setopt(easy_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);

 

FTP自定義命令

    并不是所以的協(xié)議都像HTTP那樣,通過消息頭來告訴服務(wù)器如何處理請(qǐng)求。對(duì)于FTP,你就要使用另外的方式來處理。

    發(fā)送自定義的命令到ftp服務(wù)器,意味著你發(fā)送的命令必須是能被ftp服務(wù)器理解的命令(FTP協(xié)議中定義的命令,參考rfc959)。

    下面是一個(gè)簡(jiǎn)單的例子,在文件傳輸操作操作之前刪除指定文件:

headers = curl_slist_append(headers, "DELE file-to-remove");
/* pass the list of custom commands to the handle */ 
curl_easy_setopt(easyhandle, CURLOPT_QUOTE, headers);
// curl_easy_setopt(easyhandle, CURLOPT_POSTQUOTE, headers); // 在數(shù)據(jù)傳輸之后操行刪除操作
curl_easy_perform(easyhandle); /* transfer ftp data! */ curl_slist_free_all(headers); /* free the header list */

    FTP服務(wù)器執(zhí)行命令的順序,同這些命令被添加到列表中順序是一致的。發(fā)往服務(wù)器的命令列表中,只要有一個(gè)命令執(zhí)行失敗,ftp服務(wù)器就會(huì)返回一個(gè)錯(cuò)誤代碼,此時(shí)libcurl將直接返回CURLE_QUOTE_ERROR,不再執(zhí)行剩余的FTP命令。

    將CURLOPT_HEADER設(shè)置為1,libcurl獲取目標(biāo)文件的信息,并以HTTP消息頭的樣式來輸出消息頭。

FTP自定義CUSTOMREQUEST

    使用CURLOPT_CUSTOMREQUEST屬性,可以向FTP服務(wù)器發(fā)送命令。"NLST"是ftp默認(rèn)的列出文件列表的命令。 下面的代碼用于列出FTP服務(wù)器上的文件列表:

int main(int argc, char **argv) { curl_global_init(CURL_GLOBAL_WIN32);
CURL *easy_handle = curl_easy_init(); curl_easy_setopt(easy_handle, CURLOPT_URL, "ftp://127.0.0.1/"); curl_easy_setopt(easy_handle, CURLOPT_CUSTOMREQUEST, "NLST");
curl_easy_perform(easy_handle);
curl_easy_cleanup(easy_handle); curl_global_cleanup(); 
return 0;
}

 

Cookies Without Chocolate Chips

     cookie是一個(gè)鍵值對(duì)的集合,HTTP服務(wù)器發(fā)給客戶端的cookie,客戶端提交請(qǐng)求的時(shí)候,也會(huì)將cookie發(fā)送到服務(wù)器。服務(wù)器可以根據(jù)cookie來跟蹤用戶的會(huì)話信息。cookie有過期時(shí)間,超時(shí)后cookie就會(huì)失效。cookie有域名和路徑限制,cookie只能發(fā)給指定域名和路徑的HTTP服務(wù)器。

    cookie以消息頭”Set-Cookie”的形式從HTTP服務(wù)器發(fā)送到客戶端;客戶端發(fā)以消息頭”Cookie”的形式將Cookie提交到HTTP服務(wù)器。為了對(duì)這些東西有個(gè)直觀的概念,下圖是FireFox中,使用Firebug跟蹤到的cookie消息頭: 
 pic1

    在libcurl中,可以通過CURLOPT_COOKIE屬性來設(shè)置發(fā)往服務(wù)器的cookie:

curl_easy_setopt(easy_handle, CURLOPT_COOKIE, "name1=var1; name2=var2;");

    下面的例子演示了如何使用libcurl發(fā)送cookie信息給HTTP服務(wù)器,代碼非常的簡(jiǎn)單:

int main(int argc, char **argv) { curl_global_init(CURL_GLOBAL_WIN32); CURL *easy_handle = curl_easy_init(); 
curl_easy_setopt(easy_handle, CURLOPT_URL, http://localhost:2210/Default.aspx); curl_easy_setopt(easy_handle, CURLOPT_COOKIE, "name=JGood; address=HangZhou"); 
curl_easy_perform(easy_handle);

curl_easy_cleanup(easy_handle); curl_global_cleanup(); 
return 0; }

    下圖是在ASP.NET Web服務(wù)器上調(diào)試時(shí)跟蹤到的Cookie數(shù)據(jù):pic1

     在實(shí)在的應(yīng)用場(chǎng)景中,你可能需要保存服務(wù)器發(fā)送給你的cookie,并在接下來的請(qǐng)求中,把這些cookie一并發(fā)往服務(wù)器。所以,可以把上次從服務(wù)器收到的所有響應(yīng)頭信息保存到文本文件中,當(dāng)下次需要向服務(wù)器發(fā)送請(qǐng)求時(shí),通過CURLOPT_COOKIEFILE屬性告訴libcurl從該文件中讀取cookie信息。 
    設(shè)置CURLOPT_COOKIEFILE屬性意味著激活libcurl的cookie parser。在cookie parser被激活之前,libcurl忽略所以之前接收到的cookie信息。cookie parser被激活之后,cookie信息將被保存內(nèi)存中,在接下來的請(qǐng)求中,libcurl會(huì)自動(dòng)將這些cookie信息添加到消息頭里,你的應(yīng)用程序不需要做任何事件。大多數(shù)情況下,這已經(jīng)足夠了。需要注意的是,通過CURLOPT_COOKIEFILE屬性來激活cookie parser,給CURLOPT_COOKIEFILE屬性設(shè)置的一個(gè)保存cookie信息的文本文件路徑,可能并不需要在磁盤上物理存在。 
    如果你需要使用NetScape或者FireFox瀏覽器的cookie文件,你只要用這些瀏覽器的cookie文件的路徑來初始化CURLOPT_COOKIEFILE屬性,libcurl會(huì)自動(dòng)分析cookie文件,并在接下來的請(qǐng)求過程中使用這些cookie信息。 
    libcurl甚至能夠把接收到的cookie信息保存成能被Netscape/Mozilla的瀏覽器所識(shí)別的cookie文件。通過把這些稱為cookie-jar。通過設(shè)置CURLOPT_COOKIEJAR選項(xiàng),在調(diào)用curl_easy_cleanup釋放easy handle的時(shí)候,所有的這些cookie信息都會(huì)保存到cookie-jar文件中。這就使得cookie信息能在不同的easy handle甚至在瀏覽器之間實(shí)現(xiàn)共享。

FTP Peculiarities We Need

    在使用FTP協(xié)議進(jìn)行數(shù)據(jù)傳輸?shù)臅r(shí)候,需要?jiǎng)?chuàng)建兩個(gè)連接。第一個(gè)連接用于傳輸控制命令,另一個(gè)連接用于傳輸數(shù)據(jù)。(關(guān)于FTP的通信過程,請(qǐng)參考這篇文章:http://www.wangjia.net/bo-blog/post/698/)。 FTP通信需要?jiǎng)?chuàng)建兩個(gè)連接這個(gè)事實(shí)往往被很多人忽略。根據(jù)第二個(gè)連接的發(fā)起方是誰,可以分為主動(dòng)模式與被動(dòng)模式。libcurl對(duì)此都提供了支持。libcurl默認(rèn)使用被動(dòng)模式,因?yàn)楸粍?dòng)模式可以方便的穿透防火墻,NAT等問題。在被動(dòng)模式下,libcurl要求ftp服務(wù)器打開一個(gè)新的端口監(jiān)聽,然后libcurl連接該端口用于數(shù)據(jù)傳輸。如果使用主動(dòng)模式,程序必須告訴FTP服務(wù)器你監(jiān)聽的IP與端口,通過設(shè)置CURLOPT_FTPPORT屬性來完成。

Headers Equal Fun

    (這段文字我理解的很模糊,請(qǐng)讀者參考原文)有些協(xié)議提供獨(dú)立于正常數(shù)據(jù)的 消息頭、meta-data。正常的數(shù)據(jù)流里通常不包括 信息頭和元數(shù)據(jù)。可以將CURLOPT_HEADER設(shè)置為1,使信息頭、元數(shù)據(jù)也能出現(xiàn)在數(shù)據(jù)流中。libcurl的強(qiáng)大之處在于,它能夠從數(shù)據(jù)流中解析出消息頭,….

Post Transfer Information

[ curl_easy_getinfo ]

安全考慮

    請(qǐng)參考原文,此處略。

使用multi interface同時(shí)進(jìn)行多項(xiàng)傳輸

     上面介紹的easy interface以同步的方式進(jìn)行數(shù)據(jù)傳輸,curl_easy_perform會(huì)一直阻塞到數(shù)據(jù)傳輸完畢后返回,且一次操作只能發(fā)送一次請(qǐng)求,如果要同時(shí)發(fā)送多個(gè)請(qǐng)求,必須使用多線程。 
    而multi interface以一種簡(jiǎn)單的、非阻塞的方式進(jìn)行傳輸,它允許在一個(gè)線程中,同時(shí)提交多個(gè)相同類型的請(qǐng)求。 在使用multi interface之前,你應(yīng)該掌握easy interface的基本使用。因?yàn)閙ulti interface是建立在easy interface基礎(chǔ)之上的,它只是簡(jiǎn)單的將多個(gè)easy handler添加到一個(gè)multi stack,而后同時(shí)傳輸而已。 
    使用multi interface很簡(jiǎn)單,首先使用curl_multi_init()函數(shù)創(chuàng)建一個(gè)multi handler,然后使用curl_easy_init()創(chuàng)建一個(gè)或多個(gè)easy handler,并按照上面幾章介紹的接口正常的設(shè)置相關(guān)的屬性,然后通過curl_multi_add_handler將這些easy handler添加到multi handler,最后調(diào)用curl_multi_perform進(jìn)行數(shù)據(jù)傳輸。 
    curl_multi_perform是異步的、非阻塞的函數(shù)。如果它返回CURLM_CALL_MULTI_PERFORM,表示數(shù)據(jù)通信正在進(jìn)行。

    通過select()來操作multi interface將會(huì)使工作變得簡(jiǎn)單(譯者注:其實(shí)每個(gè)easy handler在低層就是一個(gè)socket,通過select()來管理這些socket,在有數(shù)據(jù)可讀/可寫/異常的時(shí)候,通知應(yīng)用程序)。在調(diào)用select()函數(shù)之前,應(yīng)該使用curl_multi_fdset來初始化fd_set變量。

     select()函數(shù)返回時(shí),說明受管理的低層socket可以操作相應(yīng)的操作(接收數(shù)據(jù)或發(fā)送數(shù)據(jù),或者連接已經(jīng)斷開),此時(shí)應(yīng)該馬上調(diào)用curl_multi_perform,libcurl將會(huì)執(zhí)行相應(yīng)操作。使用select()時(shí),應(yīng)該設(shè)置一個(gè)較短的超時(shí)時(shí)間。在調(diào)用select()之前,造成不要忘記通過curl_multi_fdset來初始化fd_set,因?yàn)槊看尾僮鳎琭d_set中的文件描述符可能都不一樣。

    如果你想中止multi stack中某一個(gè)easy handle的數(shù)據(jù)通信,可以調(diào)用curl_multi_remove_handle函數(shù)將其從multi stack中取出。千萬另忘記釋放掉easy handle(通過curl_easy_cleanup()函數(shù))。

    當(dāng)multi stack中的一個(gè)eash handle完成數(shù)據(jù)傳輸?shù)臅r(shí)候,同時(shí)運(yùn)行的傳輸任務(wù)數(shù)量就會(huì)減少一個(gè)。當(dāng)數(shù)量降到0的時(shí)候,說明所有的數(shù)據(jù)傳輸已經(jīng)完成。

    curl_multi_info_read用于獲取當(dāng)前已經(jīng)完成的傳輸任務(wù)信息,它返回每一個(gè)easy handle的CURLcode狀態(tài)碼。可以根據(jù)這個(gè)狀態(tài)碼來判斷每個(gè)easy handle傳輸是否成功。

    下面的例子,演示了如何使用multi interface進(jìn)行網(wǎng)頁抓取:

int main(int argc, char **argv) { // 初始化 curl_global_init(CURL_GLOBAL_WIN32); CURLM *multi_handle = NULL; CURL *easy_handle1 = NULL; CURL *easy_handle2 = NULL; extern size_t save_sina_page(void *buffer, size_t size, size_t count, void *user_p); extern size_t save_sohu_page(void *buffer, size_t size, size_t count, void *user_p); FILE *fp_sina = fopen("sina.html", "ab+"); FILE *fp_sohu = fopen("sohu.html", "ab+"); multi_handle = curl_multi_init(); // 設(shè)置easy handle easy_handle1 = curl_easy_init(); curl_easy_setopt(easy_handle1, CURLOPT_URL, "http://www.sina.com.cn"); curl_easy_setopt(easy_handle1, CURLOPT_WRITEFUNCTION, &save_sina_page); curl_easy_setopt(easy_handle1, CURLOPT_WRITEDATA, fp_sina); easy_handle2 = curl_easy_init(); curl_easy_setopt(easy_handle2, CURLOPT_URL, "http://www.sohu.com"); curl_easy_setopt(easy_handle2, CURLOPT_WRITEFUNCTION, &save_sohu_page); curl_easy_setopt(easy_handle2, CURLOPT_WRITEDATA, fp_sohu); // 添加到multi stackcurl_multi_add_handle(multi_handle, easy_handle1); curl_multi_add_handle(multi_handle, easy_handle2); // int running_handle_count; while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(multi_handle, &running_handle_count)) { cout << running_handle_count << endl; } while (running_handle_count) { timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; int max_fd; fd_set fd_read; fd_set fd_write; fd_set fd_except; FD_ZERO(&fd_read); FD_ZERO(&fd_write); FD_ZERO(&fd_except); curl_multi_fdset(multi_handle, &fd_read, &fd_write, &fd_except, &max_fd);int return_code = select(max_fd + 1, &fd_read, &fd_write, &fd_except, &tv); if (SOCKET_ERROR == return_code) { cerr << "select error." << endl; break; } else { while(CURLM_CALL_MULTI_PERFORM == curl_multi_perform(multi_handle, &running_handle_count)) { cout << running_handle_count << endl; } } } // 釋放資源 fclose(fp_sina); fclose(fp_sohu); curl_easy_cleanup(easy_handle1); curl_easy_cleanup(easy_handle2); curl_multi_cleanup(multi_handle); curl_global_cleanup(); return 0; } size_t save_sina_page(void *buffer, size_t size, size_t count, void *user_p) { return fwrite(buffer, size, count, (FILE *)user_p); } size_t save_sohu_page(void *buffer, size_t size, size_t count,void *user_p) { return fwrite(buffer, size, count, (FILE *)user_p); }

 

SSL, 證書,其他技巧

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

在easy handler之間共享數(shù)據(jù)

[fill in]


posted on 2012-02-19 22:53 楊粼波 閱讀(7415) 評(píng)論(1)  編輯 收藏 引用 所屬分類: 文章收藏網(wǎng)絡(luò)編程C++

評(píng)論

# re: libcurl教程 2015-07-30 17:52 無憂

給力啊  回復(fù)  更多評(píng)論   

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            狠狠色狠狠色综合日日tαg| 国语精品一区| 久久福利一区| 欧美日韩国产综合网| 亚洲理伦在线| 一区二区三区www| 国产精品综合色区在线观看| 久久久精品国产免大香伊| 欧美专区日韩视频| 91久久国产精品91久久性色| 亚洲人www| 国产精品扒开腿做爽爽爽软件| 先锋影院在线亚洲| 久久综合九色综合欧美狠狠| 一区二区日韩免费看| 亚洲一区视频| 亚洲国产欧美一区二区三区久久| 日韩视频在线观看国产| 国产欧美一区二区三区在线看蜜臀 | 久久资源在线| 欧美日韩一二三区| 久久先锋影音| 欧美色欧美亚洲另类七区| 久久久久九九视频| 欧美日韩精品免费观看| 久久香蕉国产线看观看av| 欧美精品久久久久久久久久| 久久国产综合精品| 欧美人成免费网站| 美女视频网站黄色亚洲| 欧美系列电影免费观看| 欧美激情国产精品| 国产亚洲精久久久久久| 99av国产精品欲麻豆| **欧美日韩vr在线| 亚洲欧美在线网| 中日韩在线视频| 六十路精品视频| 久久久久国产免费免费| 国产精品第三页| 亚洲精品乱码久久久久久| 国产在线观看精品一区二区三区| 日韩一级在线观看| 亚洲精品久久久久| 久久久久一区| 巨胸喷奶水www久久久免费动漫| 国产精品久久久999| 亚洲精品无人区| 欧美高清视频一区二区三区在线观看| 欧美日韩免费精品| 久久av红桃一区二区小说| 欧美区在线观看| 91久久线看在观草草青青| 永久域名在线精品| 久久精品人人做人人综合| 欧美资源在线| 国产亚洲视频在线| 小处雏高清一区二区三区| 亚洲一区亚洲| 国产精品国产三级国产普通话蜜臀 | 亚洲影视综合| 国产精品大片wwwwww| 一区二区三区高清不卡| 亚洲一区精品在线| 国产精品久久久久久久电影 | 欧美主播一区二区三区| 国产精品久久久久久影视 | 夜夜嗨av一区二区三区中文字幕 | 亚洲专区一二三| 欧美婷婷在线| 亚洲一区视频| 久久综合九色综合欧美狠狠| 国语自产精品视频在线看一大j8 | 亚洲成人资源| 欧美激情第4页| 亚洲精品一区二区在线| 这里只有精品电影| 国产精品素人视频| 久久久久.com| 亚洲欧洲日本在线| 亚洲欧美日韩成人高清在线一区| 国产欧美精品一区二区色综合| 午夜欧美大尺度福利影院在线看| 久久网站热最新地址| 亚洲欧洲久久| 国产精品免费一区二区三区在线观看| 亚洲女同性videos| 免费欧美网站| 亚洲一级高清| 精品96久久久久久中文字幕无| 美女在线一区二区| 亚洲天堂成人在线观看| 久久一区二区三区超碰国产精品| 亚洲精品在线视频| 国产精品视区| 欧美成人网在线| 亚洲欧美在线x视频| 亚洲国产精品久久久久秋霞不卡| 亚洲免费在线观看| 亚洲国产mv| 国产精品久久久久久久久久免费看| 久久久xxx| 宅男精品视频| 欧美激情在线播放| 久久久国产成人精品| 在线一区二区三区做爰视频网站| 亚洲人成高清| 久久网站热最新地址| 一区在线观看| 国产精品久久久久久久浪潮网站| 久久午夜精品一区二区| 亚洲免费中文| 日韩视频免费观看高清在线视频| 久久免费午夜影院| 午夜精品久久久久久久蜜桃app| 亚洲第一级黄色片| 国产一区二区三区四区五区美女 | 欧美亚洲一区二区三区| 日韩亚洲精品视频| 欧美国产日韩视频| 久久人人97超碰精品888| 亚洲欧美区自拍先锋| 亚洲免费电影在线| 亚洲精品1区| 一色屋精品视频免费看| 国产欧美日韩在线| 国产精品久久久久久av福利软件| 欧美精品乱人伦久久久久久| 麻豆精品精品国产自在97香蕉| 欧美一区二区三区四区在线观看 | 久久成人精品电影| 亚洲免费在线观看| 亚洲在线免费| 亚洲一区精品在线| 中日韩美女免费视频网址在线观看 | 亚洲精品永久免费精品| 亚洲经典视频在线观看| 亚洲电影下载| 亚洲国产一区二区精品专区| 在线观看中文字幕不卡| 国产中文一区| 欲香欲色天天天综合和网| 韩日欧美一区二区三区| 国语自产偷拍精品视频偷| 一区二区在线观看av| 精品成人一区二区| 亚洲国产精品免费| 亚洲区在线播放| 一区二区三区波多野结衣在线观看| 99re这里只有精品6| 一本大道久久a久久精二百| 一区二区三区日韩欧美精品| 亚洲性av在线| 欧美亚洲视频在线观看| 久久久久国产精品一区| 欧美国产日本在线| 亚洲日韩视频| 亚洲午夜国产一区99re久久| 午夜国产不卡在线观看视频| 久久久精品一区| 欧美精品导航| 国产精品自在线| 在线观看成人一级片| 99ri日韩精品视频| 亚洲欧美日韩国产| 久久综合网hezyo| 亚洲区免费影片| 亚洲尤物视频在线| 久久尤物视频| 欧美性生交xxxxx久久久| 国产亚洲毛片| av成人免费在线| 久久九九热re6这里有精品| 91久久国产综合久久| 午夜欧美大尺度福利影院在线看 | 亚洲一区二区三区免费在线观看| 久久精品视频在线播放| 欧美黄色大片网站| 国产欧亚日韩视频| 亚洲精品日韩在线| ●精品国产综合乱码久久久久| 欧美色图首页| 在线免费日韩片| 亚洲午夜视频在线| 欧美国产精品劲爆| 亚洲欧美日韩国产另类专区| 免费日本视频一区| 国产日本欧美在线观看| 日韩视频在线播放| 老**午夜毛片一区二区三区| 一本色道婷婷久久欧美| 毛片一区二区三区| 国产日本欧美在线观看| 亚洲午夜精品久久| 亚洲国产一区二区精品专区| 欧美在线影院| 国产欧美日韩一区二区三区在线观看| 日韩视频免费观看高清在线视频| 久久亚洲国产成人| 欧美一区二区视频观看视频|