1. 程式人生 > >libcurl完全教程(注意:是,完全教程)

libcurl完全教程(注意:是,完全教程)

本文也是翻譯官網的原文的,有些翻譯不過來我的就用原文顯示了

  • 本文嘗試描述使用libcurl程式設計時的一般原則和一些基本方法,文字將主要注意力集中在C介面。

  • 此文件將引用’the user‘作為編寫使用libcurl的原始碼的人。 這可能是你或你的位置的人。

  • 通常稱’the program‘的是您收集的使用libcurl進行傳輸的原始碼。 程式在libcurl之外,libcurl在程式之外。

  • 要獲得此處描述的所有選項和功能的更多詳細資訊,請參閱其各自的手冊頁。

Building(生成)

There are many different ways to build C programs. This chapter will assume a Unix style build process. If you use a different build system, you can still read this to get general information that may apply to your environment as well.

有許多的方法來生成C語言的程式, 本章將假設一個Unix風格的生成過程。 如果使用不同的構建系統,您仍然可以閱讀此資訊,以獲取可能適用於您的環境的一般資訊。

(譯者注:用C語言從編寫原始碼,到執行一個程式的過程,可以概括為: 編寫原始碼(edit)-> 編譯(compile)-> 連線,也叫生成(build)-> 執行(execute))

Compiling the Program(編譯程式)

  • Your compiler needs to know where the libcurl headers are located. Therefore you must set your compiler’s include path to point to the directory where you installed them. The ‘curl-config’[3] tool can be used to get this information:

  • 你的編譯器需要知道libcurl頭在哪裡。 因此,必須將編譯器的include路徑設定為指向安裝它們的目錄。 curl-config[3]工具可用於獲取此資訊:

$ curl-config --cflags 

Linking the Program with libcurl(連結程式到libcurl)

  • When having compiled the program, you need to link your object files to create a single executable. For that to succeed, you need to link with libcurl and possibly also with other libraries that libcurl itself depends on. Like the OpenSSL libraries, but even some standard OS libraries may be needed on the command line. To figure out which flags to use, once again the ‘curl-config’ tool comes to the rescue:

  • 編譯程式時,需要連結目標檔案以建立單個可執行檔案。 為了成功,你需要連結到libcurl,也可能與libcurl本身依賴的其他庫。 像OpenSSL庫一樣,但在命令列上甚至可能需要一些標準的作業系統庫。 要找出要使用的標誌,curl-config工具再次拯救:

$ curl-config --libs 

SSL or Not(使用SSL或者不)

  • libcurl can be built and customized in many ways. One of the things that varies from different libraries and builds is the support for SSL-based transfers, like HTTPS and FTPS. If a supported SSL library was detected properly at build-time, libcurl will be built with SSL support. To figure out if an installed libcurl has been built with SSL support enabled, use ‘curl-config’ like this:

  • libcurl可以在許多方面構建和定製。 從不同的庫和構建不同的事情之一是支援基於SSL的資料傳輸,如HTTPSFTPS。 如果在構建時正確檢測到受支援的SSL庫,libcurl將使用SSL支援構建。 要確定是否已安裝libcurl已啟用SSL支援,請使用curl-config這樣:

$ curl-config --feature
  • And if SSL is supported, the keyword ‘SSL’ will be written to stdout, possibly together with a few other features that could be either on or off on for different libcurls.

  • See also the “Features libcurl Provides” further down.

  • 如果支援SSL,關鍵字SSL將被寫入stdout,可能有一些其他功能可以開啟或關閉不同的libcurls

  • 另見下面的 特性libcurl提供。

autoconf macro(autoconf 巨集 %只供參考%)

  • When you write your configure script to detect libcurl and setup variables accordingly, we offer a prewritten macro that probably does everything you need in this area. See docs/libcurl/libcurl.m4 file - it includes docs on how to use it.

  • 當您編寫配置指令碼以相應地檢測libcurl和設定變數時,我們提供一個預寫的巨集,可以做你可能在這一領域需要的一切。 請參閱docs/libcurl/libcurl.m4檔案 - 包括如何使用它的文件。

Portable Code in a Portable World(可移植的程式碼在可移植的世界)

The people behind libcurl have put a considerable effort to make libcurl work on a large amount of different operating systems and environments.

You program libcurl the same way on all platforms that libcurl runs on. There are only very few minor considerations that differ. If you just make sure to write your code portable enough, you may very well create yourself a very portable program. libcurl shouldn’t stop you from that.

libcurl背後的人已經付出了相當大的努力,使得libcurl可以在大量不同的作業系統和環境中工作。

你可以以相同的方式寫含有libcurl庫的程式碼在不同的平臺上,然後libcurl可以執行。 只有很少的機率你需要考慮平臺之間的不同。 只有你已經確保你的其他程式碼可移植性足夠,libcurl不會成為阻止你的程式碼,你可以很好地建立自己的一個可移植程式。

Global Preparation(全面的準備 %只供參考%)

  • The program must initialize some of the libcurl functionality globally. That means it should be done exactly once, no matter how many times you intend to use the library. Once for your program’s entire life time. This is done using

  • 程式必須在全域性上初始化一些libcurl功能。 這意味著它應該或者只應該完成一次,無論你打算使用lib多少次。 一次為你的程式的整個生命週期。 這是通過使用以下命令:

 curl_global_init()
  • and it takes one parameter which is a bit pattern that tells libcurl what to initialize. Using CURL_GLOBAL_ALL will make it initialize all known internal sub modules, and might be a good default option. The current two bits that are specified are:

  • 它需要一個引數,這是一個位模式告訴libcurl怎麼初始化。 使用CURL_GLOBAL_ALL將使它初始化所有已知的內部子模組,這可能是一個好的預設選項(意思就是你最好用這個模組初始化所有的lib除非你真正知道你需要和初始化什麼模組)。 指定的當前兩位為:

