* 원문링크 :
http://curl.haxx.se/libcurl/c/the-guide.html
http://curl.haxx.se/libcurl/c/the-guide.html
1 전역 준비함수 #
프로그램은 libcurl 기능을 사용하기위해서 몇가지 초기화를 실행해야합니다. 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
curl_global_init()
and it takes one parameter which is a bit pattern that tells libcurl what to intialize. CURL_GLOBAL_ALL을 사용하는 것은 내부 부속 모듈 모두를 초기화한다는 것을 의미하며, 이것은 좋은 기본 옵션값이라 할 수 있습니다. 현재는 두가지 옵션값이 정의되어있습니다.
- CURL_GLOBAL_WIN32 : 윈도우즈 플렛폼에서만 동작한다는 것을 의미합니다. 윈도우즈 플렛폼상에서 이 옵션을 사용하게 되면, libcurl을 winsock을 사용하여 초기화하게 됩니다. 알맞은 초기화를 하지 않고서는, 여러분의 프로그램은 소켓을 알맞게 사용할 수 없을 겁니다. 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.
- CURL_GLOBAL_SSL : libcurl이 SSL가능하도록 컴파일되고 빌드되었다는 것을 나타냅니다. On these systems, this will make libcurl init OpenSSL properly for this application. This is only needed to do once for each application so if your program or another library already does this, this bit should not be needed.
더이상 lubcurl을 사용하지 않는다면, 초기화 호출에 반대인 curl_global_cleanup()을 호출하시기 바랍니다. 이것은 curl_global_init()가 초기화한 자원을 소거하기위한 역할을 합니다.
curl_global_init()와 curl_global_cleanup()을 반복하여 호출하는 것은 피해야만 합니다. 이 함수들은 한번씩만 호출하시기 바랍니다.
2 libcurl Easy 인터페이스 다루기 #
libcurl 버전 7에서부터는 소위 easy 인터페이스라는 것이 생겼습니다. easy 인터페이스상의 모든 함수는 'curl_easy'로 시작합니다.
또한 앞으로의 libcurl버전은 multi 인터페이스라는 것을 제공할 것입니다. More about that interface, what it is targeted for and how to use it is still only debated on the libcurl mailing list and developer web pages. Join up to discuss and figure out!
easy 인터페이스를 사용하기위해서는 먼저 easy 핸들값을 먼저 생성해야만 합니다. 실행하고자하는 각각의 easy 세션마다 한개씩 핸들이 필요합니다. 기본적으로 전송처리에 사용될 모든 쓰레드마다 한 개의 핸들을 사용해야만 합니다. 여러개의 쓰레드상에서 같은 핸들을 공유할 수 없다는 점, 명심하세요.
easy 핸들을 얻으려면,
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. Alas, multiple requests using the same handle will use the same options.
Many of the informationals you set in libcurl are "strings", pointers to data terminated with a zero byte. Keep in mind that when you set strings with curl_easy_setopt(), libcurl will not copy the data. It will merely point to the data. You MUST make sure that the data remains available for libcurl to use until finished or until you use the same option again to point to something else.
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:
curl_easy_setopt(easyhandle, CURLOPT_URL, "http://curl.haxx.se/");
Let's assume for a while that you want to receive data as the URL indentifies 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:
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:
curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, write_data);
You can control what data your function get in the forth argument by setting another property:
curl_easy_setopt(easyhandle, CURLOPT_FILE, &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_FILE.
libcurl offers its own default internal callback that'll 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_FILE option.
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 a an open file with CURLOPT_FILE, it will crash. You should therefore avoid this to make your program run fine virtually everywhere.
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);
The 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.
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.
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.
3 멀티쓰레딩에 관한 주제들 #
libcurl is completely thread safe, except for two issues: signals and alarm handlers. Signals are needed for a SIGPIPE handler, and the alarm() syscall is used to catch timeouts (mostly during DNS lookup).
So when using multiple threads you should first ignore SIGPIPE in your main thread and set the CURLOPT_NOSIGNAL option to TRUE for all handles.
Everything will work fine except that timeouts are not honored during the DNS lookup - this would require some sort of asynchronous DNS lookup (which is planned for a future libcurl version).
For SIGPIPE info see the UNIX Socket FAQ at
http://www.unixguide.net/network/socketfaq/2.22.shtml
http://www.unixguide.net/network/socketfaq/2.22.shtml
Also, note that CURLOPT_DNS_USE_GLOBAL_CACHE is not thread-safe.
4 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.
There's one golden rule when these things occur: set the CURLOPT_VERBOSE option to TRUE. It'll cause the library to spew out the entire protocol details it sends, some internal info and some received protcol 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 wht the server behaves the way it does. Include headers in the normal body output with CURLOPT_HEADER set TRUE.
Of course there are bugs left. We need to get 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 much details 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.
If CURLOPT_VERBOSE is not enough, you increase the level of debug data your application receive by using the 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.
5 원격 사이트에 데이타를 업로드하기 #
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.
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.
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:
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.
curl_easy_setopt(easyhandle, CURLOPT_READFUNCTION, read_function); curl_easy_setopt(easyhandle, CURLOPT_INFILE, &filedata);
Tell libcurl that we want to upload:
curl_easy_setopt(easyhandle, CURLOPT_UPLOAD, TRUE);
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 for all known file sizes like this[1]:
curl_easy_setopt(easyhandle, CURLOPT_INFILESIZE, 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.
6 암호 #
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.
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:
protocol://user:password@example.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.
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:
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 athenticate 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:
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:
curl_easy_setopt(easyhandle, CURLOPT_NETRC, TRUE);
And a very basic example of how such a .netrc file may look like:
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.
You can in this situation either pass a password to libcurl to use to unlock the private key, or you can let libcurl prompt the user for it. If you prefer to ask the user, then you can provide your own callback function that will be called when libcurl wants the password. That way, you can control how the question will appear to the user.
To pass the known private key password to libcurl:
curl_easy_setopt(easyhandle, CURLOPT_SSLKEYPASSWD, "keypassword");
To make a password callback:
int enter_passwd(void *ourp, const char *prompt, char *buffer, int len); curl_easy_setopt(easyhandle, CURLOPT_PASSWDFUNCTION, enter_passwd);
7 HTTP POST #
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.
The first version is the simple POST, the most common version, that most HTML pages using the <form> tag uses. We provide a pointer to the data and tell libcurl to post it all to the remote site:
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 automaticly switches the handle to use POST in the upcoming request.
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 prevents 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.
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, 23); /* 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 */
While the simple examples above cover the majority of all cases where HTTP POST operations are required, they don't do multipart formposts. Multipart formposts were introduced as a better way to post (possibly large) binary data and was first documented in the RFC1867. They're called multipart because they're built by a chain of parts, each being a single unit. Each part has its own name and contents. You can in fact create and post a multipart 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.
The following example sets two simple text parts with plain textual contents, and then a file with binary contents and upload 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 you like, but this little example will show how you set headers to one specific part when you add that to the post handle:
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(post); /* 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 such a one as your next request. You force an easyhandle to back to GET by using the CURLOPT_HTTPGET option:
curl_easy_setopt(easyhandle, CURLOPT_HTTPGET, TRUE);
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!
8 진행상황 보기 #
For historical and traditional reasons, libcurl has a built-in progress meter that can be switched on and then makes it presents a progress meter in your terminal.
Switch on the progress meter by, oddly enough, set CURLOPT_NOPROGRESS to FALSE. This option is set to TRUE by default.
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.
Set the progress callback by using CURLOPT_PROGRESSFUNCTION. And pass a pointer to a function that matches this prototype:
int 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.
(계속)








