libcurl编程
?假設你要獲取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時,應該滿足這些庫對多線程操作的一些要求。
? libcurl提供協議無關的方式進行數據傳輸。所以上傳一個文件到FTP服務器,跟向HTTP服務器提交一個PUT請求的操作方式是類似的:
1. 創建easy handle或者重用先前創建的easy handle。
2. 設置CURLOPT_URL屬性。
3. 編寫回調函數。在執行上傳的時候,libcurl通過回調函數讀取要上傳的數據。(如果要從遠程服務器下載數據,可以通過回調來保存接收到的數據。)回調函數的原型如下:
size_t function(char *bufptr, size_t size, size_t nitems, void *userp);
??? bufptr指針表示緩沖區,用于保存要上傳的數據,size * nitems是緩沖區數據的長度,userp是一個用戶自定義指針,libcurl不對該指針作任何操作,它只是簡單的傳遞該指針??梢允褂迷撝羔樤趹贸绦蚺clibcurl之間傳遞信息。
4. 注冊回調函數,設置自定義指針。語法如下:?
// 注冊回調函數
curl_easy_setopt(easy_handle, CURLOPT_READFUNCTION, read_function);?
// 設置自定義指針
curl_easy_setopt(easy_handle, CURLOPT_READDATA, &filedata);
5. 告訴libcurl,執行的是上傳操作。?
curl_easy_setopt(easy_handle, CURLOPT_UPLOAD, 1L);
??? 有些協議在沒有預先知道上傳文件大小的情況下,可能無法正確判斷上傳是否結束,所以最好預先使用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);
??? 下面的這個例子演示了如何獲取網頁源碼,將其保存到本地文件,并同時將獲取的源碼輸出到控制臺上。
/**
* @brief libcurl接收到數據時的回調函數
*
* 將接收到的數據保存到本地文件中,同時顯示在控制臺上。
*
* @param [in] buffer 接收到的數據所在緩沖區
* @param [in] size 數據長度
* @param [in] nmemb 數據片數量
* @param [in/out] 用戶自定義指針
* @return 獲取的數據長度
*/
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+"); //?
// 設置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);
// 執行數據請求
curl_easy_perform(easy_handle);
// 釋放資源
fclose(fp);
curl_easy_cleanup(easy_handle);
curl_global_cleanup();
return 0;
}
總結
- 上一篇: SecureCRT如何进入和退出全屏及调
- 下一篇: curl使用笔记