CURL_GLOBAL_WIN32

  • which only does anything on Windows machines. When used on a Windows machine, it’ll make libcurl initialize the win32 socket stuff. Without having that initialized properly, your program cannot use sockets properly. You should only do this once for each application, so if your program already does this or of another library in use does it, you should not tell libcurl to do this as well.

  • 它只在Windows機器上做任何事情(就是這個函式它是用在Windows機器上的,如果你在Windows上寫程式碼並編譯就用它)。 當在Windows機器上使用時,它會使libcurl初始化win32套接字。 沒有正確初始化,你的程式不能正確使用套接字。 你應該只為每個程式初始化一次,所以如果你的程式已經做到這一點或另一個庫在使用它,你就不應該再讓libcurl這樣做了。

CURL_GLOBAL_SSL

  • which only does anything on libcurls compiled and built SSL-enabled. On these systems, this will make libcurl initialize the SSL library properly for this application. This only needs to be done once for each application so if your program or another library already does this, this bit should not be needed.

  • 它只對libcurls編譯和構建啟用SSL的任何內容。 在這些系統上,這將使libcurl為此應用程式正確初始化SSL庫。 這隻需要為每個應用程式執行一次,如果您的程式或另一個庫已經這樣做,這步驟可以不要。

  • libcurl has a default protection mechanism that detects if curl_global_init hasn’t been called by the time curl_easy_perform is called and if that is the case, libcurl runs the function itself with a guessed bit pattern. Please note that depending solely on this is not considered nice nor very good.

  • libcurl有一個預設保護機制,用於檢測curl_easy_perform是否被呼叫,如果curl_global_init沒有被呼叫,如果是這種情況,libcurl將以猜測的位模式執行函式本身。 請注意,這不被認為不錯也不不被認為是很好。

  • When the program no longer uses libcurl, it should call curl_global_cleanup, which is the opposite of the init call. It will then do the reversed operations to cleanup the resources the curl_global_init call initialized.

  • 當程式不再使用libcurl時,它應該呼叫curl_global_cleanup,這是init呼叫的相反。 然後它將執行反向操作來清除curl_global_init呼叫初始化的資源。

  • Repeated calls to curl_global_init and curl_global_cleanup should be avoided. They should only be called once each.

  • 應避免重複呼叫curl_global_init和curl_global_cleanup。 它們應該只被呼叫一次。

Features libcurl Provides(libcurl所提供的特性)

It is considered best-practice to determine libcurl features at run-time rather than at build-time (if possible of course). By calling curl_version_info and checking out the details of the returned struct, your program can figure out exactly what the currently running libcurl supports.

最好的做法是在執行時而不是在構建時確定libcurl特性(如果可能的話)。 通過呼叫curl_version_info並檢查返回的結構的詳細資訊,您的程式可以確定當前正在執行的libcurl支援什麼。

Two Interfaces(兩個介面)

libcurl first introduced the so called easy interface. All operations in the easy interface are prefixed with curl_easy. The easy interface lets you do single transfers with a synchronous and blocking function call.

libcurl首先介紹了所謂的easy interfaceeasy interface中的所有操作都以curl_easy作為字首。 easy interface允許您使用同步和阻塞函式呼叫進行單次傳輸。

libcurl also offers another interface that allows multiple simultaneous transfers in a single thread, the so called multi interface. More about that interface is detailed in a separate chapter further down. You still need to understand the easy interface first, so please continue reading for better understanding.

libcurl還提供了另一個介面,允許在單個執行緒中進行多個同時傳輸,即所謂的多介面(multi interface)。 關於該介面的更多內容將在下面單獨的章節中詳細說明。 您仍然需要先了解easy interface,因此請繼續閱讀以便更好地瞭解。

Handle the Easy libcurl(處理簡單libcurl)

To use the easy interface, you must first create yourself an easy handle. You need one handle for each easy session you want to perform. Basically, you should use one handle for every thread you plan to use for transferring. You must never share the same handle in multiple threads.

Get an easy handle with

要使用easy interface,您必須首先建立一個簡單的控制代碼(easy handle)。 你需要為每個簡單的會話你想要執行一個控制代碼。 基本上,您應該為計劃用於傳輸的每個執行緒使用一個控制代碼。 您不能在多個執行緒中共享相同的控制代碼(譯者注:這點你想共享handle控制代碼的話,可以檢視

easyhandle = curl_easy_init();

It returns an easy handle. Using that you proceed to the next step: setting up your preferred actions. A handle is just a logic entity for the upcoming transfer or series of transfers.

它返回一個容易的控制代碼。 使用它,繼續下一步:設定首選操作。 控制代碼只是用於即將到來的傳送或一系列傳送的邏輯實體。

You set properties and options for this handle using curl_easy_setopt. They control how the subsequent transfer or transfers will be made. Options remain set in the handle until set again to something different. They are sticky. Multiple requests using the same handle will use the same options.

您可以使用curl_easy_setopt為此控制代碼設定屬性和選項。 他們控制如何進行後續轉移。 選項保持設定在控制代碼中,直到設定為不同的東西。 他們是粘的。 使用相同控制代碼的多個請求將使用相同的選項。

If you at any point would like to blank all previously set options for a single easy handle, you can call curl_easy_reset and you can also make a clone of an easy handle (with all its set options) using curl_easy_duphandle.

如果你在任何時候想要為一個簡單的控制代碼清除所有以前設定的選項,你可以呼叫curl_easy_reset,你也可以使用curl_easy_duphandle克隆一個簡單控制代碼(所有的設定選項)。

Many of the options you set in libcurl are “strings”, pointers to data terminated with a zero byte. When you set strings with curl_easy_setopt, libcurl makes its own copy so that they don’t need to be kept around in your application after being set[4].

libcurl中設定的許多選項是”strings”(字串),指向以零位元組終止的資料的指標。 當你使用curl_easy_setopt設定字串時,libcurl會建立自己的副本,因此在設定之後,它們不需要儲存在應用程式中。

One of the most basic properties to set in the handle is the URL. You set your preferred URL to transfer with CURLOPT_URL in a manner similar to:

在控制代碼中設定的最基本的屬性之一是URL。 您使用CURLOPT_URL設定要傳輸的首選網址,方法與以下類似:

curl_easy_setopt(handle, CURLOPT_URL, "http://domain.com/");

Let’s assume for a while that you want to receive data as the URL identifies a remote resource you want to get here. Since you write a sort of application that needs this transfer, I assume that you would like to get the data passed to you directly instead of simply getting it passed to stdout. So, you write your own function that matches this prototype:

讓我們假設您希望接收資料,因為URL標識了您要在此處訪問的遠端資源。 因為你寫了一個需要這種傳輸的應用程式,我假設你想直接獲得傳遞給你的資料,而不是簡單地傳遞給stdout(也就是你想自己的函式處理這些返回的資料,而不是顯示在螢幕上(stdout))。 所以,你編寫自己的函式匹配這個原型:

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

You tell libcurl to pass all data to this function by issuing a function similar to this:

你使用類似於這樣的函式告訴libcurl傳遞所有資料到這個函式(這是curl初始化設定時候做的):

curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, write_data); 

