LibCurl開發_未了的雨_百度空間 - Google Chrome (2013/7/26 21:11:15)
LibCurl開發
一:LibCurl 編程流程
1.調用curl_global_init()初始化libcurl
2.調用 curl_easy_init()函數得到 easy interface型指針
3.調用curl_easy_setopt設置傳輸選項
4.根據curl_easy_setopt設置的傳輸選項,實現回調函數以完成用戶特定任務
5.調用curl_easy_perform()函數完成傳輸任務
6.調用curl_easy_cleanup()釋放內存
二:重要函數
1、CURLcode curl_global_init(long flags); //初始化libcurl
描述:這個函數只能用一次。(其實在調用curl_global_cleanup 函數后仍然可再用),如果這個函數在curl_easy_init函數調用時還沒調用,它全由libcurl庫自動完成。
參數:flags
CURL_GLOBAL_ALL //初始化所有的可能的調用。
CURL_GLOBAL_SSL //初始化支持 安全套接字層。
CURL_GLOBAL_WIN32 //初始化win32套接字庫。
CURL_GLOBAL_NOTHING //沒有額外的初始化。
2、void curl_global_cleanup(void);
描述:在結束libcurl使用的時候,用來對curl_global_init做的工作清理。類似於close的函數。
3、char *curl_version( );
描述: 打印當前libcurl庫的版本。
4、CURL *curl_easy_init( ); //得到 easy interface型指針
描述:curl_easy_init用來初始化一個CURL的指針(有些像返回FILE類型的指針一樣). 相應的在調用結束時要用curl_easy_cleanup函數清理.
一般curl_easy_init意味着一個會話的開始. 它的返回值一般都用在easy系列的函數中.
5、void curl_easy_cleanup(CURL *handle); //釋放內存
描述:這個調用用來結束一個會話.與curl_easy_init配合着用.
參數:CURL類型的指針.
6、CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter); //設置的傳輸選項,實現回調函數以完成用戶特定任務
描述: 這個函數最重要了.幾乎所有的curl 程序都要頻繁的使用它.它告訴curl庫.程序將有如何的行為. 比如要查看一個網頁的html代碼等.(這個函數有些像ioctl函數)參數:
(1) CURL類型的指針
(2) 各種CURLoption類型的選項.(都在curl.h庫里有定義,man 也可以查看到)
(3) parameter 這個參數既可以是個函數的指針,也可以是某個對象的指針,也可以是個long型的變量.它用什么這取決於第二個參數.
(4) CURLoption 這個參數的取值很多.具體的可以查看man手冊.
描述:這個函數在初始化CURL類型的指針 以及curl_easy_setopt完成后調用. 就像字面的意思所說perform就像是個舞台.讓我們設置的option 運作起來.
參數:CURL類型的指針.
補充:
(1)在連接過程中,如果出現異常,如網線拔掉,返回CURLE_COULDNT_CONNECT;
(2)在下載過程中,即已經連接上了,后面如果出現異常,如網線拔掉,返回CURLE_OPERATION_TIMEOUTED
9、curl_slist_free_all(slist); // free the list again
10、curl_formadd(struct curl_httppost ** firstitem, struct curl_httppost ** lastitem, ...) //add a section to a multipart/formdata HTTP POST
/****************************************************************/
libcurl note(Http應用)
設置Callback function處理Http頭,返回內容,進度
CURLOPT_WRITEFUNCTION
CURLOPT_WRITEDATA
CURLOPT_HEADERDATA
CURLOPT_PROGRESSFUNCTION
CURLOPT_PROGRESSDATA
設置連接等待時間,傳輸等待時間:
CURLOPT_TIMEOUT:CURLOPT_CONNECTIONTIMEOUT:
設置重定位URL:
CURLOPT_FOLLOWLOCATION實現斷點續傳:
CURLOPT_RANGE:
CURLOPT_RESUME_FROM:
CURLOPT_RESUME_FROM:
注: 在我的測試中 這兩個參數無效。
設置RANGE后 下載全部數據,而不是后續數據;
設置RESUME_FROM后,程序無響應。
Range: bytes=xx- [可以用來實現斷點續傳]
User-Agent: xx
Location: [網頁重定位 url]
Set-Cookie: [Cookie]
Content-Length: [報文長度]
Content-Type: [報文類型]
/****************************************************************/
三:應用實例
1、為什么要使用libcurl,
(1)作為http的客戶端,可以直接用socket連接服務器,然后對到的數據進行http解析,但要分析協議頭,實現代理…這樣太麻煩了。
(2)libcurl是一個開源的客戶端url傳輸庫,支持FTP,FTPS,TFTP,HTTP,HTTPS,GOPHER,TELNET,DICT,FILE和LDAP,支持Windows,Unix,Linux等平台,簡單易用,且庫文件占用空間不到200K。
2、get和post方式
客戶端在http連接時向服務提交數據的方式分為get和post兩種
(1)Get方式將所要傳輸的數據附在網址后面,然后一起送達服務器,它的優點是效率比較高;缺點是安全性差、數據不超過1024個字符、必須是7位的ASCII編碼;查詢時經常用此方法。
(2)Post通過Http post處理發送數據,它的優點是安全性較強、支持數據量大、支持字符多;缺點是效率相對低;編輯修改時多使用此方法。
3、cookie與session
(1)cookie是發送到客戶瀏覽器的文本串句柄,並保存在客戶機硬盤上,可以用來在某個Web站點會話之間持久地保持數據。cookie在客戶端。
(2)session是訪問者從到達某個特定主頁到離開為止的那段時間。每一訪問者都會單獨獲得一個session,實現站點多個用戶之間在所有頁面中共享信息。session在服務器上。
(3)libcurl中使用cookie,保存cookie, 使之后的鏈接與此鏈接使用相同的cookie
(3.1)在關閉鏈接的時候把cookie寫入指定的文件: curl_easy_setopt(curl, CURLOPT_COOKIEJAR, "/tmp/cookie.txt");
(3.2)取用現在有的cookie,而不重新得到cookie: curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt");
4、http與https的區別
(1)Http是明文發送,任何人都可以攔截並讀取內容
(2)Https是加密傳輸協議,用它傳輸的內容都是加密過的,https是http的擴展,其安全基礎是SSL協議
5、base64編碼
(1)如果要傳一段包含特殊字符比較多的數據,直接上傳就需要處理轉意符之類的很多問題,用base64編碼,它可以把數據轉成可讀的字串,base64由a-z, A-Z, +/總計64個字符組成。
(2)由於base64的組成部分有加號,而加號是url中的轉意字符,所以無論是get方式還是post,傳到服務器的過程中,都會把加號轉成空格,所以在傳base64之前需要把base64編碼后的加號替換成”%2B”,這樣就可以正常發送了。
6、curl_setop()函數中的參數中文說明
curl_setopt()函數將為一個CURL會話設置選項。option參數是你想要的設置,value是這個選項給定的值。下列選項的值將被作為長整形使用(在option參數中指定)
代碼例程
libcurl 使用筆記 - 喜歡初美 - 博客大巴 - Google Chrome (2013/7/26 17:50:53)
libcurl 使用筆記
日期:2009-12-24 | 分類:c & c++
使用 cURL 和 libcurl 通過 Internet 進行對話 - Google Chrome (2013/7/26 16:58:48)
使用 cURL 和 libcurl 通過 Internet 進行對話
將 libcurl 與 C 以及 Python 結合使用
簡介: cURL 是一個命令行工具,可以對文件傳輸使用許多協議,包括 HTTP、FTP、Secure Copy (SCP)、Telnet 等等。但是,除了可以用命令行通過 Internet 與端點對話外,還可以使用 libcurl 編寫簡單或復雜的程序,以自動化執行應用層的協議任務。本文將介紹 cURL 命令行工具,然后向您展示如何使用 libcurl 以及 C 和 Python 構建一個 HTTP 客戶端。
開發 HTTP 和 FTP 之類依賴於應用層協議的應用程序並不復雜,但也不簡單。進一步講,這不是應用程序的重點,因為大部分情況下,協議之上的內容才是真正重要的內容。因此,libcurl 引起了許多人的興趣,因為它的重點是應用程序而不是開發的各個方面。注意,很少有應用程序開發自己的 TCP/IP 堆棧,所以老話重提:盡可能重用以最小化開發安排並提高應用程序的可靠性。
本文首先簡單介紹應用層協議,然后介紹 cURL、libcurl 並解釋它們的用法。
如今構建應用程序已與過去大不相同。現在的應用程序需要能夠通過網絡或 Internet 進行通訊(提供人類可用的網絡 API 或接口),還要能支持用戶腳本化以提高靈活性。現代應用程序通常使用 HTTP 公開 Web 接口,並通過 Simple Mail Transport Protocol (SMTP) 提供警告通知。這些協議允許您將 Web 瀏覽器指向設備以獲得配置或狀態信息,並從設備或常用的電子郵件客戶端接收標准電子郵件(分別通過 HTTP 和 SMTP)。
這些 Web 服務通常構建在網絡堆棧的套接字層上(見圖 1)。套接字層實現一個最先出現在 Berkeley Software Distribution (BSD) 操作系統上的 API,並提取底層傳輸和網絡層協議的詳細信息。
圖 1. 網絡堆棧和 libcurl

