libcurl编程
?假設(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è)基本原則就是:絕對(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ì)多線程操作的一些要求。
? 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);
??? 下面的這個(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;
}
總結(jié)
- 上一篇: SecureCRT如何进入和退出全屏及调
- 下一篇: curl使用笔记