You can control what data your callback function gets in the fourth argument by setting another property:

您可以通過設定另一個屬性來控制回撥函式在<第四個引數>中獲取的資料:

 curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, &internal_struct); 

Using that property, you can easily pass local data between your application and the function that gets invoked by libcurl. libcurl itself won’t touch the data you pass with CURLOPT_WRITEDATA.

使用該屬性,您可以輕鬆地在應用程式和libcurl呼叫的函式之間傳遞本地資料。 libcurl本身不會接觸你通過CURLOPT_WRITEDATA傳遞的資料。

libcurl offers its own default internal callback that will take care of the data if you don’t set the callback with CURLOPT_WRITEFUNCTION. It will then simply output the received data to stdout. You can have the default callback write the data to a different file handle by passing a ‘FILE *’ to a file opened for writing with the CURLOPT_WRITEDATA option.

如果不使用CURLOPT_WRITEFUNCTION設定回撥,libcurl會提供自己的預設內部回撥來處理資料。 然後它將簡單地將接收到的資料輸出到stdout。 您可以通過將FILE *傳遞到使用CURLOPT_WRITEDATA選項開啟的檔案,使預設回撥將資料寫入不同的檔案控制代碼。

Now, we need to take a step back and have a deep breath. Here’s one of those rare platform-dependent nitpicks. Did you spot it? On some platforms[2], libcurl won’t be able to operate on files opened by the program. Thus, if you use the default callback and pass in an open file with CURLOPT_WRITEDATA, it will crash. You should therefore avoid this to make your program run fine virtually everywhere.

現在,我們需要退後一步,深呼吸。 這裡有一個罕見的平臺相關的nitpicks。 你找到了嗎? 在某些平臺[2]上,libcurl無法操作由程式開啟的檔案。 因此,如果您使用預設回撥並傳遞一個開啟的檔案給CURLOPT_WRITEDATA,它將崩潰。 因此,您應該避免這樣做,使您的程式幾乎無處不在執行。

(CURLOPT_WRITEDATA was formerly known as CURLOPT_FILE. Both names still work and do the same thing).

CURLOPT_WRITEDATA以前稱為CURLOPT_FILE。這兩個名稱仍然工作,並做同樣的事情)。

If you’re using libcurl as a win32 DLL, you MUST use the CURLOPT_WRITEFUNCTION if you set CURLOPT_WRITEDATA - or you will experience crashes.

如果你使用libcurl就像win32的DLL一樣,你必須使用CURLOPT_WRITEFUNCTION,如果你設定CURLOPT_WRITEDATA - 或者你會遇到崩潰。

There are of course many more options you can set, and we’ll get back to a few of them later. Let’s instead continue to the actual transfer:

當然還有更多的選擇,你可以設定,我們將回到其中幾個後來。 讓我們繼續實際傳輸:

success = curl_easy_perform(easyhandle); 

curl_easy_perform will connect to the remote site, do the necessary commands and receive the transfer. Whenever it receives data, it calls the callback function we previously set. The function may get one byte at a time, or it may get many kilobytes at once. libcurl delivers as much as possible as often as possible. Your callback function should return the number of bytes it “took care of”. If that is not the exact same amount of bytes that was passed to it, libcurl will abort the operation and return with an error code.

curl_easy_perform將連線到遠端站點,執行必要的命令並接收傳輸。每當它接收資料,它呼叫我們以前設定的回撥函式。該函式可以一次獲得一個位元組,或者它可以一次獲得許多千位元組。 libcurl儘可能多地提供儘可能多的。你的回撥函式應該返回”你所關心的”的位元組數。如果這不是傳遞給它的完全相同的位元組量,libcurl將中止操作並返回一個錯誤程式碼。

When the transfer is complete, the function returns a return code that informs you if it succeeded in its mission or not. If a return code isn’t enough for you, you can use the CURLOPT_ERRORBUFFER to point libcurl to a buffer of yours where it’ll store a human readable error message as well.

傳輸完成後,函式返回一個返回碼,通知您是否成功完成任務。如果一個返回程式碼不夠,你可以使用CURLOPT_ERRORBUFFER將libcurl指向你的緩衝區,它將儲存一個人可讀的錯誤訊息。

