* cURL에 관한 개념 정리 및 예제
gcc -L/경로/lib/ -I/경로/include/ -o testprogram test.c -lcurl
# QT개발 환경
*.pro 파일에 라이브러리 사용을 위해 다음을 추가 한다.
INCLUDEPATH +=./ \
extern “C” {
#include <curl/curl.h>
}
출처 : http://sanghun.tistory.com/197
1 Curl에 대해서
- HTTP GET : URL로 부터 내용을 가져온다.
- Anonymous FTP 다운로드 : FTP서버에 접속해서 파일을 가져온다.
- HTTP POST: Web Form을 흉내내어서 데이터를 전송한다.
- Authenticated FTP Upload : ID+Password로 접속해서 파일을 Upload한다.
libCURL은 C 라이브러리이다. 몇몇 예제는 빠른작성을 위해서 C++을 이용할것이긴 하지만, 이해하고 사용하는데 무리는 없을 것이다 – C++의 템플릿은 프로그램 작성에 걸리는 시간을 대폭 줄요준다. 테스트 프로그램을 만들고자 할때 시간을 절약하도록 해 준다 -.
2 Curl 사용 기초
- clinet는 server로 연결을 시도한다.
- 연결이 되었다면 client는 GET, POST를 이용해서 데이터를 보낼 것이다.
- server는 요청을 받고 요청에 대한 응답을 보낸다.(HTML페이지 혹은 에러 메시지)
- client는 server로의 연결을 종료한다.
좀더 자세한 내용은 TCP/IP문서를 참고하기 바란다.
libCURL은 이러한 프로세스의 중간에서 중계역할을 한다. 사용은 context 객체와 요청데이터 (URL, 인자)와 콜백(callback functions)형식의 response handler를 정의해 주면된다. 이 객채를 라이브러리에 넘기게 되면, 라이브러리는 저수준에서 일어나는 네트워크 통신을 알아서 핸들하고 그 결과 값을 response handler를 통해서 넘겨주게 된다.
- curl_blogal_init : curl 라이브러리를 초기화 한다.
- curl_easy_init : context를 생성한다.
- curl_easy_setopt : context 설정
- curl_easy_perform : 요청을 초기화 하고 callback함수를 대기시킨다.
- curl_easy_cleanup : context를 없앤다.
- curl_global_cleanup : curl 라이브러리를 없앤다.
curl_easy_setopt 함수는 다음과 같이 선언되어 있다.
curl_easy_setopt(CURL *ctx, CURLoption key, value)
3 HTTP GET:웹 페이지 긁어오기
CURL *ctx = curl_easy_init ();
curl_easy_setopt 를 이용해서 context객체를 설정한다. CURLOPT_URL은 목표 URL이다.
curl_easy_setopt(ctx, CURLOPT_URL, argv[1]);
curl_easy_setopt를 이용하면 이외에도 몇 가지 설정을 더 해줄 수 있다.
curl_easy_setiopt(ctx, CURLOPT_WRITEHEADER, stderr); curl_easy_setiopt(ctx, CURLOPT_WRITEDATA, stdout);
header 정보는 표준에러로, body정보는 표준출력으로 가져오도록 설정을 했다.
const CURLcode rc = curl_easy_perform(ctx); if (CURLE_OK != rc) { std::cerr << "Error from cURL: " << curl_easy_strerror(rc) std::endl; } else { // 데이터 처리 }
long statLong; curl_easy_getinfo(ctx, CURLINFO_HTTP_CODE, &statLong); std::cout << "HTTP response code: " << statLong << std::endl;
원하는 값을 가져오기 위해서는 데이터의 타입에 맞는 인자를 써야 한다. 200이나 404와 같은 HTTP 응답코드를 가져오길 원한다면 CURLINFO_HTTP_CODE를 전송받은 문서의 크기를 알아내길 원한다면 CURLINFO_SIZE_DOWNLOAD를 사용하는 식이다.
/*
sample for O'ReillyNet article on libcURL:
{TITLE}
{URL}
AUTHOR: Ethan McCallum
Scenario: use http/GET to fetch a webpage
이 코드는 Ubuntu 리눅스 Kernel 2.6.15에서
libcURL 버젼 7.15.1로 테스트 되었다.
2006년 8월 3일
*/
#include<iostream>
extern "C" {
#include<curl/curl.h>
}
// - - - - - - - - - - - - - - - - - - - -
enum {
ERROR_ARGS = 1 ,
ERROR_CURL_INIT = 2
} ;
enum {
OPTION_FALSE = 0 ,
OPTION_TRUE = 1
} ;
enum {
FLAG_DEFAULT = 0
} ;
// - - - - - - - - - - - - - - - - - - - -
int main( const int argc , const char** argv ){
if( argc != 2 ){
std::cerr << " Usage: ./" << argv[0] << " {url} [debug]" << std::endl ;
return( ERROR_ARGS ) ;
}
const char* url = argv[1] ;
// lubcURL 초기화
curl_global_init( CURL_GLOBAL_ALL ) ;
// context객체의 생성
CURL* ctx = curl_easy_init() ;
if( NULL == ctx ){
std::cerr << "Unable to initialize cURL interface" << std::endl ;
return( ERROR_CURL_INIT ) ;
}
// context 객체를 설정한다.
// 긁어올 url을 명시하고, url이 URL정보임을 알려준다.
curl_easy_setopt( ctx , CURLOPT_URL, url ) ;
// no progress bar:
curl_easy_setopt( ctx , CURLOPT_NOPROGRESS , OPTION_TRUE ) ;
/*
By default, headers are stripped from the output.
They can be:
- passed through a separate FILE* (CURLOPT_WRITEHEADER)
- included in the body's output (CURLOPT_HEADER -> nonzero value)
(here, the headers will be passed to whatever function
processes the body, along w/ the body)
- handled with separate callbacks (CURLOPT_HEADERFUNCTION)
(in this case, set CURLOPT_WRITEHEADER to a
matching struct for the function)
*/
// 헤더는 표준에러로 출력하도록 하다.
curl_easy_setopt( ctx , CURLOPT_WRITEHEADER , stderr ) ;
// body 데이터는 표준출력 하도록 한다.
curl_easy_setopt( ctx , CURLOPT_WRITEDATA , stdout ) ;
// context 객체의 설정 종료
// 웹페이지를 긁어온다.
const CURLcode rc = curl_easy_perform( ctx ) ;
if( CURLE_OK != rc ){
std::cerr << "Error from cURL: " << curl_easy_strerror( rc ) << std::endl ;
}else{
// get some info about the xfer:
double statDouble ;
long statLong ;
char* statString = NULL ;
// HTTP 응답코드를 얻어온다.
if( CURLE_OK == curl_easy_getinfo( ctx , CURLINFO_HTTP_CODE , &statLong ) ){
std::cout << "Response code: " << statLong << std::endl ;
}
// Content-Type 를 얻어온다.
if( CURLE_OK == curl_easy_getinfo( ctx , CURLINFO_CONTENT_TYPE , &statString ) ){
std::cout << "Content type: " << statString << std::endl ;
}
// 다운로드한 문서의 크기를 얻어온다.
if( CURLE_OK == curl_easy_getinfo( ctx , CURLINFO_SIZE_DOWNLOAD , &statDouble ) ){
std::cout << "Download size: " << statDouble << "bytes" << std::endl ;
}
//
if( CURLE_OK == curl_easy_getinfo( ctx , CURLINFO_SPEED_DOWNLOAD , &statDouble ) ){
std::cout << "Download speed: " << statDouble << "bytes/sec" << std::endl ;
}
}
// cleanup
curl_easy_cleanup( ctx ) ;
curl_global_cleanup() ;
return( 0 ) ;
} // main()
4 FTP Download: FTP 데이터 다운로드
size_t showSize(...); curl_esay_setopt(ctx, CURLOPT_WRITEFUNCTION, showSize);
CURLOPT_WRITEFUNCTION은 원격지 파일을 다운로드 할께 호출할 함수를 정의하기 위해서 사용한다.
class XferInfo
{
void add(int more);
int getBytesTransferred();
int getTimesCalled();
};
...
XferInfo info;
curl_easy_setopt(ctx, CURLOPT_WRITEDATA, &info);
extern "C"
size_t showSize
{
void *source,
size_t size,
size_t nmemb,
void *userData
}
extern "C" size_t showSize(...) { XferInfo *info = static_cast<XferInfo *>(userData); const int bufferSize = size * nmemb; info->add(bufferSize); }
이제 우리가 만든 콜백함수는 읽어들인 파일의 크기를 (size*nmemb)로 계산해서 되돌려줄 것이다. 또한 이 함수는 bufferSize를 되돌려주게 되는데, libCURL은 콜백함수의 리턴값과 전송받은 실제 데이터의 크기를 비교해서 틀릴경우 0을 리턴해주게 된다. 이런식으로 데이터를 정상적으로 받았는지를 체크할 수 있다.
/*
sample for O'ReillyNet article on libcURL:
{TITLE}
{URL}
AUTHOR: Ethan McCallum
anon ftp/download (scenario: fetch remote file)
이 코드는 Ubuntu 리눅스 Kernel 2.6.15에서
libcURL 버젼 7.15.1로 테스트 되었다.
2006년 8월 3일
*/
#include<iostream>
#include<string>
#include<sstream>
#include<list>
extern "C" {
#include<curl/curl.h>
}
// - - - - - - - - - - - - - - - - - - - -
typedef std::list< std::string > FileList ;
enum {
ERROR_ARGS = 1 ,
ERROR_CURL_INIT = 2
} ;
enum {
OPTION_FALSE = 0 ,
OPTION_TRUE = 1
} ;
// - - - - - - - - - - - - - - - - - - - -
// 콜백함수에서 사용할 사용자 정의 객체
class XferInfo {
private:
int bytesTransferred_ ;
int invocations_ ;
protected:
// empty
public:
XferInfo(){
reset() ;
} // ctor
/// reset counters
void reset(){
bytesTransferred_ = 0 ;
invocations_ = 0 ;
return ;
} // reset()
/// add the number of bytes transferred in this call
void add( int more ){
bytesTransferred_ += more ;
++invocations_ ;
return ;
} // add()
/// get the amount of data transferred, in bytes
int getBytesTransferred() const {
return( bytesTransferred_ ) ;
} // getBytesTransferred()
/// get the number of times add() has been called
int getTimesCalled(){
return( invocations_ ) ;
} // getTimesCalled()
} ;
// - - - - - - - - - - - - - - - - - - - -
// C++ 에서 C함수를 링크시키기 위해서는, "extern C"를 이용해야 한다.
extern "C"
size_t showSize( void *source , size_t size , size_t nmemb , void *userData ){
// this function may be called any number of times for even a single
// transfer; be sure to write it accordingly.
// source is the actual data fetched by libcURL; cast it to whatever
// type you need (usually char*). It has NO terminating NULL byte.
// we don't touch the data here, so the cast is commented out
// const char* data = static_cast< const char* >( source ) ;
// userData is called "stream" in the docs, which is misleading:
// that parameter can be _any_ data type, not necessarily a FILE*
// Here, we use it to save state between calls to this function
// and track number of times this callback is invoked.
XferInfo* info = static_cast< XferInfo* >( userData ) ;
const int bufferSize = size * nmemb ;
std::cout << 't' << "showSize() called: " << bufferSize << " bytes passed" << std::endl ;
// ... pretend real data processing on *source happens here ...
info->add( bufferSize ) ;
/*
return some number less than bufferSize to indicate an
error (xfer abort)
nicer code would also set a status var (in userData) for the
calling function
*/
return( bufferSize ) ;
} // showSize()
// - - - - - - - - - - - - - - - - - - - -
int main( const int argc , const char** argv ){
if( argc < 3 ){
std::cerr << "test of libcURL: anonymous FTP" << std::endl ;
std::cerr << " Usage: " << argv[0] << " {server} {file1} [{file2} ...]" << std::endl ;
return( ERROR_ARGS ) ;
}
// remote FTP server
const char* server = argv[1] ;
const int totalTargets = argc - 2 ;
std::cout << "Attempting to download " << totalTargets
<< " files from " << server << std::endl
;
curl_global_init( CURL_GLOBAL_ALL ) ;
CURL* ctx = curl_easy_init() ;
if( NULL == ctx ){
std::cerr << "Unable to initialize cURL interface" << std::endl ;
return( ERROR_CURL_INIT ) ;
}
/* BEGIN: global handle options */
// handy for debugging: see *everything* that goes on
// curl_easy_setopt( ctx , CURLOPT_VERBOSE, OPTION_TRUE ) ;
// no progress bar:
curl_easy_setopt( ctx , CURLOPT_NOPROGRESS , OPTION_TRUE ) ;
// what to do with returned data
curl_easy_setopt( ctx , CURLOPT_WRITEFUNCTION , showSize ) ;
XferInfo info ;
for(
int ix = 2 ;
ix < argc ;
++ix
){
const char* item = argv[ ix ] ;
// zero counters for each file
info.reset() ;
// target url: concatenate the server and target file name
urlBuffer.str( "" ) ;
urlBuffer << "ftp://" << server << "/" << item << std::flush ;
std::cout << "Trying " << urlBuffer.str() << std::endl ;
const std::string url = urlBuffer.str() ;
curl_easy_setopt( ctx , CURLOPT_URL, url.c_str() ) ;
// set the write function's user-data (state data)
curl_easy_setopt( ctx , CURLOPT_WRITEDATA , &info ) ;
// action!
const CURLcode rc = curl_easy_perform( ctx ) ;
// for curl v7.11.x and earlier, look into
// curl_easy_setopt( ctx , CURLOPT_ERRORBUFFER , /* char array */ ) ;
if( CURLE_OK == rc ){
std::cout << 't' << "xfer size: " << info.getBytesTransferred() << " bytes" << std::endl ;
std::cout << 't' << "Callback was invoked " << info.getTimesCalled() << " times for this file" << std::endl ;
} else {
std::cerr << "Error from cURL: " << curl_easy_strerror( rc ) << std::endl ;
}
std::cout << std::endl ;
}
// cleanup
curl_easy_cleanup( ctx ) ;
curl_global_cleanup() ;
return( 0 ) ;
} // main()
5 FTP 업로드
ftp://host/file
login:password
ftp://login:password@host/file
struct curl_slist *command=NULL; command = curl_slist_append( commands, "mkdir /some/path"); command = curl_slist_append( commands, "mkdir /another/path");
물론 이들 명령이 실행하기 위해서는 로그인 과정을 우선 거쳐야 할 것이다. 이제 CURLOPT_QUOTE를 이용해서 명령을 등록시키면 된다.
curl_easy_setopt(ctx, CURLPOST_QUOTE, commands); // curl_easy_perform을 이용해서 ftp 세션을 시작하고 // 명령을 실행한다. curl_slist_free_all(command);
6 HTTP POST
const char* postData="param1=value1¶m2=value2&...";
POST 데이터 전송을 위해서 CURLOPT_POSTFIELDS옵션을 설정하면 된다.
curl_easy_setopt(ctx, CURLOPT_POSTFIELDS, postData);
curl_sist * responseHeaders=NULL;
responseHeaders = curl_slist_append(
responseHeaders,
"Expect: 100-continue"
);
curl_easy_setopt(ctx, CURLOPT_HTTPHEADER, responseHeaders);
주의 할것은 libCURL은 hidden 필드나 JavaScript와 같은 클라이언트측의 기술들을 사용하지 못한다는 점이다. 예를들어 폼입력을 하고나서 submit버튼을 클릭하면 폼의 각 필드를 검사하는 등의 자바스크립트등은 처리할 수 없다.
/*
sample for O'ReillyNet article on libcURL:
{TITLE}
{URL}
AUTHOR: Ethan McCallum
HTTP POST (e.g. form processing or REST web services)
이 코드는 Ubuntu 6.06 Dapper Drake,
libcURL
This code was built/tested under Fedora Core 3,
libcURL version 7.12.3 환경에서 테스트 되었다.
*/
#include<cstdio>
#include<iostream>
#include<string>
#include<sstream>
extern "C" {
#include<curl/curl.h>
}
// - - - - - - - - - - - - - - - - - - - -
enum {
ERROR_ARGS = 1 ,
ERROR_CURL_INIT = 2
} ;
enum {
OPTION_FALSE = 0 ,
OPTION_TRUE = 1
} ;
enum {
FLAG_DEFAULT = 0
} ;
const char* targetUrl ;
// - - - - - - - - - - - - - - - - - - - -
int main( int argc , char** argv ){
if( argc != 2 ){
std::cerr << "test of libcURL: test an HTTP post" << std::endl ;
std::cerr << "(post data is canned)" << std::endl ;
std::cerr << " Usage: " << argv[0] << " {post url}" << std::endl ;
std::exit( ERROR_ARGS ) ;
}
targetUrl = argv[1] ;
curl_global_init( CURL_GLOBAL_ALL ) ;
CURL* ctx = curl_easy_init() ;
if( NULL == ctx ){
std::cerr << "Unable to initialize cURL interface" << std::endl ;
return( ERROR_CURL_INIT ) ;
}
/* BEGIN: configure the handle: */
// Target URL:
curl_easy_setopt( ctx , CURLOPT_URL, targetUrl ) ;
// no progress bar:
curl_easy_setopt( ctx , CURLOPT_NOPROGRESS , OPTION_TRUE ) ;
// 응답데이터를 표준출력으로 보낸다.
curl_easy_setopt( ctx , CURLOPT_WRITEDATA , stdout ) ;
// 사용자 정의 HTTP 헤더: create a linked list and assign
curl_slist* responseHeaders = NULL ;
responseHeaders = curl_slist_append( responseHeaders , "Expect: 100-continue" ) ;
responseHeaders = curl_slist_append( responseHeaders , "User-Agent: Some Custom App" ) ;
curl_easy_setopt( ctx , CURLOPT_HTTPHEADER , responseHeaders ) ;
// POST Data 설정
// notice the URL-unfriendly characters (such as "%" and "&")
// URL에서는 '%', '&', ' '와 같은 문자를 URL encoding 시켜줘야 한다.
// curl_escape 함수를 이용해서 인코딩할 수 있다.
const char* postParams[] = {
"One" , "this has % and & symbols" ,
"Dos" , "value with spaces" ,
"Trois" , "plus+signs+will+be+escaped" ,
"Chetirye" , "fourth param..." ,
NULL
} ;
// buffer for the POST params
std::ostringstream postBuf ;
const char** postParamsPtr = postParams ;
while( NULL != *postParamsPtr )
{
// curl_escape( {string} , 0 ): replace special characters
// (such as space, "&", "+", "%") with HTML entities.
// ( 0 => "use strlen to find string length" )
// remember to call curl_free() on the strings on the way out
char* key = curl_escape( postParamsPtr[0] , FLAG_DEFAULT ) ;
char* val = curl_escape( postParamsPtr[1] , FLAG_DEFAULT ) ;
std::cout << "Setting POST param: "" << key << "" => "" << val << """ << std::endl ;
postBuf << key << "=" << val << "&" ;
postParamsPtr += 2 ;
// the cURL lib allocated the escaped versions of the
// param strings; we must free them here
curl_free( key ) ;
curl_free( val ) ;
}
postBuf << std::flush ;
// We can't really call "postBuf.str().c_str()" here, because
// the std::string created in the middle is a temporary. In turn,
// the char* buf from its c_str() operation isn't guaranteed to
// be around after the function call.
// The solution: explicitly create the string.
// Larger (and/or better) code would use std::string::copy() to create
// a const char* pointer to pass to cURL, then clean it up later.
// e.g.:
// const char* postData = new char*[ 1 + postBuf.tellg() ] ;
// postBuf.str().copy( postData , std::string::npos ) ;
// postData[ postBuf.tellg() ] == '' ;
const std::string postData = postBuf.str() ;
std::cout << "post data: " << postData << std::endl ;
curl_easy_setopt( ctx , CURLOPT_POSTFIELDS , postData.c_str() ) ;
// do a standard HTTP POST op
// in theory, this is automatically set for us by setting
// CURLOPT_POSTFIELDS...
curl_easy_setopt( ctx , CURLOPT_POST , OPTION_TRUE ) ;
/* END: configure the handle */
// action!
std::cout << "- - - BEGIN: response - - -" << std::endl ;
CURLcode rc = curl_easy_perform( ctx ) ;
std::cout << "- - - END: response - - -" << std::endl ;
// "curl_easy_strerror()" available in curl v7.12.x and later
if( CURLE_OK != rc ){
std::cerr << 't' << "Error from cURL: " << curl_easy_strerror( rc ) << std::endl ;
}
// cleanup
curl_slist_free_all( responseHeaders ) ;
curl_easy_cleanup( ctx ) ;
curl_global_cleanup() ;
std::exit( 0 ) ;
} // main()
<?
echo "SERVER Ons : $One<br>n";
echo "SERVER Dos : $Dos<br>n";
echo "SERVER Trois : $Trois<br>n";
echo "SERVER Chetirye : $Chetirye<br>n";
?>

18 Replies to “Curl이란?”