Web 服務發生在客戶端和服務器之間的協議對話中。在 HTTP 上下文中,服務器是終端設備,客戶端是位於端點上的瀏覽器。對於 SMTP,服務器是郵件網關或端點用戶,客戶端是終端設備。在某些情況下,協議對話發生在兩個步驟(請求和響應)中,但另一些情況下,需要協商和通訊的通信量更多。這種協商可能增加了大量復雜性,這可以通過 API 進行抽象,比如 libcurl。
cURL 最初的設計初衷是使用不同的協議(比如 FTP、HTTP、SCP 等)在端點之間移動文件。它最初是一個命令行實用工具,但現在也是一個綁定了 30 多種語言的庫。因此,現在不僅可以通過 shell 使用 cURL,您還可以構建合並了這個重要功能的應用程序。libcurl 庫也是可以移植的,支持 Linux®、IBM®AIX®操作系統、BSD、Solaris 以及許多其他 UNIX®變體。
獲取和安裝 libcurl 非常簡單,取決於您所運行的 Linux 發行版。如果運行的是 Ubuntu,您可以使用 apt-get
輕松安裝這些包。以下行演示了如何為 libcurl 安裝 libcurl 和 Python 綁定:
$ sudo apt-get install libcurl3 $ sudo apt-get install python-pycurl |
apt-get
實用工具確保該過程滿足所有的依賴關系。
cURL 最開始是一個命令行工具,可以使用 Uniform Resource Locator (URL) 語法執行數據傳輸。考慮到它在命令行上的流行度,后來創建了一個可以在應用程序中生成這些行為的庫。如今,命令行 cURL 是 cURL 庫的包裝器。本文首先研究 cURL 作為命令行的功能,然后深入探討如何將它作為庫使用。
cURL 的兩種常見用法是使用 HTTP 和 FTP 協議進行文件傳輸。cURL 為這些協議提供一個簡單的接口。要使用 HTTP 從網站獲取文件,只需告訴 cURL 您要將網頁寫入到其中的本地文件的文件名、網站的 URL 以及要獲取的文件。讓我們看一下清單 1 中的簡單命令行示例。
清單 1. 使用 cURL 從網站獲取文件的示例
$ curl -o test html www.exampledomain.com % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 43320 100 43320 0 0 55831 0 --:--:-- --:--:-- --:--:-- 89299 $ |
注意,由於我指定了域而不是文件,我將獲得根文件(index.html)。要使用 cURL 將該文件移動到 FTP 站點,可以使用 -T
選項指定要上傳的文件,然后提供 FTP 站點的 URL 以及文件的路徑。
清單 2. 使用 cURL 將文件上傳到 FTP 站點的示例
$ curl -T test.html ftp://user:password@ftp.exampledomain.com/ftpdir/ % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 43320 0 0 100 43320 0 38946 0:00:01 0:00:01 --:--:-- 124k $ |
是不是很簡單?學習了一些模式之后您會發現,cURL 使用起來非常簡單。但是您可以使用的選項非常多 —在 cURL 命令行中請求幫助(使用 --help
)可以得到 129 行選項。如果您覺得這還不算太多,那么還有一大批其他控制選項(從詳細度到安全性),以及特定於協議的配置項。
從開發人員的角度看,這還不算是 cURL 最令人興奮的地方。讓我們深入了解 cURL 庫,學習如何向應用程序添加文件傳輸協議。
如果您有 10 年以上的腳本語言經驗,您就會注意到它們的標記有很大的變化。Python、Ruby、Perl 等這些腳本語言不僅包含套接字層(C 或 C++ 中也有),還包含了應用層協議 API。這些腳本語言合並了高級功能,可以創建 HTTP 服務器或客戶端。libcurl 庫為 C 和 C++ 之類的語言添加了類似的功能,但是它可以在不同的語言之間移植。在所有它支持的語言中都能找到與 libcurl 相當的行為,但是由於這些語言的差異很大(設想一下 C 和 Scheme),提供這些行為的方式也很不相同。
libcurl 庫以 API 的形式封裝清單 1和清單 2中描述的行為,因此它可以被高級語言使用(如今已超過 30 種)。本文提供了 libcurl 的兩個示例。第一個示例研究使用 c 構建的簡單 HTTP 客戶端(適合構建 Web 爬行器),第二個示例是一個使用 Python 創建的簡單 HTTP 客戶端。
C API 在 libcurl 功能上提供了兩個 API。easy 接口是一個簡單的同步 API(意味着當您使用請求調用 libcurl 時,將能夠滿足您的請求,直到完成或發生錯誤)。多接口可以進一步控制 libcurl,您的應用程序可以執行多個同步傳輸,並控制 libcurl 何時何地移動數據。
該示例使用 easy 接口。該 API 還能控制數據移動過程(使用回調),但正如其名稱所示,使用起來非常簡單。清單 3 提供了 HTTP 的 C 語言示例。
清單 3. 使用 libcurl easy 接口的 C HTTP 客戶端
#include <stdio.h> #include <string.h> #include <curl/curl.h> #define MAX_BUF 65536 char wr_buf[MAX_BUF+1]; int wr_index; /* * Write data callback function (called within the context of * curl_easy_perform. */ size_t write_data( void *buffer, size_t size, size_t nmemb, void *userp ) { int segsize = size * nmemb; /* Check to see if this data exceeds the size of our buffer. If so, * set the user-defined context value and return 0 to indicate a * problem to curl. */ if ( wr_index + segsize > MAX_BUF ) { *(int *)userp = 1; return 0; } /* Copy the data from the curl buffer into our buffer */ memcpy( (void *)&wr_buf[wr_index], buffer, (size_t)segsize ); /* Update the write index */ wr_index += segsize; /* Null terminate the buffer */ wr_buf[wr_index] = 0; /* Return the number of bytes received, indicating to curl that all is okay */ return segsize; } /* * Simple curl application to read the index.html file from a Web site. */ int main( void ) { CURL *curl; CURLcode ret; int wr_error; wr_error = 0; wr_index = 0; /* First step, init curl */ curl = curl_easy_init(); if (!curl) { printf("couldn't init curl\n"); return 0; } /* Tell curl the URL of the file we're going to retrieve */ curl_easy_setopt( curl, CURLOPT_URL, "www.exampledomain.com" ); /* Tell curl that we'll receive data to the function write_data, and * also provide it with a context pointer for our error return. */ curl_easy_setopt( curl, CURLOPT_WRITEDATA, (void *)&wr_error ); curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_data ); /* Allow curl to perform the action */ ret = curl_easy_perform( curl ); printf( "ret = %d (write_error = %d)\n", ret, wr_error ); /* Emit the page if curl indicates that no errors occurred */ if ( ret == 0 ) printf( "%s\n", wr_buf ); curl_easy_cleanup( curl ); return 0; } |
最上方是必需的 include
文件,包括 cURL 根文件。接下來,我定義了兩個用於傳輸的變量。第一個變量是 wr_buf
,表示將在其中寫入傳入數據的緩沖區。wr_index
表示緩沖區的當前寫入索引。
轉到 main
函數,該函數使用 easy API 進行設置。所有 cURL 調用都通過維護特定請求狀態的句柄進行操作。這稱為 CURL
指針引用。本例還創建一個特殊的返回碼,稱為 CURLcode
。在使用任何 libcurl 函數之前,您需要調用 curl_easy_init
獲取 CURL
句柄。接下來,注意 curl_easy_setopt
調用的數量。它們為特定的操作配置句柄。對於這些調用,您提供句柄、命令和選項。首先,本例使用CURLOPT_URL
指定要獲取的 URL。然后,它使用 CURL_WRITEDATA
提供一個上下文變量(在本例中,它是內部的 write 錯誤變量)。最后,它使用 CURLOPT_WRITEFUNCTION
指定數據可用時應該調用的函數。在啟動 API 之后,API 將使用它讀取的數據多次調用該函數。
要開始傳輸,調用 curl_easy_perform
。它的工作是根據之前的配置執行傳輸。調用該函數時,在完成傳輸或發生錯誤之前該函數不會返回。main
的最后一步是提交返回狀態,提交頁面讀取,最后使用 curl_easy_cleanup
清除(當使用句柄執行完操作后)。
現在看看 write_data
函數。該函數是針對特定操作收到數據時調用的回調。注意,當您從網站讀取數據時,將寫入該數據(write_data
)。將向回調提供一個緩沖區(包含可用數據)、成員數量和大小(緩沖中可用數據總量)、上下文指針。第一個任務是確保緩沖區(wr_buf
)的空間足以寫入數據。如果不夠,它將設置上下文指針並返回 0,表示出現問題。否則,它將 cURL 緩沖區的數據復制到您的緩沖區,並增加索引,指向要寫入的下一個位置。本例還終止字符串,稍后可以對其使用 printf
。最后,它返回 libcurl 操作的字節數量。這將告訴 libcurl 數據被提取,它也可以丟棄該數據。這就是從網站將文件讀取到內存的相對簡單的方法。
本節提供的示例類似於基於 C 的 HTTP 客戶端,不過它使用的是 Python。Python 是一種非常有用的面向對象的腳本語言,在原型化和構建生產軟件方面非常突出。示例假設您較熟悉 Python,但使用不多,因此不要期望過高。
這個簡單的 Python HTTP 客戶端使用 pycurl
,如清單 4 所示。
清單 4. 使用 libcurl 的
pycurl
接口的 Python HTTP 客戶端
import sys import pycurl wr_buf = '' def write_data( buf ): global wr_buf wr_buf += buf def main(): c = pycurl.Curl() c.setopt( pycurl.URL, 'http://www.exampledomain.com' ) c.setopt( pycurl.WRITEFUNCTION, write_data ) c.perform() c.close() main() sys.stdout.write(wr_buf) |
這比 C 語言版本簡單的多。它首先導入必需的模塊(用於標准系統的sys
和 pycurl
模塊)。接下來,它定義 write 緩沖區(wr_buf
)。像 C 程序中一樣,我聲明一個 write_data
函數。注意,該函數只有一個參數:從 HTTP 服務器中讀取的數據緩沖區。我將該緩沖區連接到全局 write 緩沖區。main
函數首先創建一個 Curl
句柄,然后使用 setopt
方法為傳輸定義 URL
和 WRITEFUNCTION
。它調用 perform
方法啟動傳輸並關閉句柄。最后,它調用 main
函數,並將 write 緩沖區提交到 stdout
。注意,在這種情況下,您不需要錯誤上下文指針,因為您使用了 Python 字符串連接,這就是說您不會使用大小固定的字符串。
本文僅僅簡單介紹了 libcurl,介紹了它支持的多種協議和語言。希望這能夠展示它如何輕松構建使用應用層協議(如 HTTP)的應用程序。libcurl 網站(見 參考資料)提供了很多示例和有用的文檔。下一次開發 Web 瀏覽器、爬行器或其他有應用層協議要求的應用程序時,試試 libcurl。它一定能大大減少您的開發時間,並找回編碼的樂趣。
學習
- cURL是一個命令行工具和庫,實現了各種客戶端協議。它支持 12 種以上的協議,包括 FTP、HTTP、Telnet 以及其他安全變體。許多平台上都能找到 cURL,包括 Linux、AIX、BSD 和 Solaris,它支持 30 多種語言。
- PycURL是 libcurl API 之上的一個薄層,PycURL 速度非常快。使用 PycURL,您可以使用 libcurl 庫開發 Python 應用程序。
- 說到應用程序靈活性,您可以在 “用 Guile 編寫腳本” 中了解更多有關將腳本功能集成到應用程序的內容。
- 要收聽有關軟件開發人員的有趣采訪和討論,請查看 developerWorks 網絡廣播。
- 了解最新的 developerWorks 技術活動 和 網絡廣播。
- 在 Twitter 上跟隨 developerWorks。
- 查閱最近將在全球舉辦的面向 IBM 開放源碼開發人員的研討會、交易展覽、網絡廣播和其他 活動。
- 訪問 developerWorks 開源專區獲得豐富的 how-to 信息、工具和項目更新,幫助您用開放源碼技術進行開發,並與 IBM 產品結合使用。
- 查看免費的 developerWorks On demand 演示,觀看並了解 IBM 及開源技術和產品功能。
獲得產品和技術
- 使用 IBM 試用軟件改進您的下一個開發項目,這些軟件可以通過下載中心獲得。
- 下載 IBM 產品評估版 或 在 IBM SOA Sandbox 中研究在線試用版,開始使用來自 DB2®、Lotus®、Rational®、Tivoli®和 WebSphere®的應用程序開發工具和中間件產品。
fatal error: curl/curl.h: No such file or directory | Ubuntu 12.04 from alexsleat.co.uk - Google Chr (2013/7/26 16:49:05)
fatal error: curl/curl.h: No such file or directory | Ubuntu 12.04
If you’re missing the curl/curl.h header file, chances are you’re just missing the correct dev package file for the curl library.
That should fix it.
curl_easy_getinfo() -- 從 curl 句柄里獲得附加信息|Functions - 曲徑通幽 - Standing on the shoulders of giants - Goo (2013/5/23 17:52:30)
curl_easy_getinfo() 函數原型聲明如下:
1
2
|
#include <curl/curl.h>
CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ... );
|
使用該函數可以在請求求 curl 會話中的相關信息。注意,第 3 個參數必須是一個 long 型,或char型,或curl_slist型,抑或是double型的指針。函數所請求信息只有在函數返回 CURLE_OK 時才會被有效填充,該函數一般用在 perform 函數(如 curl_easy_perform() )之后。
第 2 個參數有眾多選項,每個選項都有其相應的含義:
CURLINFO_SIZE_DOWNLOAD
使用該選項時要求第 3 個參數是個 double 型的指針,這樣在一次傳輸成功后會將本次傳輸所下載的字節數賦值到指針所指向的 double 型變量中。注意,這個字節數只能反映最近一次的下載。
CURLINFO_SPEED_DOWNLOAD
該選項要求傳遞一個 double 型參數指針,用以接收下載的平均速度,這個速度不是即時速度,而是下載完成后的速度,單位是 字節/秒 。
CURLINFO_TOTAL_TIME
該選項要求傳遞一個 double 指針到函數中,double 型變量指示了傳輸的總耗時(單位為秒),這個總的時間里包括了域名解析,以及 TCP 連接過程中所需要的時間。
CURLINFO_CONTENT_TYPE
該選項獲得 HTTP 中從服務器端收到的頭部中的 Content-Type 信息。
測試代碼 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
#include <stdio.h>
#include <curl/curl.h>
#include <stdlib.h>
#include <string.h>
int
main(
void
)
{
CURL *curl;
CURLcode res;
FILE
*fp;
if
(!(fp =
fopen
(
"info.html"
,
"w+"
))) {
perror
(
"fopen error:"
);
exit
(EXIT_FAILURE);
}
curl = curl_easy_init();
if
(curl) {
res = curl_easy_perform(curl);
if
(CURLE_OK == res) {
char
*contype;
res = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &contype);
if
((CURLE_OK == res) && ct)
fwrite
(contype, 1,
strlen
(contype), fp);
fwrite
(
"\n\0"
, 1, 1, fp);
//為了在 vi 里看起來完全正常,需要添加換行符和 NULL
}
close (fp);
curl_easy_cleanup(curl);
}
return
0;
}
|
運行輸出:
$ cat info.html
text/html;charset=gb2312
CURLINFO_FILETIME
使用該選項時需要傳遞一個 long 型指針到函數,該 long 型變量中保存了遠程主機上的文件的最近修改日期。如果使用該值時函數返回 -1,原因是多樣的(比如一些未知的,比如服務器對此日期信息做了隱藏,或者是服務器不支持獲取文檔時間的命令等等)。 需要注意的是 ,在使用該選項時,需要先在 curl_easy_setopt() 函數中使用 CURLOPT_FILETIME 選項,然后再運行 curl_easy_perform() 后,方能獲得服務器上的文檔時間。
CURLINFO_CONTENT_LENGTH_DOWNLOAD
使用該選項時要求傳遞一個 double 型指針到函數中,該 double 型變量用來存放所要下載文件(或者是所要查詢的文件)的 content-length (文檔長度) 的信息。如果文件大小無法獲取,那么函數返回值為 -1 。
CURLINFO_FILETIME 和 CURLINFO_CONTENT_LENGTH_DOWNLOAD 選項使用示例,下面代碼獲取 FTP 服務器上的一個文件的時間和大小信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <curl/easy.h>
static
size_t
save_header(
void
*ptr,
size_t
size,
size_t
nmemb,
void
*data)
{
return
(
size_t
)(size * nmemb);
}
int
main(
void
)
{
CURL *curl;
CURLcode res;
const
time_t
filetime;
const
double
filesize;
const
char
*filename =
strrchr
(ftpurl,
'/'
) + 1;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if
(curl) {
curl_easy_setopt(curl, CURLOPT_URL, ftpurl);
curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
curl_easy_setopt(curl, CURLOPT_FILETIME, 1L);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, save_header);
curl_easy_setopt(curl, CURLOPT_HEADER, 0L);
//curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_perform(curl);
if
(res != CURLE_OK) {
res = curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime);
if
((CURLE_OK == res) && filetime)
printf
(
"filetime %s: %s"
, filename,
ctime
(&filetime));
res = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &filesize);
if
((CURLE_OK == res) && (filesize > 0))
printf
(
"filesize %s: %0.0f bytes\n"
, filename, filesize);
}
else
{
fprintf
(stderr,
"curl told us %d\n"
, res);
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return
0;
}
|
運行輸出:
beyes@debian:~/C/curl$ ./getftpinfo
Last-Modified: Mon, 25 Apr 2011 15:26:56 GMT
Content-Length: 1758
Accept-ranges: bytes
filetime favicon.ico: Mon Apr 25 11:26:56 2011
filesize favicon.ico: 1758 bytes
C++ 用libcurl庫進行http通訊網絡編程 - 綠色冰點 - 博客園 - Google Chrome (2013/5/23 16:59:53)
一、LibCurl基本編程框架 二、一些基本的函數 三、curl_easy_setopt函數部分選項介紹 四、curl_easy_perform 函數說明(error 狀態碼) 五、libcurl使用的HTTP消息頭
六、獲取http應答頭信息 七、多線程問題 八、什么時候libcurl無法正常工作 九、關於密碼 十、HTTP驗證 十一、代碼示例 1.基本的http GET/POST操作 2 獲取html網頁 3 網頁下載保存實例 4 進度條實例顯示文件下載進度 5 斷點續傳實例
libcurl是一個跨平台的網絡協議庫,支持http, https, ftp, gopher, telnet, dict, file, 和ldap 協議。libcurl同樣支持HTTPS證書授權,HTTP POST, HTTP PUT, FTP 上傳, HTTP基本表單上傳,代理,cookies,和用戶認證。想要知道更多關於libcurl的介紹,可以到官網 http://curl.haxx.se/上去了解,在這里不再詳述。
2. 調用 curl_easy_init()函數得到 easy interface型指針
3. 調用 curl_easy_setopt()設置傳輸選項
4. 根據 curl_easy_setopt()設置的傳輸選項,實現回調函數以完成用戶特定任務
5. 調用 curl_easy_perform()函數完成傳輸任務
6. 調用 curl_easy_cleanup()釋放內存
在整過過程中設置 curl_easy_setopt()參數是最關鍵的,幾乎所有的libcurl程序都要使用它。
二、一些基本的函數
1.CURLcode curl_global_init(long flags);
描述:
這個函數只能用一次。(其實在調用curl_global_cleanup 函數后仍然可再用)
如果這個函數在curl_easy_init函數調用時還沒調用,它講由libcurl庫自動調用, 所以多線程下最好主動調用該函數以防止在線程中curl_easy_init時多次調用。
參數:flags
CURL_GLOBAL_ALL //初始化所有的可能的調用。
CURL_GLOBAL_SSL //初始化支持 安全套接字層。
CURL_GLOBAL_WIN32 //初始化win32套接字庫。
CURL_GLOBAL_NOTHING //沒有額外的初始化。
2 void curl_global_cleanup(void);
描述:在結束libcurl使用的時候,用來對curl_global_init做的工作清理。類似於close的函數。
3 char *curl_version( );
描述: 打印當前libcurl庫的版本。
4 CURL *curl_easy_init( );
描述:
curl_easy_init用來初始化一個CURL的指針(有些像返回FILE類型的指針一樣). 相應的在調用結束時要用curl_easy_cleanup函數清理.
一般curl_easy_init意味着一個會話的開始. 它會返回一個easy_handle(CURL*對象), 一般都用在easy系列的函數中.
5 void curl_easy_cleanup(CURL *handle);
描述:
這個調用用來結束一個會話.與curl_easy_init配合着用.
參數:
CURL類型的指針.
6 CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
描述: 這個函數最重要了.幾乎所有的curl 程序都要頻繁的使用它.它告訴curl庫.程序將有如何的行為. 比如要查看一個網頁的html代碼等.(這個函數有些像ioctl函數)參數:
1 CURL類型的指針
2 各種CURLoption類型的選項.(都在curl.h庫里有定義,man 也可以查看到)
3 parameter 這個參數 既可以是個函數的指針,也可以是某個對象的指針,也可以是個long型的變量.它用什么這取決於第二個參數.
CURLoption 這個參數的取值很多.具體的可以查看man手冊.
7 CURLcode curl_easy_perform(CURL *handle);
option 運作起來.參數:
CURL類型的指針.
三、 curl_easy_setopt函數部分選項介紹
本節主要介紹curl_easy_setopt中跟http相關的參數。該函數是curl中非常重要的函數,curl所有設置都是在該函數中完成的,該函數的設置選項眾多,注意本節的闡述的只是部分常見選項。
設置訪問URL
2. CURLOPT_WRITEFUNCTION,CURLOPT_WRITEDATA
回調函數原型為: size_t function( void *ptr, size_t size, size_t nmemb, void *stream); 函數將在libcurl接收到數據后被調用,因此函數多做數據保存的功能,如處理下載文件。CURLOPT_WRITEDATA 用於表明CURLOPT_WRITEFUNCTION函數中的stream指針的來源。
3. CURLOPT_HEADERFUNCTION,CURLOPT_HEADERDATA
回調函數原型為 size_t function( void *ptr, size_t size,size_t nmemb, void *stream); libcurl一旦接收到http 頭部數據后將調用該函數。CURLOPT_WRITEDATA 傳遞指針給libcurl,該指針表明CURLOPT_HEADERFUNCTION 函數的stream指針的來源。
4. CURLOPT_READFUNCTION CURLOPT_READDATA
libCurl需要讀取數據傳遞給遠程主機時將調用CURLOPT_READFUNCTION指定的函數,函數原型是:size_t function(void *ptr, size_t size, size_t nmemb,void *stream). CURLOPT_READDATA 表明CURLOPT_READFUNCTION函數原型中的stream指針來源。
5. CURLOPT_NOPROGRESS,CURLOPT_PROGRESSFUNCTION,CURLOPT_PROGRESSDATA
跟數據傳輸進度相關的參數。CURLOPT_PROGRESSFUNCTION 指定的函數正常情況下每秒被libcurl調用一次,為了使CURLOPT_PROGRESSFUNCTION被調用,CURLOPT_NOPROGRESS必須被設置為false,CURLOPT_PROGRESSDATA指定的參數將作為CURLOPT_PROGRESSFUNCTION指定函數的第一個參數
6. CURLOPT_TIMEOUT,CURLOPT_CONNECTIONTIMEOUT:
CURLOPT_TIMEOUT 由於設置傳輸時間,CURLOPT_CONNECTIONTIMEOUT 設置連接等待時間
7. CURLOPT_FOLLOWLOCATION
設置重定位URL
8. CURLOPT_RANGE: CURLOPT_RESUME_FROM:
斷點續傳相關設置。CURLOPT_RANGE 指定char *參數傳遞給libcurl,用於指明http域的RANGE頭域,例如:
表示頭500個字節:bytes=0-499
表示第二個500字節:bytes=500-999
表示最后500個字節:bytes=-500
表示500字節以后的范圍:bytes=500-
第一個和最后一個字節:bytes=0-0,-1
同時指定幾個范圍:bytes=500-600,601-999
CURLOPT_RESUME_FROM 傳遞一個long參數給libcurl,指定你希望開始傳遞的 偏移量。
四、 curl_easy_perform 函數說明(error 狀態碼)
該函數是完成curl_easy_setopt指定的所有選項,本節重點介紹curl_easy_perform的返回值。返回0意味一切ok,非0代表錯誤發生。主要錯誤碼說明:
1. CURLE_OK
任務完成一切都好
2 CURLE_UNSUPPORTED_PROTOCOL
不支持的協議,由URL的頭部指定
3 CURLE_COULDNT_CONNECT
不能連接到remote 主機或者代理
4 CURLE_REMOTE_ACCESS_DENIED
訪問被拒絕
5 CURLE_HTTP_RETURNED_ERROR
Http返回錯誤
6 CURLE_READ_ERROR
讀本地文件錯誤
五、libcurl使用的HTTP消息頭
當使用libcurl發送http請求時,它會自動添加一些http頭。我們可以通過CURLOPT_HTTPHEADER屬性手動替換、添加或刪除相應 的HTTP消息頭。
Host
http1.1(大部分http1.0)版本都要求客戶端請求提供這個信息頭。
Pragma
"no-cache"。表示不要緩沖數據。
Accept
"*/*"。表示允許接收任何類型的數據。
Expect
以POST的方式向HTTP服務器提交請求時,libcurl會設置該消息頭為"100-continue",它要求服務器在正式處理該請求之前,返回一 個"OK"消息。如果POST的數據很小,libcurl可能不會設置該消息頭。
自定義選項
當前越來越多的協議都構建在HTTP協議之上(如:soap),這主要歸功於HTTP的可靠性,以及被廣泛使用的代理支持(可以穿透大部分防火牆)。 這些協議的使用方式與傳統HTTP可能有很大的不同。對此,libcurl作了很好的支持。
自定義請求方式(CustomRequest)
HTTP支持GET, HEAD或者POST提交請求。可以設置CURLOPT_CUSTOMREQUEST來設置自定義的請求方式,libcurl默認以GET方式提交請求:
curl_easy_setopt(easy_handle, CURLOPT_CUSTOMREQUEST, "MYOWNREQUEST");
修改消息頭
HTTP協議提供了消息頭,請求消息頭用於告訴服務器如何處理請求;響應消息頭則告訴瀏覽器如何處理接收到的數據。在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 */
對於已經存在的消息頭,可以重新設置它的值:
headers = curl_slist_append(headers, "Accept: Agent-007"); headers = curl_slist_append(headers, "Host: munged.host.line");
刪除消息頭
對於一個已經存在的消息頭,設置它的內容為空,libcurl在發送請求時就不會同時提交該消息頭:
headers = curl_slist_append(headers, "Accept:");
六、獲取http應答頭信息
發出http請求后,服務器會返回應答頭信息和應答數據,如果僅僅是打印應答頭的所有內容,則直接可以通過curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, 打印函數)的方式來完成,這里需要獲取的是應答頭中特定的信息,比如應答碼、cookies列表等,則需要通過下面這個函數:
CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ... );
info參數就是我們需要獲取的內容,下面是一些參數值:
1.CURLINFO_RESPONSE_CODE
獲取應答碼
2.CURLINFO_HEADER_SIZE
頭大小
3.CURLINFO_COOKIELIST
cookies列表
除了獲取應答信息外,這個函數還能獲取curl的一些內部信息,如請求時間、連接時間等等。
更多的參數可以參考API文檔。
七、多線程問題
首先一個基本原則就是:絕對不應該在線程之間共享同一個libcurl handle(CURL *對象),不管是easy handle還是multi handle(本文只介紹easy_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時,就越不容易犯錯。
九、關於密碼
客戶端向服務器發送請求時,許多協議都要求提供用戶名與密碼。libcurl提供了多種方式來設置它們。
一些協議支持在URL中直接指定用戶名和密碼,類似於: protocol://user:password@example.com/path/。libcurl能正確的識別這種URL中的用戶名與密碼並執行 相應的操作。如果你提供的用戶名和密碼中有特殊字符,首先應該對其進行URL編碼。
也可以通過CURLOPT_USERPWD屬性來設置用戶名與密碼。參數是格式如 “user:password ”的字符串:
curl_easy_setopt(easy_handle, CURLOPT_USERPWD, "user_name:password");
有時候在訪問代理服務器的時候,可能時時要求提供用戶名和密碼進行用戶身份驗證。這種情況下,libcurl提供了另 一個屬性CURLOPT_PROXYUSERPWD:
curl_easy_setopt(easy_handle, CURLOPT_PROXYUSERPWD, "user_name:password");
在UNIX平台下,訪問FTP的用戶名和密碼可能會被保存在$HOME/.netrc文件中。libcurl支持直接從這個文件中獲取用戶名與密碼:
curl_easy_setopt(easy_handle, CURLOPT_NETRC, 1L);
在使用SSL時,可能需要提供一個私鑰用於數據安全傳輸,通過CURLOPT_KEYPASSWD來設置私鑰:
curl_easy_setopt(easy_handle, CURLOPT_KEYPASSWD, "keypassword");
十、HTTP驗證
在使用HTTP協議時,客戶端有很多種方式向服務器提供驗證信息。默認的 HTTP驗證方法是"Basic”,它將用戶名與密碼以明文的方式、經Base64編碼后保存在HTTP請求頭中,發往服務器。當然這不太安全。
當前版本的libcurl支持的驗證方法有:basic, Digest, NTLM, Negotiate, GSS-Negotiate and SPNEGO。(譯者感嘆:搞Web這么多年,盡然不知道這些Http的驗證方式,實在慚愧。)可以通過CURLOPT_HTTPAUTH屬性來設置具體 的驗證方式:
curl_easy_setopt(easy_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
向代理服務器發送驗證信息時,可以通過CURLOPT_PROXYAUTH設置驗證方式:
curl_easy_setopt(easy_handle, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
也可以同時設置多種驗證方式(通過按位與), 使用‘CURLAUTH_ANY‘將允許libcurl可以選擇任何它所支持的驗證方式。通過CURLOPT_HTTPAUTH或 CURLOPT_PROXYAUTH屬性設置的多種驗證方式,libcurl會在運行時選擇一種它認為是最好的方式與服務器通信:
curl_easy_setopt(easy_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST|CURLAUTH_BASIC);
// curl_easy_setopt(easy_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
#include <stdio.h> #include <curl/curl.h> bool getUrl(char *filename) { CURL *curl; CURLcode res; FILE *fp; if ((fp = fopen(filename, "w")) == NULL) // 返回結果用文件存儲 return false; struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Accept: Agent-007"); curl = curl_easy_init(); // 初始化 if (curl) { //curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:8080");// 代理 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);// 改協議頭 curl_easy_setopt(curl, CURLOPT_URL,"http://www.baidu.com"); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); //將返回的http頭輸出到fp指向的文件 curl_easy_setopt(curl, CURLOPT_HEADERDATA, fp); //將返回的html主體數據輸出到fp指向的文件 res = curl_easy_perform(curl); // 執行 if (res != 0) { curl_slist_free_all(headers); curl_easy_cleanup(curl); } fclose(fp); return true; } } bool postUrl(char *filename) { CURL *curl; CURLcode res; FILE *fp; if ((fp = fopen(filename, "w")) == NULL) return false; curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt"); // 指定cookie文件 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "&logintype=uid&u=xieyan&psw=xxx86"); // 指定post內容 //curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:8080"); curl_easy_setopt(curl, CURLOPT_URL, " http://mail.sina.com.cn/cgi-bin/login.cgi "); // 指定url curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); res = curl_easy_perform(curl); curl_easy_cleanup(curl); } fclose(fp); return true; } int main(void) { getUrl("/tmp/get.html"); postUrl("/tmp/post.html"); }
編譯gcc get_post.c -o get_post –lcurl
./ get_post
2 獲取html網頁
#include <stdio.h> #include <curl/curl.h> #include <stdlib.h> int main(int argc, char *argv[]) { CURL *curl; //定義CURL類型的指針 CURLcode res; //定義CURLcode類型的變量,保存返回狀態碼 if(argc!=2) { printf("Usage : file <url>;\n"); exit(1); } curl = curl_easy_init(); //初始化一個CURL類型的指針 if(curl!=NULL) { //設置curl選項. 其中CURLOPT_URL是讓用戶指 定url. argv[1]中存放的命令行傳進來的網址 curl_easy_setopt(curl, CURLOPT_URL, argv[1]); //調用curl_easy_perform 執行我們的設置.並進行相關的操作. 在這 里只在屏幕上顯示出來. res = curl_easy_perform(curl); //清除curl操作. curl_easy_cleanup(curl); } return 0; }
編譯gcc get_http.c -o get_http –lcurl
./ get_http www.baidu.com
// 采用CURLOPT_WRITEFUNCTION 實現網頁下載保存功能 #include <stdio.h>; #include <stdlib.h>; #include <unistd.h>; #include <curl/curl.h>; #include <curl/types.h>; #include <curl/easy.h>; FILE *fp; //定義FILE類型指針 //這個函數是為了符合CURLOPT_WRITEFUNCTION而構造的 //完成數據保存功能 size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) { int written = fwrite(ptr, size, nmemb, (FILE *)fp); return written; } int main(int argc, char *argv[]) { CURL *curl; curl_global_init(CURL_GLOBAL_ALL); curl=curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, argv[1]); if((fp=fopen(argv[2],"w"))==NULL) { curl_easy_cleanup(curl); exit(1); } ////CURLOPT_WRITEFUNCTION 將后繼的動作交給write_data函數處理 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_perform(curl); curl_easy_cleanup(curl); exit(0); }
./ save_http www.baidu.com
/tmp/baidu
4 進度條實例顯示文件下載進度
// 采用CURLOPT_NOPROGRESS, CURLOPT_PROGRESSFUNCTION CURLOPT_PROGRESSDATA 實現文件傳輸進度提示功能 //函數采用了gtk庫,故編譯時需指定gtk庫 //函數啟動專門的線程用於顯示gtk 進度條bar #include <stdio.h> #include <gtk/gtk.h> #include <curl/curl.h> #include <curl/types.h> /* new for v7 */ #include <curl/easy.h> /* new for v7 */ GtkWidget *Bar; ////這個函數是為了符合CURLOPT_WRITEFUNCTION而構造的 //完成數據保存功能 size_t my_write_func(void *ptr, size_t size, size_t nmemb, FILE *stream) { return fwrite(ptr, size, nmemb, stream); } //這個函數是為了符合CURLOPT_READFUNCTION而構造的 //數據上傳時使用 size_t my_read_func(void *ptr, size_t size, size_t nmemb, FILE *stream) { return fread(ptr, size, nmemb, stream); } //這個函數是為了符合CURLOPT_PROGRESSFUNCTION而構造的 //顯示文件傳輸進度,t代表文件大小,d代表傳 輸已經完成部分 int my_progress_func(GtkWidget *bar, double t, /* dltotal */ double d, /* dlnow */ double ultotal, double ulnow) { /* printf("%d / %d (%g %%)\n", d, t, d*100.0/t);*/ gdk_threads_enter(); gtk_progress_set_value(GTK_PROGRESS(bar), d*100.0/t); gdk_threads_leave(); return 0; } void *my_thread(void *ptr) { CURL *curl; CURLcode res; FILE *outfile; gchar *url = ptr; curl = curl_easy_init(); if(curl) { outfile = fopen("test.curl", "w"); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_write_func); curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_read_func); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, my_progress_func); curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, Bar); res = curl_easy_perform(curl); fclose(outfile); /* always cleanup */ curl_easy_cleanup(curl); } return NULL; } int main(int argc, char **argv) { GtkWidget *Window, *Frame, *Frame2; GtkAdjustment *adj; /* Must initialize libcurl before any threads are started */ curl_global_init(CURL_GLOBAL_ALL); /* Init thread */ g_thread_init(NULL); gtk_init(&argc, &argv); Window = gtk_window_new(GTK_WINDOW_TOPLEVEL); Frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(Frame), GTK_SHADOW_OUT); gtk_container_add(GTK_CONTAINER(Window), Frame); Frame2 = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(Frame2), GTK_SHADOW_IN); gtk_container_add(GTK_CONTAINER(Frame), Frame2); gtk_container_set_border_width(GTK_CONTAINER(Frame2), 5); adj = (GtkAdjustment*)gtk_adjustment_new(0, 0, 100, 0, 0, 0); Bar = gtk_progress_bar_new_with_adjustment(adj); gtk_container_add(GTK_CONTAINER(Frame2), Bar); gtk_widget_show_all(Window); if (!g_thread_create(&my_thread, argv[1], FALSE, NULL) != 0) g_warning("can't create the thread"); gdk_threads_enter(); gtk_main(); gdk_threads_leave(); return 0; }
gcc progress.c –o progress ` pkg-config --libs –cflags gtk+-2..0` -lcurl –lgthread-2.0
./ progress http://software.sky-union.cn/index.asp
5 斷點續傳實例
//采用CURLOPT_RESUME_FROM_LARGE 實現文件斷點續傳功能 #include <stdlib.h> #include <stdio.h> #include <sys/stat.h> #include <curl/curl.h> //這個函數為CURLOPT_HEADERFUNCTION參數構造 /* 從http頭部獲取文件size*/ size_t getcontentlengthfunc(void *ptr, size_t size, size_t nmemb, void *stream) { int r; long len = 0; /* _snscanf() is Win32 specific */ // r = _snscanf(ptr, size * nmemb, "Content-Length: %ld\n", &len); r = sscanf(ptr, "Content-Length: %ld\n", &len); if (r) /* Microsoft: we don't read the specs */ *((long *) stream) = len; return size * nmemb; } /* 保存下載文件 */ size_t wirtefunc(void *ptr, size_t size, size_t nmemb, void *stream) { return fwrite(ptr, size, nmemb, stream); } /*讀取上傳文件 */ size_t readfunc(void *ptr, size_t size, size_t nmemb, void *stream) { FILE *f = stream; size_t n; if (ferror(f)) return CURL_READFUNC_ABORT; n = fread(ptr, size, nmemb, f) * size; return n; } // 下載 或者上傳文件函數 int download(CURL *curlhandle, const char * remotepath, const char * localpath, long timeout, long tries) { FILE *f; curl_off_t local_file_len = -1 ; long filesize =0 ; CURLcode r = CURLE_GOT_NOTHING; int c; struct stat file_info; int use_resume = 0; /* 得到本地文件大小 */ //if(access(localpath,F_OK) ==0) if(stat(localpath, &file_info) == 0) { local_file_len = file_info.st_size; use_resume = 1; } //采用追加方式打開文件,便於實現文件斷點續傳工作 f = fopen(localpath, "ab+"); if (f == NULL) { perror(NULL); return 0; } //curl_easy_setopt(curlhandle, CURLOPT_UPLOAD, 1L); curl_easy_setopt(curlhandle, CURLOPT_URL, remotepath); curl_easy_setopt(curlhandle, CURLOPT_CONNECTTIMEOUT, timeout); // 設置連接超時,單位秒 //設置http 頭部處理函數 curl_easy_setopt(curlhandle, CURLOPT_HEADERFUNCTION, getcontentlengthfunc); curl_easy_setopt(curlhandle, CURLOPT_HEADERDATA, &filesize); // 設置文件續傳的位置給libcurl curl_easy_setopt(curlhandle, CURLOPT_RESUME_FROM_LARGE, use_resume?local_file_len:0); curl_easy_setopt(curlhandle, CURLOPT_WRITEDATA, f); curl_easy_setopt(curlhandle, CURLOPT_WRITEFUNCTION, wirtefunc); //curl_easy_setopt(curlhandle, CURLOPT_READFUNCTION, readfunc); //curl_easy_setopt(curlhandle, CURLOPT_READDATA, f); curl_easy_setopt(curlhandle, CURLOPT_NOPROGRESS, 1L); curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 1L); r = curl_easy_perform(curlhandle); fclose(f); if (r == CURLE_OK) return 1; else { fprintf(stderr, "%s\n", curl_easy_strerror(r)); return 0; } } int main(int c, char **argv) { CURL *curlhandle = NULL; curl_global_init(CURL_GLOBAL_ALL); curlhandle = curl_easy_init(); //download(curlhandle, "ftp://user:pass@host/path/file", "C:\\file", 0, 3); download(curlhandle , "http://software.sky-union.cn/index.asp","/work/index.asp",1,3); curl_easy_cleanup(curlhandle); curl_global_cleanup(); return 0; }
./ resume
Linux libcurl使用(一)(收藏)_留風的貝殼_百度空間 - Google Chrome (2013/5/23 15:17:01)
Linux libcurl使用(一)(收藏)
LibCurl編程 http://wangjiajun53880.blog.163.com/blog/static/117001394200992011257363/
Linux 2009-10-20 13:07 閱讀 評論字號: 大 大 中 中 小 小2.1 LibCurl編程流程
在基於LibCurl的程序里,主要采用callback function (回調函數)的形式完成傳輸任務,用戶在啟動傳輸前設置好各類參數和回調函數,當滿足條件時libcurl將調用用戶的回調函數實現特定功能。下面是利用libcurl完成傳輸任務的流程:
1. 調用curl_global_init()初始化libcurl
2. 調用 curl_easy_init()函數得到 easy interface型指針
3. 調用curl_easy_setopt設置傳輸選項
4. 根據curl_easy_setopt設置的傳輸選項,實現回調函數以完成用戶特定任務
5. 調用curl_easy_perform()函數完成傳輸任務
6. 調用curl_easy_cleanup()釋放內存
在整過過程中設置curl_easy_setopt()參數是最關鍵的,幾乎所有的libcurl程序都要使用它。
2.2 重要函數
1.CURLcode curl_global_init(long flags);
描述:
這個函數只能用一次。(其實在調用curl_global_cleanup 函數后仍然可再用)
如果這個函數在curl_easy_init函數調用時還沒調用,它講由libcurl庫自動完成。
參數:flags
CURL_GLOBAL_ALL //初始化所有的可能的調用。
CURL_GLOBAL_SSL //初始化支持 安全套接字層。
CURL_GLOBAL_WIN32 //初始化win32套接字庫。
CURL_GLOBAL_NOTHING //沒有額外的初始化。
2 void curl_global_cleanup(void);
描述:在結束libcurl使用的時候,用來對curl_global_init做的工作清理。類似於close的函數。
3 char *curl_version( );
描述: 打印當前libcurl庫的版本。
4 CURL *curl_easy_init( );
描述:
curl_easy_init用來初始化一個CURL的指針(有些像返回FILE類型的指針一樣). 相應的在調用結束時要用curl_easy_cleanup函數清理.
一般curl_easy_init意味着一個會話的開始. 它的返回值一般都用在easy系列的函數中.
5 void curl_easy_cleanup(CURL *handle);
描述:
這個調用用來結束一個會話.與curl_easy_init配合着用.
參數:
CURL類型的指針.
6 CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
描述: 這個函數最重要了.幾乎所有的curl 程序都要頻繁的使用它.它告訴curl庫.程序將有如何的行為. 比如要查看一個網頁的html代碼等.(這個函數有些像ioctl函數)參數:
1 CURL類型的指針
2 各種CURLoption類型的選項.(都在curl.h庫里有定義,man 也可以查看到)
3 parameter 這個參數 既可以是個函數的指針,也可以是某個對象的指針,也可以是個long型的變量.它用什么這取決於第二個參數.
CURLoption 這個參數的取值很多.具體的可以查看man手冊.
7 CURLcode curl_easy_perform(CURL *handle);描述:這個函數在初始化CURL類型的指針 以及curl_easy_setopt完成后調用. 就像字面的意思所說perform就像是個舞台.讓我們設置的
option 運作起來.參數:
CURL類型的指針.
3.3 curl_easy_setopt函數介紹
本節主要介紹curl_easy_setopt中跟http相關的參數。注意本節的闡述都是以libcurl作為主體,其它為客體來闡述的。
1. CURLOPT_URL
設置訪問URL
2. CURLOPT_WRITEFUNCTION,CURLOPT_WRITEDATA
回調函數原型為:size_t function( void *ptr, size_t size, size_t nmemb, void *stream); 函數將在libcurl接收到數據后被調用,因此函數多做數據保存的功能,如處理下載文件。CURLOPT_WRITEDATA 用於表明CURLOPT_WRITEFUNCTION函數中的stream指針的來源。
3. CURLOPT_HEADERFUNCTION,CURLOPT_HEADERDATA
回調函數原型為 size_t function( void *ptr, size_t size,size_t nmemb, void *stream); libcurl一旦接收到http 頭部數據后將調用該函數。CURLOPT_WRITEDATA 傳遞指針給libcurl,該指針表明CURLOPT_HEADERFUNCTION 函數的stream指針的來源。
4. CURLOPT_READFUNCTION CURLOPT_READDATA
libCurl需要讀取數據傳遞給遠程主機時將調用CURLOPT_READFUNCTION指定的函數,函數原型是:size_t function(void *ptr, size_t size, size_t nmemb,void *stream). CURLOPT_READDATA 表明CURLOPT_READFUNCTION函數原型中的stream指針來源。
5. CURLOPT_NOPROGRESS,CURLOPT_PROGRESSFUNCTION,CURLOPT_PROGRESSDATA
跟數據傳輸進度相關的參數。CURLOPT_PROGRESSFUNCTION 指定的函數正常情況下每秒被libcurl調用一次,為了使CURLOPT_PROGRESSFUNCTION被調用,CURLOPT_NOPROGRESS必須被設置為false,CURLOPT_PROGRESSDATA指定的參數將作為CURLOPT_PROGRESSFUNCTION指定函數的第一個參數
6. CURLOPT_TIMEOUT,CURLOPT_CONNECTIONTIMEOUT:
CURLOPT_TIMEOUT 由於設置傳輸時間,CURLOPT_CONNECTIONTIMEOUT 設置連接等待時間
7. CURLOPT_FOLLOWLOCATION
設置重定位URL
CURLOPT_RANGE: CURLOPT_RESUME_FROM:
斷點續傳相關設置。CURLOPT_RANGE 指定char *參數傳遞給libcurl,用於指明http域的RANGE頭域,例如:
表示頭500個字節:bytes=0-499
表示第二個500字節:bytes=500-999
表示最后500個字節:bytes=-500
表示500字節以后的范圍:bytes=500-
第一個和最后一個字節:bytes=0-0,-1
同時指定幾個范圍:bytes=500-600,601-999
CURLOPT_RESUME_FROM 傳遞一個long參數給libcurl,指定你希望開始傳遞的
偏移量。
3.4 curl_easy_perform 函數說明(error 狀態碼)
該函數完成curl_easy_setopt指定的所有選項,本節重點介紹curl_easy_perform的返回值。返回0意味一切ok,非0代表錯誤發生。主要錯誤碼說明:
1. CURLE_OK
任務完成一切都好
2 CURLE_UNSUPPORTED_PROTOCOL
不支持的協議,由URL的頭部指定
3 CURLE_COULDNT_CONNECT
不能連接到remote 主機或者代理
4 CURLE_REMOTE_ACCESS_DENIED
訪問被拒絕
5 CURLE_HTTP_RETURNED_ERROR
Http返回錯誤
6 CURLE_READ_ERROR
讀本地文件錯誤
3.1 獲取html網頁
view plaincopy to clipboardprint?
#include <stdio.h>;
#include <stdlib.h>;
#include <unistd.h>;
#include <curl/curl.h>;
#include <curl/types.h>;
#include <curl/easy.h>;
FILE *fp; //定義FILE類型指針
size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) //這個函數是為了符合CURLOPT_WRITEFUNCTION, 而構造的
{
int written = fwrite(ptr, size, nmemb, (FILE *)fp);
return written;
}
int main(int argc, char *argv[])
{
CURL *curl;
curl_global_init(CURL_GLOBAL_ALL);
curl=curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, argv[1]);
if((fp=fopen(argv[1],"w"))==NULL)
{
curl_easy_cleanup(curl);
exit(1);
}
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); //CURLOPT_WRITEFUNCTION 將后繼的動作交給write_data函數處理
curl_easy_perform(curl);
curl_easy_cleanup(curl);
exit(0);
}
#include <stdio.h>;
#include <stdlib.h>;
#include <unistd.h>;
#include <curl/curl.h>;
#include <curl/types.h>;
#include <curl/easy.h>;
FILE *fp; //定義FILE類型指針
size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) //這個函數是為了符合CURLOPT_WRITEFUNCTION, 而構造的
{
int written = fwrite(ptr, size, nmemb, (FILE *)fp);
return written;
}
int main(int argc, char *argv[])
{
CURL *curl;
curl_global_init(CURL_GLOBAL_ALL);
curl=curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, argv[1]);
if((fp=fopen(argv[1],"w"))==NULL)
{
curl_easy_cleanup(curl);
exit(1);
}
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); //CURLOPT_WRITEFUNCTION 將后繼的動作交給write_data函數處理
curl_easy_perform(curl);
curl_easy_cleanup(curl);
exit(0);
}
把獲得的html代碼存入相應的文件中.看下面一個例子
view plaincopy to clipboardprint?
#include <stdio.h>;
#include <stdlib.h>;
#include <unistd.h>;
#include <curl/curl.h>;
#include <curl/types.h>;
#include <curl/easy.h>;
FILE *fp; //定義FILE類型指針
size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) //這個函數是為了符合CURLOPT_WRITEFUNCTION, 而構造的
{
int written = fwrite(ptr, size, nmemb, (FILE *)fp);
return written;
}
int main(int argc, char *argv[])
{
CURL *curl;
curl_global_init(CURL_GLOBAL_ALL);
curl=curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, argv[1]);
if((fp=fopen(argv[1],"w"))==NULL)
{
curl_easy_cleanup(curl);
exit(1);
}
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); //CURLOPT_WRITEFUNCTION 將后繼的動作交給write_data函數處理
curl_easy_perform(curl);
curl_easy_cleanup(curl);
exit(0);
}
#include <stdio.h>;
#include <stdlib.h>;
#include <unistd.h>;
#include <curl/curl.h>;
#include <curl/types.h>;
#include <curl/easy.h>;
FILE *fp; //定義FILE類型指針
size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) //這個函數是為了符合CURLOPT_WRITEFUNCTION, 而構造的
{
int written = fwrite(ptr, size, nmemb, (FILE *)fp);
return written;
}
int main(int argc, char *argv[])
{
CURL *curl;
curl_global_init(CURL_GLOBAL_ALL);
curl=curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, argv[1]);
if((fp=fopen(argv[1],"w"))==NULL)
{
curl_easy_cleanup(curl);
exit(1);
}
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); //CURLOPT_WRITEFUNCTION 將后繼的動作交給write_data函數處理
curl_easy_perform(curl);
curl_easy_cleanup(curl);
exit(0);
}
從ftp站點下載文件的例子.
view plaincopy to clipboardprint?
#include <stdio.h>;
#include <curl/curl.h>;
#include <curl/types.h>;
#include <curl/easy.h>;
struct FtpFile //定義一個結構為了傳遞給my_fwrite函數.可用curl_easy_setopt的CURLOPT_WRITEDATA選項傳遞
{
char *filename;
FILE *stream;
};
int my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
{
struct FtpFile *out=(struct FtpFile *)stream; // stream指針其實就是 指向struct FtpFile ftpfile的
if(out && !out->stream)
{
out->;stream=fopen(out->filename, "wb"); //沒有這個流的話就創建一個 名字是out->filename.
if(!out->stream)
return -1;
}
return fwrite(buffer, size, nmemb, out->stream);
}
int main(int argc, char *argv[])
{
CURL *curl;
CURLcode res;
struct FtpFile ftpfile={argv[2],NULL}; //初始化一個FtpFile結構
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_URL,argv[1]);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile); //給相關函數的第四個參數 傳遞一個結構體的指針
curl_easy_setopt(curl, CURLOPT_VERBOSE, TRUE); //CURLOPT_VERBOSE 這個選項很常用 用來在屏幕上顯示對服務器相關操作返回的信息
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if(CURLE_OK != res)
fprintf(stderr, "curl told us %d\n", res);
}
if(ftpfile.stream)
fclose(ftpfile.stream);
curl_global_cleanup();
return 0;
}
#include <stdio.h>;
#include <curl/curl.h>;
#include <curl/types.h>;
#include <curl/easy.h>;
struct FtpFile //定義一個結構為了傳遞給my_fwrite函數.可用curl_easy_setopt的CURLOPT_WRITEDATA選項傳遞
{
char *filename;
FILE *stream;
};
int my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
{
struct FtpFile *out=(struct FtpFile *)stream; // stream指針其實就是 指向struct FtpFile ftpfile的
if(out && !out->stream)
{
out->;stream=fopen(out->filename, "wb"); //沒有這個流的話就創建一個 名字是out->filename.
if(!out->stream)
return -1;
}
return fwrite(buffer, size, nmemb, out->stream);
}
int main(int argc, char *argv[])
{
CURL *curl;
CURLcode res;
struct FtpFile ftpfile={argv[2],NULL}; //初始化一個FtpFile結構
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_URL,argv[1]);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile); //給相關函數的第四個參數 傳遞一個結構體的指針
curl_easy_setopt(curl, CURLOPT_VERBOSE, TRUE); //CURLOPT_VERBOSE 這個選項很常用 用來在屏幕上顯示對服務器相關操作返回的信息
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if(CURLE_OK != res)
fprintf(stderr, "curl told us %d\n", res);
}
if(ftpfile.stream)
fclose(ftpfile.stream);
curl_global_cleanup();
return 0;
}
進度條實例??顯示文件下載進度
view plaincopy to clipboardprint?
// 采用CURLOPT_NOPROGRESS, CURLOPT_PROGRESSFUNCTION CURLOPT_PROGRESSDATA 實現文件傳輸進度提示功能
//函數采用了gtk庫,故編譯時需指定gtk庫
//函數啟動專門的線程用於顯示gtk 進度條bar
#include <stdio.h>
#include <gtk/gtk.h>
#include <curl/curl.h>
#include <curl/types.h> /* new for v7 */
#include <curl/easy.h> /* new for v7 */
GtkWidget *Bar;
////這個函數是為了符合CURLOPT_WRITEFUNCTION而構造的
//完成數據保存功能
size_t my_write_func(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
return fwrite(ptr, size, nmemb, stream);
}
//這個函數是為了符合CURLOPT_READFUNCTION而構造的
//數據上傳時使用
size_t my_read_func(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
return fread(ptr, size, nmemb, stream);
}
//這個函數是為了符合CURLOPT_PROGRESSFUNCTION而構造的
//顯示文件傳輸進度,t代表文件大小,d代表傳輸已經完成部分
int my_progress_func(GtkWidget *bar,
double t, /* dltotal */
double d, /* dlnow */
double ultotal,
double ulnow)
{
/* printf("%d / %d (%g %%)\n", d, t, d*100.0/t);*/
gdk_threads_enter();
gtk_progress_set_value(GTK_PROGRESS(bar), d*100.0/t);
gdk_threads_leave();
return 0;
}
void *my_thread(void *ptr)
{
CURL *curl;
CURLcode res;
FILE *outfile;
gchar *url = ptr;
curl = curl_easy_init();
if(curl)
{
outfile = fopen("test.curl", "w");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_write_func);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_read_func);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, my_progress_func);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, Bar);
res = curl_easy_perform(curl);
fclose(outfile);
/* always cleanup */
curl_easy_cleanup(curl);
}
return NULL;
}
【Curl (libcurl) 開發 】Cocos2dx之libcurl(curl_easy)的編程教程(幫助手冊)! | 黑米GameDev街區 - Google Chrome (2013/5/23 14:56:10)
本篇介紹使用libcurl編程的一般原則和一些基本方法。本文主要是介紹 c 語言的調用接口,同時也可能很好的適用於其他類 c 語言的接口。
libcurl庫背后的開發人員投入了相當大的努力確保libcurl可以在很多不同的系統和環境里工作。
程序必須初始化一些libcurl的全局函數。這意味着不管你准備使用libcurl多少次,你都應該,且只初始化一次。當你的程序開始的時候,使用
curl_global_init()
這個函數需要一個參數來告訴它如何來初始化。使用CURL_GLOBAL_ALL ,它將用通常是比較好的默認參數來初始化所有已知的內部子模塊。還有兩個可選值:
CURL_GLOBAL_WIN32
這個參數只被用在windows 操作系統上。它讓libcurl初始化win32套接字的的東西。沒有正確的初始化,你的程序將不能正確的使用套接字。你應該只為每個程序做一次這樣的操作,如果你的程序的其他庫這樣了,你就不要再讓libcurl這樣做。
CURL_GLOBAL_SSL
這個參數會使libcurl具有SSL的能力。你應該只為每個程序做一次這樣的操作,如果你的程序的其他庫這樣了,你就不要再讓libcurl這樣做。
libcurl有個默認的保護機制,檢測如果curl_global_init沒有在curl_easy_perform之前被調用,那么libcurl會猜測該使用的初始化模式來執行程序。請注意,依靠默認的保護機制來這么做一點都不好。
當程序不再使用libcurl,請調用curl_global_cleanup函數來對應初始化函數所做的工作,它會做逆向的工作來清理curl_global_init所初始化的資源。
請避免重復的調用curl_global_init和 curl_global_cleanup。他們每個僅被調用一次。
確定libcurl所提供功能的最佳辦法是在運行的時候而不是在編譯的時候。通過調用curl_version_info函數,然后查看返回信息的結構體,你知道當前的libcurl版本所支持的功能了。
首先介紹libcurl的簡單接口(easy interface),這些接口都有curl_easy的前綴。 libcurl的最近版本還提供了復雜接口(multi interface)。更多關系該接口的信息將另作討論,為了更好的理解復雜接口,你仍然需要先了解簡單接口。
為了使用簡單接口,你先需要創建一個簡單接口的句柄。每一個簡單接口的數據通信都需要這個句柄。一般來說,你需要為每個准備傳輸數據的線程使用一個句柄。但你絕不能在多線程里共享相同的句柄。
獲取簡單句柄
easyhandle = curl_easy_init();
這個函數返回一個簡單接口句柄。接下來的操作力,可以在這個`easyhandle設置各種選項。在隨后的一個或者多次數據傳輸中,句柄只是一個邏輯實體。 通過 curl_easy_setopt設置句柄的屬性和選項,它們控制隨后的數據傳輸。這些屬性和選項的設置一個保存在句柄里直到它再次被設置為其他值。多次網絡請求使用相同的句柄,它們的句柄選擇和屬性也是相同的。
很多用於設置libcurl屬性都是字符串,一個指向一段以0結尾數據。當你用字符串設置 curl_easy_setopt(),libcurl會復制這個字符串的一個副本,所以你在設置后不用再保存那個字符串的內存。
在句柄上最經常設置的屬性是URL。你可以這樣設置它
curl_easy_setopt(handle, CURLOPT_URL, ”http://domain.com/”);
假如你希望得到指定URL上的遠程主機的數據資源到本地。如果你想自己處理得到的數據而不是直接顯示在標准輸出里,你可以寫一個符合下面原型的函數
size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp);
你可以用類似下面這樣的代碼來控制libcurl將得到的數據傳遞到你寫的函數里
curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, write_data);
你還可以控制你的回調函數第四個參數得到的數據,用這樣的函數原型
curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, &internal_struct);
使用這種原型,你可以很容易在你程序和被libcurl調用函數之間傳遞本地數據。使用 CURLOPT_WRITEDATA,libcurl不會處理你所傳遞的數據。
假如你沒用使用CURLOPT_WRITEFUNCTION設置回調函數,libcurl會有默認的處理。它只是把接收到的數據輸出到標准輸出里。你可以使用傳遞一個FILE *的打開文件參數,設置默認處理函數CURLOPT_WRITEDATA ,它把得到的數據存放在一個文件里。
這里有一個與平台有關的缺陷。有時候libcurl不能操作一個唄程序所打開的文件。如果你用CURLOPT_WRITEDATA給默認的回調函數傳遞一個打開的文件,程序可能崩潰。(CURLOPT_WRITEDATA 原來的名稱為CURLOPT_FILE,它們是同樣的工作機理)。
如果你以 win32 dll的方式使用libcurl,如果你設置了,CURLOPT_WRITEDATA ,也必須用CURLOPT_WRITEFUNCTION ,否則你會遇到程序崩潰。 還有其他很多的選項可以設置,我們放在后面再詳細討論,接着往下看
success = curl_easy_perform(easyhandle);
curl_easy_perform函數將會連接遠程的站點,發送必要的命令和接受傳輸的數據。當它收到了數據,它就會調用我們先前設置的回調函數。這個函數可能一次得到一個字節或者幾千個字節。libcurl會盡可能多的,盡可能快的傳回數據。我們回調函數返回它所得到數據的大小。如果返回的數據大小與傳遞給它數據大小不一致,libcurl將會終止操作,並返回一個錯誤代碼。
當數據傳輸完成,curl_easy_perform返回一個代碼來告訴你是否成功。如果你僅返回一個代碼還不夠,你可以使用CURLOPT_ERRORBUFFER ,讓libcurl緩存許多可讀的錯誤信息
如果你還想傳輸其他數據,已有的句柄可以多次使用。記住,鼓勵你使用現有的句柄來傳輸其他數據,libcurl會嘗試已經先前已經建立好的連接。
對於某些協議,下載文件可能涉及到很多復雜的協議用來記錄信息,設置傳輸模式,更改當前的目錄並最終傳遞數據。傳遞一個文件的URL,libcurl會為你掌管所有細節,把文件從一台機器移動到兩一台機器。
最基本的原則是絕對不要同時在多個線程之間共享一個libcurl的句柄。確保任何時候一個句柄只是在一個線程里使用。你可以在多個線程之間傳遞句柄,但是你不能使用。
libcurl是線程安全的,除了以下兩種情況:信號量和SSL/TLS句柄。信號量用於超時失效名字解析(在域名解析的時候)。
如果你通過多線程方式來訪問HTTPS 或者 FTPS 網址,你可以使用底層的SSL庫,多線程庫。這些庫可能有它們獨有的要求,你要多加注意詳細參考
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 它宣稱是線程安全,沒有任何特殊的要求。 PolarSSL 未知。yassl 未知。axTLS 未知。
當你使用多線程的時候,你應當為所有的句柄設置CURLOPT_NOSIGNAL 選項。所有的時候都可以工作正常除了DNS查詢超時的時候。 同樣,CURLOPT_DNS_USE_GLOBAL_CACHE 也不是線程安全的。
總是有各種原因導致網絡傳輸失敗。你可能設置錯誤的libcurl選項,誤解了libcurl某些選項的實際作用,或者遠程服務器返回libcurl一個非標准的應答。
當發生錯誤的時候,這里有一個黃金法:設置CURLOPT_VERBOSE 選項為1。這將導致libcurl顯示出所有發送的實體協議的細節,或者還有一些內部的信息和一些收到協議的數據(尤其是 FTP)。如果你使用HTTP ,CURLOPT_HEADER設為1,請求頭/響應頭也會被輸出,這些頭信息將出現在消息的內容中。
如果CURLOPT_VERBOSE 還不夠,你設置CURLOPT_DEBUGFUNCTION來調試你的數據。
libcurl盡量保持與協議無關性,就是上傳文件到遠程的FTP跟用PUT方式上傳數據到HTTP服務器和非常類似的。 我們寫一個程序,很可能想libcurl按照我們的要求上傳數據。我需要設置如下函數原型的讀數據的回調函數
size_t function(char *bufptr, size_t size, size_t nitems, void *userp);
bufptr 參數指向一段准備上傳數據的緩沖區,nitem是這段緩沖區的大小,userp是一個用戶自定義指針,libcurl不對該指針作任何操作,它只是簡單的傳遞該指針。可以使用該指針在應用程序與libcurl之間傳遞信息。
curl_easy_setopt(easyhandle, CURLOPT_READFUNCTION, read_function);
curl_easy_setopt(easyhandle, CURLOPT_READDATA, &filedata);
Tell libcurl that we want to upload:
curl_easy_setopt(easyhandle, CURLOPT_UPLOAD, 1L);
有幾個協議將不能正常工作當上傳的時候沒有告訴上傳文件的大小。所以設置上傳文件的大小請使用CURLOPT_INFILESIZE_LARGE
/* in this example, file_size must be an curl_off_t variable */
curl_easy_setopt(easyhandle, CURLOPT_INFILESIZE_LARGE, file_size);
當你調用curl_easy_perform()的時候,libcurl會執行所有的必要動作,當開始上傳的時候,它會調用我的回調函數。程序會盡可能多,盡可能快的的上傳數據。回調函數返回寫入緩沖區的數據的大小。返回0的時候就表示上傳結束了。
許多協議要求用戶名和密碼才能下載或者上傳你所選擇的數據。libcurl提供了指定的幾種方法。
許多協議都支持用戶名和密碼包含在指定的URL里。libcurl會檢測並相應的使用它們。可以按照這樣的格式寫
protocol://user:password@example.com/path/
如果你的用戶名和密碼需要一些奇特的字符,你應該使用URLd編碼,像%XX,其中XX是兩位十六進制的數字。
libcurl同事也提供了一個設置各種類型密碼的選項。設置CURLOPT_USERPWD 選項如下
curl_easy_setopt(easyhandle, CURLOPT_USERPWD, ”myname:thesecret”);
在某些情況下可能會多次用到用戶名和密碼,可以使用代碼來驗證身份。libcurl提供一個CURLOPT_PROXYUSERPWD選項來實現這種功能,跟CURLOPT_USERPWD 選項很類似
curl_easy_setopt(easyhandle, CURLOPT_PROXYUSERPWD, ”myname:thesecret”);
先前的章節我們顯示了如何為需要驗證的URL設置用戶和密碼。當我們使用HTTP協議的時候,客戶端有許多不同的向服務器發送身份驗證。最基本的HTTP認證為Basic認證,它發送base64編碼的明文用戶和密碼,這不安全。 如今,libcrul支持使用Basic, Digest, NTLM, Negotiate, GSS-Negotiate and SPNEGO等方式的認證,你可以設置CURLOPT_HTTPAUTH 告訴libcurl使用那種認證。
curl_easy_setopt(easyhandle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
當你想代理服務器認證的時候,你可以使用
CURLOPT_PROXYAUTH:
curl_easy_setopt(easyhandle, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
你可以組合多種認證方式,libcurl會以合理的方式處理 curl_easy_setopt(easyhandle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST|CURLAUTH_BASIC);
為了方便起見,你還可以使用CURLAUTH_ANY,它允許libcurl使用任何想要的方法。
HTML最簡單的也是最常見的POST是使用<form>這個標簽來實現的。我們提供了一個指向數據段 指針,然后告訴licurl把它發送到遠程站點:
char *data=”name=daniel&project=curl”;
curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, data);
curl_easy_setopt(easyhandle, CURLOPT_URL, ”http://posthere.com/”);
curl_easy_perform(easyhandle); /* post away! */
是不是很簡單。當你設置了CURLOPT_POSTFIELDS選項,它會自動切換句柄來POST接下的請求。
如果你想發送二進制的數據,需要設置一下數據頭,用來知名數據的類型和數據的大小。如下
struct curl_slist *headers=NULL;
headers = curl_slist_append(headers, ”Content-Type: text/xml”);
/* post binary data */
curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, binaryptr);
/* set the size of the postfields data */
curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDSIZE, 23L);
/* pass our list of custom made headers */
curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers);
curl_easy_perform(easyhandle); /* post away! */
curl_slist_free_all(headers); /* free the header list */
還有一種是multi-part 表單提交,這被認為是一種更好的方法提交二進制的數據。libcrul同樣也支持這種方法。下面是例子:
struct curl_httppost *post=NULL;
struct curl_httppost *last=NULL;
curl_formadd(&post, &last, CURLFORM_COPYNAME, ”name”, CURLFORM_COPYCONTENTS, ”daniel”, CURLFORM_END);
curl_formadd(&post, &last, CURLFORM_COPYNAME, ”project”, CURLFORM_COPYCONTENTS, ”curl”, CURLFORM_END);
curl_formadd(&post, &last, CURLFORM_COPYNAME, ”logotype-image”, CURLFORM_FILECONTENT, ”curl.png”,
CURLFORM_END);
/* Set the form info */
curl_easy_setopt(easyhandle, CURLOPT_HTTPPOST, post);
curl_easy_perform(easyhandle); /* post away! */
/* free the post data again */
curl_formfree(post);
libcurl還允許你在每個定義的部分設置頭。下面就是例子
struct curl_slist *headers=NULL;
headers = curl_slist_append(headers, ”Content-Type: text/xml”);
curl_formadd(&post, &last, CURLFORM_COPYNAME, ”logotype-image”, CURLFORM_FILECONTENT, ”curl.xml”,
CURLFORM_CONTENTHEADER, headers, CURLFORM_END);
curl_easy_perform(easyhandle); /* post away! */
curl_formfree(post);
/* free post */
curl_slist_free_all(headers);
/* free custom header list */
所有對簡單接口句柄都是”粘的”,這些屬性會一直保持不變除非你在調用 curl_easy_perform之前改過。通過將CURLOPT_HTTPGET設為1可以使簡單接口句柄回到最原始的狀態:
curl_easy_setopt(easyhandle, CURLOPT_HTTPGET, 1L);
只設置CURLOPT_POSTFIELDS 的只為”"或者NULL是不會停止libcurl做POST請求的,這只會做提交空數據的POST。
通過將CURLOPT_NOPROCESS設置為0開啟進度支持。該選項默認值為1。對大多數應用程序,我們需要提供一個進度顯示回調。libcurl會不定期的將當前傳輸的進度通過回調函數告訴你的程序。
//回調函數原型
int progress_callback(void *clientp, double dltotal,
double dlnow, double ultotal, double ulnow);
/*通過CURLOPT_PROGRESSFUNCTION注冊該回調函數。
參數clientp是一個用戶自定義指針,應用程序通過CURLOPT_PROCESSDATA
屬性將該自定義指定傳遞給libcurl。libcurl對該參數不作任何處理,
只是簡單將其傳遞給回調函數。*/
只有一件需要特別注意的是回調函數不能使類的非靜態成員函數。如
class AClass
{
static size_t write_data(void *ptr, size_t size, size_t nmemb,
void *ourpointer)
{
/* do what you want with the data */
}
}
略過,有興趣請參考原文。
當需要發送多次請求時,應該重復使用easy handle。
每次執行完curl_easy_perform,licurl會繼續保持與服務器的連接。接下來的請求可以使用這個連接而不必創建新的連接(如果目標主機是同一個的話)。這樣可以減少網絡開銷。 即使連接被釋放了,libcurl也會緩存這些連接的會話信息,這樣下次再連接到目標主機上時,就可以使用這些信息,從而減少重新連接所需的時間。
FTP連接可能會被保存較長的時間。因為客戶端要與FTP服務器進行頻繁的命令交互。對於有訪問人數上限的FTP服務器,保持一個長連接,可以使你不需要排除等待,就直接可以與FTP服務器通信。
libcurl會緩存DNS的解析結果。
在今后的libcurl版本中,還會添加一些特性來提高數據通信的效率。 每個簡單接口句柄都會保存最近使用的幾個連接,以備重用。默認是5個。可以通過CURLOPT_MAXCONNECTS屬性來設置保存連接的數量。
如果你不想重用連接,將CURLOPT_FRESH_CONNECT屬性設置為1。這樣每次提交請求時,libcurl都會先關閉以前創建的連接,然后重新創建一個新的連接。也可以將CURLOPT_FORBID_REUSE設置為1,這樣每次執行完請求,連接就會馬上關閉。
當使用libcurl發送http請求時,它會自動添加一些http頭。我們可以通過CURLOPT_HTTPHEADER屬性手動替換、添加或刪除相應的HTTP消息頭。
例外:以POST的方式向HTTP服務器提交請求時,libcurl會設置該消息頭為”100-continue”,它要求服務器在正式處理該請求之前,返回一個”OK”消息。如果POST的數據很小,libcurl可能不會設置該消息頭。
當前越來越多的協議都構建在HTTP協議之上(如:soap),這主要歸功於HTTP的可靠性,以及被廣泛使用的代理支持(可以穿透大部分防火牆)。 這些協議的使用方式與傳統HTTP可能有很大的不同。對此,libcurl作了很好的支持。
cookie是一個鍵值對的集合,HTTP服務器發給客戶端的cookie,客戶端提交請求的時候,也會將cookie發送到服務器。服務器可以根據cookie來跟蹤用戶的會話信息。cookie有過期時間,超時后cookie就會失效。cookie有域名和路徑限制,cookie只能發給指定域名和路徑的HTTP服務器。
在libcurl中,可以通過CURLOPT_COOKIE屬性來設置發往服務器的cookie:
curl_easy_setopt(easyhandle, CURLOPT_COOKIE, ”name1=var1; name2=var2;”);
在實在的應用場景中,你可能需要保存服務器發送給你的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甚至在瀏覽器之間實現共享。
curl使用筆記 | 大魚兒@Live - Google Chrome (2013/5/22 15:34:11)
curl使用筆記
CURLOPT_HEADER:設為1,則在返回的內容里包含http header;
CURLOPT_FOLLOWLOCATION:設為0,則不會自動301,302跳轉;
*CURLOPT_INFILESIZE: 當你上傳一個文件到遠程站點,這個選項告訴PHP你上傳文件的大小。
*CURLOPT_VERBOSE: 如果你想CURL報告每一件意外的事情,設置這個選項為一個非零值。
*CURLOPT_HEADER: 如果你想把一個頭包含在輸出中,設置這個選項為一個非零值。
*CURLOPT_NOPROGRESS: 如果你不會PHP為CURL傳輸顯示一個進程條,設置這個選項為一個非零值。
注意:PHP自動設置這個選項為非零值,你應該僅僅為了調試的目的來改變這個選項。
*CURLOPT_NOBODY: 如果你不想在輸出中包含body部分,設置這個選項為一個非零值。
*CURLOPT_FAILONERROR: 如果你想讓PHP在發生錯誤(HTTP代碼返回大於等於300)時,不顯示,設置這個選項為一人非零值。默認行為是返回一個正常頁,忽略代碼。
*CURLOPT_UPLOAD: 如果你想讓PHP為上傳做准備,設置這個選項為一個非零值。
*CURLOPT_POST: 如果你想PHP去做一個正規的HTTP POST,設置這個選項為一個非零值。這個POST是普通的 application/x-www-from-urlencoded 類型,多數被HTML表單使用。
*CURLOPT_FTPLISTONLY: 設置這個選項為非零值,PHP將列出FTP的目錄名列表。
*CURLOPT_FTPAPPEND: 設置這個選項為一個非零值,PHP將應用遠程文件代替覆蓋它。
*CURLOPT_NETRC: 設置這個選項為一個非零值,PHP將在你的 ~./netrc 文件中查找你要建立連接的遠程站點的用戶名及密碼。
*CURLOPT_FOLLOWLOCATION: 設置這個選項為一個非零值(象 “Location: “)的頭,服務器會把它當做HTTP頭的一部分發送(注意這是遞歸的,PHP將發送形如 “Location: “的頭)。
*CURLOPT_PUT: 設置這個選項為一個非零值去用HTTP上傳一個文件。要上傳這個文件必須設置CURLOPT_INFILE和CURLOPT_INFILESIZE選項.
*CURLOPT_MUTE: 設置這個選項為一個非零值,PHP對於CURL函數將完全沉默。
*CURLOPT_TIMEOUT: 設置一個長整形數,作為最大延續多少秒。
*CURLOPT_LOW_SPEED_LIMIT: 設置一個長整形數,控制傳送多少字節。
*CURLOPT_LOW_SPEED_TIME: 設置一個長整形數,控制多少秒傳送CURLOPT_LOW_SPEED_LIMIT規定的字節數。
*CURLOPT_RESUME_FROM: 傳遞一個包含字節偏移地址的長整形參數,(你想轉移到的開始表單)。
*CURLOPT_SSLVERSION: 傳遞一個包含SSL版本的長參數。默認PHP將被它自己努力的確定,在更多的安全中你必須手工設置。
*CURLOPT_TIMECONDITION: 傳遞一個長參數,指定怎么處理CURLOPT_TIMEVALUE參數。你可以設置這個參數為
TIMECOND_IFMODSINCE 或 TIMECOND_ISUNMODSINCE。這僅用於HTTP。
*CURLOPT_TIMEVALUE: 傳遞一個從1970-1-1開始到現在的秒數。這個時間將被CURLOPT_TIMEVALUE選項作為指定值使用,或被默認
TIMECOND_IFMODSINCE使用。
下列選項的值將被作為字符串:
*CURLOPT_URL: 這是你想用PHP取回的URL地址。你也可以在用curl_init()函數初始化時設置這個選項。
*CURLOPT_USERPWD: 傳遞一個形如[username]:[password]風格的字符串,作用PHP去連接。
*CURLOPT_PROXYUSERPWD: 傳遞一個形如[username]:[password] 格式的字符串去連接HTTP代理。
*CURLOPT_RANGE: 傳遞一個你想指定的范圍。它應該是”X-Y”格式,X或Y是被除外的。HTTP傳送同樣支持幾個間隔,用逗句來分隔(X-Y,N-M)。
*CURLOPT_POSTFIELDS: 傳遞一個作為HTTP “POST”操作的所有數據的字符串。
*CURLOPT_REFERER: 在HTTP請求中包含一個”referer”頭的字符串。
*CURLOPT_USERAGENT: 在HTTP請求中包含一個”user-agent”頭的字符串。
*CURLOPT_FTPPORT: 傳遞一個包含被ftp “POST”指令使用的IP地址。這個POST指令告訴遠程服務器去連接我們指定的IP地址。 這個字符串可以是一個IP地址,一個主機名,一個網絡界面名(在UNIX下),或是‘-’(使用系統默認IP地址)。
*CURLOPT_COOKIE: 傳遞一個包含HTTP cookie的頭連接。
*CURLOPT_SSLCERT: 傳遞一個包含PEM格式證書的字符串。
*CURLOPT_SSLCERTPASSWD: 傳遞一個包含使用CURLOPT_SSLCERT證書必需的密碼。
*CURLOPT_COOKIEFILE: 傳遞一個包含cookie數據的文件的名字的字符串。這個cookie文件可以是Netscape格式,或是堆存在文件中的HTTP風格的頭。
*CURLOPT_CUSTOMREQUEST: 當進行HTTP請求時,傳遞一個字符被GET或HEAD使用。為進行DELETE或其它操作是有益的,更Pass a string to be used instead of GET or HEAD when doing an HTTP request. This is useful for doing or another, more obscure, HTTP request.
注意: 在確認你的服務器支持命令先不要去這樣做。
下列的選項要求一個文件描述(通過使用fopen()函數獲得):
*CURLOPT_FILE: 這個文件將是你放置傳送的輸出文件,默認是STDOUT.
*CURLOPT_INFILE: 這個文件是你傳送過來的輸入文件。
*CURLOPT_WRITEHEADER: 這個文件寫有你輸出的頭部分。
*CURLOPT_STDERR: 這個文件寫有錯誤而不是stderr。
libcurl 是一個很不錯的庫,支持http,ftp等很多的協議。使用庫最大的心得就是,不仔細看文檔,僅僅看着例子就寫程序,是一件危險的事情。我的程序崩潰了,我懷疑是自己代碼寫的問題,后來發現是庫沒用對。不仔細看文檔(有時候文檔本身也比較差勁,這時除了看仔細外,還要多動腦子,考慮它是怎么實現的),后果很嚴重。不加思索的使用別人的庫或者代碼,有時候很愜意,但是出問題時,卻是寢食難安的。
1. CURLcode curl_global_init(long flags); 在多線程應用中,需要在主線程中調用這個函數。這個函數設置libcurl所需的環境。通常情況,如果不顯式的調用它,第一次調用curl_easy_init()時,curl_easy_init 會調用 curl_global_init,在單線程環境下,這不是問題。但是多線程下就不行了,因為curl_global_init不是線程安全的。在多個線程中調用curl_easy_int,然后如果兩個線程同時發現curl_global_init還沒有被調用,同時調用curl_global_init,悲劇就發生了。這種情況發生的概率很小,但可能性是存在的。
2. libcurl 有個很好的特性,它甚至可以控制域名解析的超時。但是在默認情況下,它是使用alarm + siglongjmp 實現的。用alarm在多線程下做超時,本身就幾乎不可能。如果只是使用alarm,並不會導致程序崩潰,但是,再加上siglongjmp,就要命了(程序崩潰的很可怕,core中幾乎看不出有用信息),因為其需要一個sigjmp_buf型的全局變量,多線程修改它。(通常情況下,可以每個線程一個 sigjmp_buf 型的變量,這種情況下,多線程中使用 siglongjmp 是沒有問題的,但是libcurl只有一個全局變量,所有的線程都會用)。
具體是類似 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L) 的超時設置,導致alarm的使用(估計發生在域名解析階段),如前所述,這在多線程中是不行的。解決方式是禁用掉alarm這種超時, curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L)。
這樣,多線程中使用超時就安全了。但是域名解析就沒了超時機制,碰到很慢的域名解析,也很麻煩。文檔的建議是 Consider building libcurl with c-ares support to enable asynchronous DNS lookups, which enables nice timeouts for name resolves without signals. c-ares 是異步的 DNS 解決方案。
curl_slist_append() 函數聲明如下:
1
2
|
#include <curl/curl.h>
struct
curl_slist *curl_slist_append(
struct
curl_slist * list,
const
char
* string );
|
該函數將一個字符串追加到 slist 鏈表中。第 1 個參數是 struct_slist 類型鏈表;第 2 個參數是要增加到鏈表節點結構中的字符串。函數完成后,返回指向該鏈表的指針。
struct curl_slist 的類型定義在 curl.h 中:
1
2
3
4
5
|
/* linked-list structure for the CURLOPT_QUOTE option (and other) */
struct
curl_slist {
char
*data;
struct
curl_slist *next;
};
|
使用了該函數,在工作完成后應該使用 curl_slist_free_all() 對其鏈表進行釋放。
curl/types.h: No such file or directory問題的解決
發布時間: 2012-08-27 09:49 來源: blog.csdn.net
在程序中用到了libcurl,編譯時出現
curl/types.h: No such file or directory錯誤
查找到http://projects.springlobby.info/issues/1575中提到了這個問題
This is a problem on archlinux which just got curl 7.21.7.curl devs removed curl/types.h and put its content in curl/curl.h.
意思是在新版中把curl/types.h合並到了curl/curl.h中
我的解決方法是在
- cd /usr/include/curl/
- sudo ln -s curl.h types.h
再編譯,問題沒有了。
linux curl命令詳解 - - ITeye技術網站 - Google Chrome (2013/5/22 15:31:00)
個人技術博客:http://demi-panda.com
一、參數詳解
-M/--manual | 顯示全手動 |
-n/--netrc | 從netrc文件中讀取用戶名和密碼 |
--netrc-optional | 使用 .netrc 或者 URL來覆蓋-n |
--ntlm | 使用 HTTP NTLM 身份驗證 |
-N/--no-buffer | 禁用緩沖輸出 |
-o/--output | 把輸出寫到該文件中 |
-O/--remote-name | 把輸出寫到該文件中,保留遠程文件的文件名 |
-p/--proxytunnel | 使用HTTP代理 |
--proxy-anyauth | 選擇任一代理身份驗證方法 |
--proxy-basic | 在代理上使用基本身份驗證 |
--proxy-digest | 在代理上使用數字身份驗證 |
--proxy-ntlm | 在代理上使用ntlm身份驗證 |
-P/--ftp-port <address> | 使用端口地址,而不是使用PASV |
-Q/--quote <cmd> | 文件傳輸前,發送命令到服務器 |
-r/--range <range> | 檢索來自HTTP/1.1或FTP服務器字節范圍 |
--range-file | 讀取(SSL)的隨機文件 |
-R/--remote-time | 在本地生成文件時,保留遠程文件時間 |
--retry <num> | 傳輸出現問題時,重試的次數 |
--retry-delay <seconds> | 傳輸出現問題時,設置重試間隔時間 |
--retry-max-time <seconds> | 傳輸出現問題時,設置最大重試時間 |
-s/--silent | 靜音模式。不輸出任何東西 |
-S/--show-error | 顯示錯誤 |
--socks4 <host[:port]> | 用socks4代理給定主機和端口 |
--socks5 <host[:port]> | 用socks5代理給定主機和端口 |
-t/--telnet-option <OPT=val> | Telnet選項設置 |
--trace <file> | 對指定文件進行debug |
--trace-ascii <file> Like | 跟蹤但沒有hex輸出 |
--trace-time | 跟蹤/詳細輸出時,添加時間戳 |
-T/--upload-file <file> | 上傳文件 |
--url <URL> | Spet URL to work with |
-u/--user <user[:password]> | 設置服務器的用戶和密碼 |
-U/--proxy-user <user[:password]> | 設置代理用戶名和密碼 |
-V/--version | 顯示版本信息 |
-w/--write-out [format] | 什么輸出完成后 |
-x/--proxy <host[:port]> | 在給定的端口上使用HTTP代理 |
-X/--request <command> | 指定什么命令 |
-y/--speed-time | 放棄限速所要的時間。默認為30 |
-Y/--speed-limit | 停止傳輸速度的限制,速度時間'秒 |
-z/--time-cond | 傳送時間設置 |
-0/--http1.0 | 使用HTTP 1.0 |
-1/--tlsv1 | 使用TLSv1(SSL) |
-2/--sslv2 | 使用SSLv2的(SSL) |
-3/--sslv3 | 使用的SSLv3(SSL) |
--3p-quote | like -Q for the source URL for 3rd party transfer |
--3p-url | 使用url,進行第三方傳送 |
--3p-user | 使用用戶名和密碼,進行第三方傳送 |
-4/--ipv4 | 使用IP4 |
-6/--ipv6 | 使用IP6 |
-#/--progress-bar | 用進度條顯示當前的傳送狀態 |
二,常用curl實例
1,抓取頁面內容到一個文件中
[root@10.10.90.97 ~]# curl -o home.html http://www.sina.com.cn 2,用-O(大寫的),后面的url要具體到某個文件,不然抓不下來。我們還可以用正則來抓取東西 [root@10.10.90.97 ~]# curl -O http://www.it415.com/czxt/linux/25002_3.html 3,模擬表單信息,模擬登錄,保存cookie信息 [root@10.10.90.97 ~]# curl -c ./cookie_c.txt -F log=aaaa -F pwd=****** http://blog.51yip.com/wp-login.php 4,模擬表單信息,模擬登錄,保存頭信息 [root@10.10.90.97 ~]# curl -D ./cookie_D.txt -F log=aaaa -F pwd=****** http://blog.51yip.com/wp-login.php -c(小寫)產生的cookie和-D里面的cookie是不一樣的。
5,使用cookie文件
[root@10.10.90.97 ~]# curl -b ./cookie_c.txt http://blog.51yip.com/wp-admin 7,傳送數據,最好用登錄頁面測試,因為你傳值過去后,curl回抓數據,你可以看到你傳值有沒有成功 [root@10.10.90.97 ~]# curl -d log=aaaa http://blog.51yip.com/wp-login.php 8,顯示抓取錯誤 [root@10.10.90.97 ~]# curl -f http://www.sina.com.cn/asdf curl: (22) The requested URL returned error: 404 [root@10.10.90.97 ~]# curl http://www.sina.com.cn/asdf <HTML><HEAD><TITLE>404,not found</TITLE> 。。。。。。。。。。。。
10,當我們經常用curl去搞人家東西的時候,人家會把你的IP給屏蔽掉的,這個時候,我們可以用代理
[root@10.10.90.97 ~]# curl -x 10.10.90.83:80 -o home.html http://www.sina.com.cn
11,比較大的東西,我們可以分段下載
[root@10.10.90.97 ~]# curl -r 0-100 -o img.part1 http://i2.f.itc.cn/thumb/180/bj/6018/b_60178154.jpg % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 101 100 101 0 0 1926 0 --:--:-- --:--:-- --:--:-- 0 [root@10.10.90.97 ~]# curl -r 100-200 -o img.part2 http://i2.f.itc.cn/thumb/180/bj/6018/b_60178154.jpg % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 101 100 101 0 0 3498 0 --:--:-- --:--:-- --:--:-- 98k [root@10.10.90.97 ~]# curl -r 200- -o img.part3 http://i2.f.itc.cn/thumb/180/bj/6018/b_60178154.jpg % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 13515 100 13515 0 0 154k 0 --:--:-- --:--:-- --:--:-- 280k [root@10.10.90.97 ~]# ll |grep img.part -rw-r--r-- 1 root root 101 Jan 24 10:59 img.part1 -rw-r--r-- 1 root root 101 Jan 24 11:00 img.part2 -rw-r--r-- 1 root root 13515 Jan 24 11:00 img.part3 用的時候,把他們cat一下就OK了,cat img.part* >img.jpg
12,不顯示下載進度信息
[root@10.10.90.97 ~]# curl -s -o aaa.jpg
13,顯示下載進度條
[root@10.10.90.97 ~]# curl -# -O http://www.it415.com/czxt/linux/25002_3.html ######################################################################## 100.0%
14,通過ftp下載文件
[root@10.10.90.97 ~]# curl -u 用戶名:密碼 -O http://blog.51yip.com/demo/curtain/bbstudy_files/style.css % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 101 1934 101 1934 0 0 3184 0 --:--:-- --:--:-- --:--:-- 7136 或者用下面的方式 [root@10.10.90.97 ~]# curl -O ftp://xukai:test@192.168.242.144:21/www/focus/enhouse/index.php % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 87518 100 87518 0 0 2312k 0 --:--:-- --:--:-- --:--:-- 11.5M
15,通過ftp上傳 [root@10.10.90.97 ~]# curl -T xukai.php ftp://xukai:test@192.168.242.144:21/www/focus/enhouse/ % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 87518 0 0 100 87518 0 2040k --:--:-- --:--:-- --:--:-- 8901k |