If you then want to transfer another file, the handle is ready to be used again. Mind you, it is even preferred that you re-use an existing handle if you intend to make another transfer. libcurl will then attempt to re-use the previous connection.

如果隨後要傳輸另一個檔案,則該控制代碼已準備好再次使用。注意,如果您打算再次進行傳輸,最好重新使用現有的控制代碼。 libcurl然後將嘗試重新使用先前的連線。

For some protocols, downloading a file can involve a complicated process of logging in, setting the transfer mode, changing the current directory and finally transferring the file data. libcurl takes care of all that complication for you. Given simply the URL to a file, libcurl will take care of all the details needed to get the file moved from one machine to another.

對於一些協議,下載檔案可能涉及登入的複雜過程,設定傳送模式,改變當前目錄並最終傳送檔案資料。 libcurl照顧所有那些複雜的你。給定一個檔案的URL,libcurl將處理所有需要的細節,以使檔案從一臺機器移動到另一臺機器。

Multi-threading Issues(多執行緒問題)

libcurl is thread safe but there are a few exceptions. Refer to libcurl-thread for more information.

libcurl是執行緒安全的,但有一些例外。 有關更多資訊,請參閱libcurl-thread。

When It Doesn’t Work

There will always be times when the transfer fails for some reason. You might have set the wrong libcurl option or misunderstood what the libcurl option actually does, or the remote server might return non-standard replies that confuse the library which then confuses your program.

總有一些時候,由於某種原因傳輸失敗。你可能設定了錯誤的libcurl選項或誤解了libcurl選項實際上做了什麼,或者遠端伺服器可能返回非標準的回覆混淆庫,然後混淆你的程式。

There’s one golden rule when these things occur: set the CURLOPT_VERBOSE option to 1. It’ll cause the library to spew out the entire protocol details it sends, some internal info and some received protocol data as well (especially when using FTP). If you’re using HTTP, adding the headers in the received output to study is also a clever way to get a better understanding why the server behaves the way it does. Include headers in the normal body output with CURLOPT_HEADER set 1.

當這些事情發生時,有一個黃金規則:將CURLOPT_VERBOSE選項設定為1.它將使得庫顯示它傳送的整個協議細節,一些內部資訊和一些接收的協議資料(特別是當使用FTP時)。如果你使用HTTP,在接收到的輸出中新增頭部來學習也是一個聰明的方式來更好地瞭解伺服器為什麼會這樣做。在CURLOPT_HEADER設定為1的正常正文輸出中包括標頭。

Of course, there are bugs left. We need to know about them to be able to fix them, so we’re quite dependent on your bug reports! When you do report suspected bugs in libcurl, please include as many details as you possibly can: a protocol dump that CURLOPT_VERBOSE produces, library version, as much as possible of your code that uses libcurl, operating system name and version, compiler name and version etc.

當然,還有bug。我們需要知道他們能夠修復它們,所以我們完全依賴於你的錯誤報告!當您在libcurl中報告可疑錯誤時,請儘可能包括儘可能多的細節:CURLOPT_VERBOSE生成的協議轉儲,庫版本,儘可能多的使用libcurl的程式碼,作業系統名稱和版本,編譯器名稱和版本等等

If CURLOPT_VERBOSEis not enough, you increase the level of debug data your application receive by using the CURLOPT_DEBUGFUNCTION.

如果CURLOPT_VERBOSE不夠,您可以使用CURLOPT_DEBUGFUNCTION增加應用程式接收的除錯資料的級別。

Getting some in-depth knowledge about the protocols involved is never wrong, and if you’re trying to do funny things, you might very well understand libcurl and how to use it better if you study the appropriate RFC documents at least briefly.

獲得關於所涉及的協議的一些深入的知識是從來沒有錯,如果你想做有趣的事情,你可能很好地理解libcurl和如何使用它更好,如果你學習適當的RFC文件至少簡要。

Upload Data to a Remote Site(將資料上傳到遠端站點)

libcurl tries to keep a protocol independent approach to most transfers, thus uploading to a remote FTP site is very similar to uploading data to a HTTP server with a PUT request.

libcurl嘗試保持大多數傳輸的協議獨立的方法,因此上傳到遠端FTP站點非常類似於使用PUT請求將資料上傳到HTTP伺服器

Of course, first you either create an easy handle or you re-use one existing one. Then you set the URL to operate on just like before. This is the remote URL, that we now will upload.

當然,首先你要麼建立一個簡單的控制代碼,要麼重新使用一個現有的控制代碼。 然後你設定URL的操作就像以前一樣。 這是遠端URL,我們現在將上傳。

Since we write an application, we most likely want libcurl to get the upload data by asking us for it. To make it do that, we set the read callback and the custom pointer libcurl will pass to our read callback. The read callback should have a prototype similar to:

由於我們編寫一個應用程式,我們最有可能希望libcurl通過請求我們獲取上傳資料。 為了做到這一點,我們設定read回撥,自定義指標libcurl將傳遞給我們的read回撥。 讀回撥應該有類似的原型:

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

Where bufptr is the pointer to a buffer we fill in with data to upload and size*nitems is the size of the buffer and therefore also the maximum amount of data we can return to libcurl in this call. The userp pointer is the custom pointer we set to point to a struct of ours to pass private data between the application and the callback.

其中bufptr是指向緩衝區的指標,我們填充要上傳的資料,size * nitems是緩衝區的大小,因此也是我們可以在此呼叫中返回libcurl的最大資料量。 userp指標是我們設定為指向我們的結構體的自定義指標,以在應用程式和回撥之間傳遞私有資料。

curl_easy_setopt(easyhandle, CURLOPT_READFUNCTION, read_function);
curl_easy_setopt(easyhandle, CURLOPT_READDATA, &filedata); 

Tell libcurl that we want to upload:

告訴libcurl我們想上傳檔案:

curl_easy_setopt(easyhandle, CURLOPT_UPLOAD, 1L); 

A few protocols won’t behave properly when uploads are done without any prior knowledge of the expected file size. So, set the upload file size using the CURLOPT_INFILESIZE_LARGE for all known file sizes like this[1]:

在完成上傳時,沒有預期檔案大小的任何預先知識,一些協議將無法正常工作。 所以,使用CURLOPT_INFILESIZE_LARGE為所有已知的檔案大小設定上傳檔案大小,如[1]:

/* in this example, file_size must be an curl_off_t variable */
/* 在本例中,檔案大小必須是curl_off_t型別的變數 */
curl_easy_setopt(easyhandle, CURLOPT_INFILESIZE_LARGE, file_size);

When you call curl_easy_perform this time, it’ll perform all the necessary operations and when it has invoked the upload it’ll call your supplied callback to get the data to upload. The program should return as much data as possible in every invoke, as that is likely to make the upload perform as fast as possible. The callback should return the number of bytes it wrote in the buffer. Returning 0 will signal the end of the upload.

當你這次呼叫curl_easy_perform時,它會執行所有必要的操作,當它呼叫上傳時,它會呼叫你提供的回撥來獲取資料上傳。 程式應該在每次呼叫中返回儘可能多的資料,因為這可能會使上傳執行儘可能快。 回撥應該返回它在緩衝區中寫入的位元組數。 返回0將表示上傳結束。

Passwords

Many protocols use or even require that user name and password are provided to be able to download or upload the data of your choice. libcurl offers several ways to specify them.

許多協議使用或甚至要求提供使用者名稱和密碼,以便能夠下載或上傳您選擇的資料。 libcurl提供了幾種方法來指定它們。

Most protocols support that you specify the name and password in the URL itself. libcurl will detect this and use them accordingly. This is written like this:

大多數協議支援您在URL本身中指定名稱和密碼。 libcurl將檢測到並相應地使用它們。 這是這樣寫的:

protocol://user:[email protected].com/path/

If you need any odd letters in your user name or password, you should enter them URL encoded, as %XX where XX is a two-digit hexadecimal number.

如果您在使用者名稱或密碼中需要任何奇怪字母,則應輸入URL編碼,如%XX,其中XX是兩位十六進位制數(譯者注:網址的傳輸並不是以明文方式,需要一定的編碼將明文轉換成base64或者其他的什麼編碼才能傳輸)。

libcurl also provides options to set various passwords. The user name and password as shown embedded in the URL can instead get set with the CURLOPT_USERPWD option. The argument passed to libcurl should be a char * to a string in the format “user:password”. In a manner like this:

libcurl還提供了設定各種密碼的選項。 如圖所示,嵌入在URL中的使用者名稱和密碼可以使用CURLOPT_USERPWD選項設定。 傳遞給libcurl的引數應該是一個char *,格式為user:password。 以這樣的方式:

curl_easy_setopt(easyhandle, CURLOPT_USERPWD, "myname:thesecret"); 

Another case where name and password might be needed at times, is for those users who need to authenticate themselves to a proxy they use. libcurl offers another option for this, the CURLOPT_PROXYUSERPWD. It is used quite similar to the CURLOPT_USERPWD option like this:

有時可能需要名稱和密碼的另一種情況是那些需要通過他們使用的代理對自己進行身份驗證的使用者。 libcurl為此提供了另一個選項,CURLOPT_PROXYUSERPWD。 它的使用非常類似於CURLOPT_USERPWD選項,像這樣:

 curl_easy_setopt(easyhandle, CURLOPT_PROXYUSERPWD, "myname:thesecret"); 

There’s a long time Unix “standard” way of storing FTP user names and passwords, namely in the $HOME/.netrc file. The file should be made private so that only the user may read it (see also the “Security Considerations” chapter), as it might contain the password in plain text. libcurl has the ability to use this file to figure out what set of user name and password to use for a particular host. As an extension to the normal functionality, libcurl also supports this file for non-FTP protocols such as HTTP. To make curl use this file, use the CURLOPT_NETRC option:

有很長一段時間是使用Unix”標準”的方式來儲存FTP使用者名稱和密碼,即在$HOME/.netrc檔案中。 該檔案應該是私有的,以便只有使用者可以閱讀它(參見“安全注意事項”一章),因為它可能包含明文密碼。 libcurl有能力使用這個檔案來找出用於特定主機的使用者名稱和密碼的集合。 作為正常功能的擴充套件,libcurl還支援非FTP協議(如HTTP)的此檔案。 要使curl使用此檔案,請使用CURLOPT_NETRC選項:

curl_easy_setopt(easyhandle, CURLOPT_NETRC, 1L); 

And a very basic example of how such a .netrc file may look like:
這裡有個很基礎的例子來展示.netrc檔案看起像什麼:

machine myhost.mydomain.com
login userlogin
password secretword

All these examples have been cases where the password has been optional, or at least you could leave it out and have libcurl attempt to do its job without it. There are times when the password isn’t optional, like when you’re using an SSL private key for secure transfers.

To pass the known private key password to libcurl:

所有這些例子都是密碼是可選的,或者至少你可以離開它,並有libcurl嘗試沒有它的工作。 有時,密碼不是可選的,例如,當您使用SSL私鑰進行安全傳輸時。

將已知的私鑰密碼傳遞給libcurl:

curl_easy_setopt(easyhandle, CURLOPT_KEYPASSWD, "keypassword"); 

HTTP Authentication(HTTP驗證)

The previous chapter showed how to set user name and password for getting URLs that require authentication. When using the HTTP protocol, there are many different ways a client can provide those credentials to the server and you can control which way libcurl will (attempt to) use them. The default HTTP authentication method is called ‘Basic’, which is sending the name and password in clear-text in the HTTP request, base64-encoded. This is insecure.

上一章展示瞭如何設定用於獲取需要身份驗證的URL的使用者名稱和密碼。 當使用HTTP協議時,客戶端可以通過許多不同的方式向伺服器提供這些憑據,並且您可以控制libcurl將(嘗試)使用它們的方式。 預設的HTTP認證方法叫做“Basic”,它以HTTP請求中的明文形式傳送名稱和密碼,base64編碼。 這是不安全的。

At the time of this writing, libcurl can be built to use: Basic, Digest, NTLM, Negotiate (SPNEGO). You can tell libcurl which one to use with CURLOPT_HTTPAUTH as in:

在撰寫本文時,libcurl可以構建為使用:BasicDigestNTLMNegotiate(SPNEGO)。 你可以告訴libcurl使用哪個CURLOPT_HTTPAUTH

 curl_easy_setopt(easyhandle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); 

And when you send authentication to a proxy, you can also set authentication type the same way but instead with CURLOPT_PROXYAUTH:

當您向代理髮送身份驗證時,您還可以使用相同的方式設定身份驗證型別,但使用CURLOPT_PROXYAUTH

curl_easy_setopt(easyhandle, CURLOPT_PROXYAUTH, CURLAUTH_NTLM); 

Both these options allow you to set multiple types (by ORing them together), to make libcurl pick the most secure one out of the types the server/proxy claims to support. This method does however add a round-trip since libcurl must first ask the server what it supports:

這兩個選項允許您設定多個型別(通過對它們進行ORing化),使libcurl選擇伺服器/代理聲稱支援的型別中最安全的一個。 然而,此方法新增一個往返,因為libcurl必須首先詢問伺服器它支援:

curl_easy_setopt(easyhandle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST|CURLAUTH_BASIC); 

For convenience, you can use the CURLAUTH_ANY define (instead of a list with specific types) which allows libcurl to use whatever method it wants.

為了方便起見,可以使用CURLAUTH_ANY定義(而不是具有特定型別的列表),它允許libcurl使用它想要的任何方法。

When asking for multiple types, libcurl will pick the available one it considers “best” in its own internal order of preference.

當要求多個型別時,libcurl將選擇它在自己的內部優先順序中認為“最好”的可用型別。

HTTP POSTing(HTTP 釋出操作(POST)ing)

We get many questions regarding how to issue HTTP POSTs with libcurl the proper way. This chapter will thus include examples using both different versions of HTTP POST that libcurl supports.

我們得到許多關於如何使用libcurl正確的方式發出HTTP POST的問題。 因此本章將包括使用libcurl支援的不同版本的HTTP POST的示例。

The first version is the simple POST, the most common version, that most HTML pages using the tag uses. We provide a pointer to the data and tell libcurl to post it all to the remote site:

第一個版本是簡單的POST,最常見的版本,大多數HTML頁面使用標籤使用。 我們提供一個指向資料的指標,並告訴libcurl將其全部發布到遠端站點:

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! */

Simple enough, huh? Since you set the POST options with the CURLOPT_POSTFIELDS, this automatically switches the handle to use POST in the upcoming request.

夠簡單,是嗎? 由於您使用CURLOPT_POSTFIELDS設定POST選項,因此會自動將控制代碼切換到在即將到來的請求中使用POST。

Ok, so what if you want to post binary data that also requires you to set the Content-Type: header of the post? Well, binary posts prevent libcurl from being able to do strlen() on the data to figure out the size, so therefore we must tell libcurl the size of the post data. Setting headers in libcurl requests are done in a generic way, by building a list of our own headers and then passing that list to libcurl.

Ok,所以如果你想釋出二進位制資料,還需要你設定Content-Type:標題的帖子? 好吧,二進位制帖子阻止libcurl對資料做strlen()操作,以確定大小,因此我們必須告訴libcurl的post資料的大小。 在libcurl請求中設定標頭檔案是通過一個通用的方法,通過構建一個我們自己的標頭檔案列表,然後將列表傳遞給libcurl。

/* 定義一個curl_slist型別的結構指標命名為headers */
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 釋出我們的列表用自定義的http頭部 */
curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers);

curl_easy_perform(easyhandle); /* post away! */

curl_slist_free_all(headers); /* free the header list 釋放http頭部 */

While the simple examples above cover the majority of all cases where HTTP POST operations are required, they don’t do multi-part formposts. Multi-part formposts were introduced as a better way to post (possibly large) binary data and were first documented in the RFC 1867 (updated in RFC 2388). They’re called multi-part because they’re built by a chain of parts, each part being a single unit of data. Each part has its own name and contents. You can in fact create and post a multi-part formpost with the regular libcurl POST support described above, but that would require that you build a formpost yourself and provide to libcurl. To make that easier, libcurl provides curl_formadd. Using this function, you add parts to the form. When you’re done adding parts, you post the whole form.

雖然上面的簡單示例涵蓋了需要HTTP POST操作的大多數情況,但它們不會執行多部分表單。 多部分表單被引入作為更好的方式來發布(可能是大的)二進位制資料,並且首先記錄在RFC 1867(在RFC 2388中更新)。 它們被稱為多部分,因為它們由一系列部分構成,每個部分是單個數據單元。 每個部分都有自己的名稱和內容。 事實上,您可以使用上面描述的常規libcurl POST支援來建立和釋出多部分表單,但是這需要您自己構建一個formpost並提供給libcurl。 為了使這更容易,libcurl提供curl_formadd。 使用此功能,您可以向表單中新增零件。 當您完成新增零件後,您釋出整個表單。

The following example sets two simple text parts with plain textual contents, and then a file with binary contents and uploads the whole thing.

以下示例使用純文字內容設定兩個簡單文字部分,然後使用二進位制內容設定檔案,並上傳整個內容。

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);

Multipart formposts are chains of parts using MIME-style separators and headers. It means that each one of these separate parts get a few headers set that describe the individual content-type, size etc. To enable your application to handicraft this formpost even more, libcurl allows you to supply your own set of custom headers to such an individual form part. You can of course supply headers to as many parts as you like, but this little example will show how you set headers to one specific part when you add that to the post handle:

多部分表單是使用MIME樣式分隔符和標題的部分鏈。 這意味著這些單獨的部分中的每一個都獲得一些描述單個內容型別,大小等的頭設定。為了使您的應用程式能夠更多地工作這個formpost,libcurl允許您提供自己的一套自定義標頭檔案 個別形式部分。 你當然可以提供儘可能多的部分,但這個小例子將顯示如何設定標題到一個特定的部分,當你新增到post控制代碼:

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 */

Since all options on an easyhandle are “sticky”, they remain the same until changed even if you do call curl_easy_perform, you may need to tell curl to go back to a plain GET request if you intend to do one as your next request. You force an easyhandle to go back to GET by using the CURLOPT_HTTPGET option:

因為easyhandle上的所有選項都是“sticky”,它們保持不變直到改變,即使你呼叫curl_easy_perform,你可能需要告訴curl回到一個簡單的GET請求,如果你打算做一個作為下一個請求。 使用CURLOPT_HTTPGET選項強制簡單控制代碼返回GET:

 curl_easy_setopt(easyhandle, CURLOPT_HTTPGET, 1L);

Just setting CURLOPT_POSTFIELDS to "" or NULL will not stop libcurl from doing a POST. It will just make it POST without any data to send!

只要將CURLOPT_POSTFIELDS設定為""NULL將不會停止libcurl做POST。 它將只是使其POST沒有任何資料傳送!

Showing Progress(顯示進展)

For historical and traditional reasons, libcurl has a built-in progress meter that can be switched on and then makes it present a progress meter in your terminal.

由於歷史和傳統的原因,libcurl有一個內建的進度表,可以開啟,然後使它呈現在您的終端進度表。

Switch on the progress meter by, oddly enough, setting CURLOPT_NOPROGRESS to zero. This option is set to 1 by default.

奇怪的是,開啟進度表,將CURLOPT_NOPROGRESS設定為零。 預設情況下,此選項設定為1。

For most applications however, the built-in progress meter is useless and what instead is interesting is the ability to specify a progress callback. The function pointer you pass to libcurl will then be called on irregular intervals with information about the current transfer.

然而,對於大多數應用程式,內建的進度表是無用的,而有趣的是能夠指定進度回撥。 然後,傳遞給libcurl的函式指標將在有關當前傳輸的資訊的不規則間隔上呼叫。

Set the progress callback by using CURLOPT_PROGRESSFUNCTION. And pass a pointer to a function that matches this prototype:

使用CURLOPT_PROGRESSFUNCTION設定進度回撥。 並傳遞一個指標到匹配這個原型的函式:

nt progress_callback(void *clientp,
                     double dltotal,
                     double dlnow,
                     double ultotal,
                     double ulnow);

If any of the input arguments is unknown, a 0 will be passed. The first argument, the clientp is the pointer you pass to libcurl with CURLOPT_PROGRESSDATA. libcurl won’t touch it.

如果任何輸入引數未知,將傳遞0。 第一個引數,clientp是您傳遞給libcurl與CURLOPT_PROGRESSDATA的指標。 libcurl不會碰它。

libcurl with C++

There’s basically only one thing to keep in mind when using C++ instead of C when interfacing libcurl:

The callbacks CANNOT be non-static class member functions

Example C++ code:

在連線libcurl時使用C ++而不是C時,基本上只有一件事要注意:

回撥不能是非靜態類成員函式

示例C ++程式碼:

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

Proxies(代理)

What “proxy” means according to Merriam-Webster: “a person authorized to act for another” but also “the agency, function, or office of a deputy who acts as a substitute for another”.

根據Merriam-Webster的說法,“代理人”是指“被授權為另一人行事的人”,而且“代理人代理人的代理人,職能或辦公室”。

Proxies are exceedingly common these days. Companies often only offer Internet access to employees through their proxies. Network clients or user-agents ask the proxy for documents, the proxy does the actual request and then it returns them.

代理是非常普遍的這些天。公司通常只通過代理人為員工提供網際網路接入。網路客戶端或使用者代理向代理請求文件,代理執行實際請求,然後返回它們。

libcurl supports SOCKS and HTTP proxies. When a given URL is wanted, libcurl will ask the proxy for it instead of trying to connect to the actual host identified in the URL.

libcurl支援SOCKS和HTTP代理。當需要給定的URL時,libcurl將詢問代理,而不是嘗試連線到URL中標識的實際主機。

If you’re using a SOCKS proxy, you may find that libcurl doesn’t quite support all operations through it.

如果你使用SOCKS代理,你可能會發現libcurl不支援所有的操作。

For HTTP proxies: the fact that the proxy is a HTTP proxy puts certain restrictions on what can actually happen. A requested URL that might not be a HTTP URL will be still be passed to the HTTP proxy to deliver back to libcurl. This happens transparently, and an application may not need to know. I say “may”, because at times it is very important to understand that all operations over a HTTP proxy use the HTTP protocol. For example, you can’t invoke your own custom FTP commands or even proper FTP directory listings.

對於HTTP代理:代理是HTTP代理的事實對實際可能發生的事情施加了某些限制。可能不是HTTP URL的請求的URL仍將被傳遞到HTTP代理以傳回libcurl。這是透明的,應用可能不需要知道。我說“可以”,因為有時,非常重要的是要理解,通過HTTP代理的所有操作使用HTTP協議。例如,您不能呼叫您自己的自定義FTP命令或甚至適當的FTP目錄列表。

Proxy Options

  • To tell libcurl to use a proxy at a given port number:

  • 告訴libcurl在給定埠號使用代理:

curl_easy_setopt(easyhandle, CURLOPT_PROXY, "proxy-host.com:8080"); 
  • Some proxies require user authentication before allowing a request, and you pass that information similar to this:

  • 一些代理在允許請求之前需要使用者身份驗證,您傳遞的資訊類似於:

curl_easy_setopt(easyhandle, CURLOPT_PROXYUSERPWD, "user:password"); 
  • If you want to, you can specify the host name only in the CURLOPT_PROXY option, and set the port number separately with CURLOPT_PROXYPORT.

  • 如果需要,您只能在CURLOPT_PROXY選項中指定主機名,並使用CURLOPT_PROXYPORT分別設定埠號。

  • Tell libcurl what kind of proxy it is with CURLOPT_PROXYTYPE (if not, it will default to assume a HTTP proxy):

  • 告訴libcurl它是什麼樣的代理它是與CURLOPT_PROXYTYPE(如果不是,它將預設假設一個HTTP代理):

curl_easy_setopt(easyhandle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); 

Environment Variables(環境變數)

  • libcurl automatically checks and uses a set of environment variables to know what proxies to use for certain protocols. The names of the variables are following an ancient de facto standard and are built up as "[protocol]_proxy"(note the lower casing). Which makes the variable 'http_proxy' checked for a name of a proxy to use when the input URL is HTTP. Following the same rule, the variable named 'ftp_proxy' is checked for FTP URLs. Again, the proxies are always HTTP proxies, the different names of the variables simply allows different HTTP proxies to be used.

  • libcurl自動檢查並使用一組環境變數來了解某些協議使用什麼代理。變數的名稱遵循一個古老的事實標準,並被建立為[protocol] _proxy(注意下套管)。這使得變數“http_proxy”在輸入URL是HTTP時檢查要使用的代理的名稱。遵循相同的規則,檢查名為“ftp_proxy”的變數的FTP URL。同樣,代理總是HTTP代理,變數的不同名稱只允許使用不同的HTTP代理。

  • The proxy environment variable contents should be in the format "[protocol://][user:[email protected]]machine[:port]". Where the protocol:// part is simply ignored if present (so http://proxy and bluerk://proxy will do the same) and the optional port number specifies on which port the proxy operates on the host. If not specified, the internal default port number will be used and that is most likely not the one you would like it to be.

  • 代理環境變數內容應為格式"[protocol//][user:[email protected]]machine[:port]"。其中protocol://部分被忽略(如果存在)(因此http:// proxy和bluerk:// proxy將會這樣做),可選埠號指定代理在主機上在哪個埠上操作。如果沒有指定,將使用內部預設埠號,這很可能不是你想要的。

  • There are two special environment variables.'all_proxy'is what sets proxy for any URL in case the protocol specific variable wasn’t set, and 'no_proxy' defines a list of hosts that should not use a proxy even though a variable may say so. If 'no_proxy' is a plain asterisk (“*”) it matches all hosts.

  • 有兩個特殊的環境變數。 'all_proxy'是在未設定協議特定變數的情況下為任何URL設定代理,'no_proxy'定義不應使用代理的主機列表,即使變數可能這樣。如果'no_proxy'是一個簡單的星號("*"),它匹配所有主機。

  • To explicitly disable libcurl’s checking for and using the proxy environment variables, set the proxy name to "" - an empty string - with CURLOPT_PROXY.

  • 要顯式禁用libcurl的檢查和使用代理環境變數,請將代理名稱設定為""- 一個空字串 - 使用CURLOPT_PROXY

SSL and Proxies(SSL和代理)

  • SSL is for secure point-to-point connections. This involves strong encryption and similar things, which effectively makes it impossible for a proxy to operate as a “man in between” which the proxy’s task is, as previously discussed. Instead, the only way to have SSL work over a HTTP proxy is to ask the proxy to tunnel trough everything without being able to check or fiddle with the traffic.

  • SSL用於安全的點對點連線。這涉及強加密和類似的事情,這有效地使得代理不可能作為代理的任務是“之間的人”操作,如先前所討論的。相反,使SSL通過HTTP代理工作的唯一方法是請求代理通過所有通道,而不能檢查或干擾流量。

  • Opening an SSL connection over a HTTP proxy is therefor a matter of asking the proxy for a straight connection to the target host on a specified port. This is made with the HTTP request CONNECT. (“please mr proxy, connect me to that remote host”).

  • 通過HTTP代理開啟SSL連線就是要求代理在指定埠上直接連線到目標主機。這是使用HTTP請求CONNECT。(“please mr proxy, connect me to that remote host”)。

  • Because of the nature of this operation, where the proxy has no idea what kind of data that is passed in and out through this tunnel, this breaks some of the very few advantages that come from using a proxy, such as caching. Many organizations prevent this kind of tunneling to other destination port numbers than 443 (which is the default HTTPS port number).

  • 由於此操作的本質,代理不知道通過此隧道傳入和傳出哪種型別的資料,這打破了使用代理(如快取)帶來的一些非常少的優點。許多組織防止這種型別的隧道到其他目標埠號443(這是預設的HTTPS埠號)。

Tunneling Through Proxy(通過代理隧道)

  • As explained above, tunneling is required for SSL to work and often even restricted to the operation intended for SSL; HTTPS.

  • 如上所述,SSL需要隧道來工作,並且通常甚至限於用於SSL的操