cURL用于tranfering data with URLs。支持大部分常见通讯协议,也支持大部分的平台,而且是完全开源的 ,因此被广泛使用。我们常说的cURL是指它的命令行工具(command line tool),libcurl是指它的库(library,C实现)。
一、资料
快速查询API?将https://curl.se/libcurl/c/*.html中的*替换成相应api进行查询。
二、Two interfaces
libcurl提供了两套接口,easy和multi。
curl_easy_***(...); // 这是easy版接口curl_multi_***(...); // 这是multi版接口
The easy interface lets you do single transfers with a synchronous and blocking function call.
The multi interface allows multiple simultaneous transfers in a single thread.
1、easy interface
使用示例如下:
// ************************************************************// libcurl的全局初始化,在我们的程序中只需调用一次,和curl_global_cleanup首尾呼应。// 注意,这不是thread-safe的,确保在main thread执行。// ************************************************************curl_global_init( CURL_GLOBAL_ALL );// ************************************************************// 获取当前libcurl的版本信息,一般用于我们检查是否是合适的版本。// ************************************************************curl_version_info_data *data = curl_version_info( CURLVERSION_NOW );// ************************************************************// create an easy handle which starts a libcurl easy session// 必须和curl_easy_cleanup()首尾呼应。// ************************************************************CURL *easyhandle = curl_easy_init();// ************************************************************// 设置选项,和OpenGL的State类似,只要设置了就一直保持,除非显式改变。// API手册:https://curl.se/libcurl/c/curl_easy_setopt.html// 每个option的用法:https://curl.se/libcurl/c/****.html,将**替换成optionname// 每个option的例子:https://curl.se/libcurl/c/options-in-examples.html// ************************************************************curl_easy_setopt( easyhandle, optionname, ...... );// 设置error msg的输出error buffer,用于我们debug。char curl_errbuf[CURL_ERROR_SIZE];curl_easy_setopt( easyhandle, CURLOPT_ERRORBUFFER, curl_errbuf );// ************************************************************// 重置这个handle的所有选项至默认。// ************************************************************curl_easy_reset( easyhandle );// ************************************************************// 复制一个全新独立的handle,option也会复制。// ************************************************************CURL *copyEasyHandle = curl_easy_duphandle( easyhandle );// ************************************************************// perform a blocking(阻塞) file transfer,将会阻塞,直到transfer完成。// 如果触发错误,则会输出到上面的CURLOPT_ERRORBUFFER指定的buffer// 错误码解释:https://curl.se/libcurl/c/libcurl-errors.html// ************************************************************CURLcode errCode = curl_easy_perform( easyhandle );if( errCode == CURLE_OK ){}else{CCLOG( curl_errbuf );}// ************************************************************// End a libcurl easy handle,这个handle将不再有用,和curl_easy_init正好相反。// handle所使用到的connections都会关闭,除非attached to multi handle// 可能会触发progress callback或者header callback,因为有些通讯协议是请求/响应协议。比如FTP、POP3、IMAP// ************************************************************curl_easy_cleanup( easyhandle );// ************************************************************// releases resources acquired by curl_global_init.// 这不是thread-safe的,// ************************************************************curl_global_cleanup();
2、multi interface
The multi interface allows your program to transfer multiple files in both directions at the same time。注意,这是说,我们可以在单线程中调用multi接口,就有多线程异步效果,而不需要我们自己设计多线程。
The multi interface is simply a way to make multiple transfers at the same time by adding up multiple easy handles into a “multi stack”。
使用示例如下:
curl_global_init( CURL_GLOBAL_ALL );// ******************************************************************************************// create a multi handle,和curl_multi_cleanup首尾呼应。// ******************************************************************************************CURLM* multi_handle = curl_multi_init();CURL *easyhandle = curl_easy_init();curl_easy_setopt( easyhandle, optionname, ...... );// ******************************************************************************************// add an easy handle to a multi session// easy handle将压栈multi stack,由multi handle管理,禁止再调用curl_easy_perform// easy handle will use a shared connection cache owned by the multi handle// 必须调用curl_multi_remove_handle才能从stack中去除,即使eady handle compeleted也没用。// ******************************************************************************************curl_multi_add_handle(multi_handle, easyhandle);// ******************************************************************************************// 和curl_multi_add_handle相反,记得还要调用curl_easy_cleanup才能真正release the easy hanle// ******************************************************************************************curl_multi_remove_handle(multi_handle, easyhandle);curl_easy_cleanup(easyhandle);// ******************************************************************************************// reads/writes available data from each easy handle// ******************************************************************************************int runningHandles; // perform之后还在传输数据的handle数量。CURLMcode errCode = curl_multi_perform(multi_handle, &runningHandles);// ******************************************************************************************// read multi stack informationals// ******************************************************************************************CURLMsg* msg = curl_multi_info_read(multi_handle, &msgs_in_queue);// ************************************************************// close down a multi session which removes a whole multi stack.// 如果multi_handle=nulltpr,则返回ErrCode = CURLM_BAD_HANDLE// ************************************************************CURLMCode errCode = curl_multi_cleanup(multi_handle);// ************************************************************// releases resources acquired by curl_global_init.// 这不是thread-safe的,// ************************************************************curl_global_cleanup();
三、使用例子
1、下载单个文件(异步)
#include <stdio.h>#include <string.h>/* curl stuff */#include <curl/curl.h>#ifdef _WIN32#define WAITMS(x) Sleep(x)#else/* Portable sleep for platforms other than Windows. */#define WAITMS(x) \struct timeval wait = { 0, ( x ) * 1000 }; \( void ) select( 0, NULL, NULL, NULL, &wait )#endifint main(){CURL *http_handle;CURLM *multi_handle;CURLMcode errCode;int numfds; // number of file descriptorsint still_running = 0; // keep number of running handlesint repeats = 0;curl_global_init( CURL_GLOBAL_ALL );http_handle = curl_easy_init();curl_easy_setopt( http_handle, CURLOPT_URL, "https://www.example.com/" );multi_handle = curl_multi_init(); // create a multi stackcurl_multi_add_handle( multi_handle, http_handle ); // add the individual transferscurl_multi_perform( multi_handle, &still_running ); // we start some action by calling perform right awaywhile( still_running ){errCode = curl_multi_wait( multi_handle, NULL, 0, 1000, &numfds ); // wait for activity, timeout or "nothing"if( errCode != CURLM_OK ){fprintf( stderr, "curl_multi_wait() failed, code %d.\n", errCode );break;}// 'numfds' being zero means either a timeout or no file descriptors to wait for.// Try timeout on first occurrence, then assume no file descriptors and no file descriptors// to wait for means wait for 100 milliseconds.if( !numfds ){repeats++; // count number of repeated zero numfdsif( repeats > 1 ){WAITMS( 100 ); // sleep 100 milliseconds}}else{repeats = 0;}curl_multi_perform( multi_handle, &still_running );}curl_multi_remove_handle( multi_handle, http_handle );curl_easy_cleanup( http_handle );curl_multi_cleanup( multi_handle );curl_global_cleanup();}
2、并行下载多个文件(异步)
#include <errno.h>#include <stdlib.h>#include <string.h>#ifndef WIN32#include <unistd.h>#endif#include <curl/curl.h>static const char *urls[] ={"https://www.microsoft.com","https://opensource.org","https://www.google.com","https://www.yahoo.com","https://www.ibm.com","https://www.mysql.com","https://www.oracle.com","https://www.ripe.net","https://www.iana.org","https://www.amazon.com","https://www.netcraft.com","https://www.heise.de","https://www.chip.de","https://www.ca.com","https://www.cnet.com","https://www.mozilla.org","https://www.cnn.com","https://www.wikipedia.org","https://www.dell.com","https://www.hp.com","https://www.cert.org","https://www.mit.edu","https://www.nist.gov","https://www.ebay.com","https://www.playstation.com","https://www.uefa.com","https://www.ieee.org","https://www.apple.com","https://www.symantec.com","https://www.zdnet.com","https://www.fujitsu.com/global/","https://www.supermicro.com","https://www.hotmail.com","https://www.ietf.org","https://www.bbc.co.uk","https://news.google.com","https://www.foxnews.com","https://www.msn.com","https://www.wired.com","https://www.sky.com","https://www.usatoday.com","https://www.cbs.com","https://www.nbc.com/","https://slashdot.org","https://www.informationweek.com","https://apache.org","https://www.un.org",};#define MAX_PARALLEL 10#define NUM_URLS sizeof(urls)/sizeof(char *)static size_t write_cb( char *data,size_t n,size_t l,void *userp ){/* take care of the data here, ignored in this example */( void ) data;( void ) userp;return n * l;}static void add_transfer( CURLM *multi_handle, int index ){CURL *easy_handle = curl_easy_init();curl_easy_setopt( easy_handle, CURLOPT_WRITEFUNCTION, write_cb );curl_easy_setopt( easy_handle, CURLOPT_URL, urls[index] );curl_easy_setopt( easy_handle, CURLOPT_PRIVATE, urls[index] );curl_multi_add_handle( multi_handle, easy_handle );}int main( void ){CURLM *multi_handle;CURLMsg *msg;unsigned int transfers = 0;int msgs_left = -1;int still_alive = 1;curl_global_init( CURL_GLOBAL_ALL );multi_handle = curl_multi_init();// Limit the amount of simultaneous connections curl should allowcurl_multi_setopt( multi_handle, CURLMOPT_MAXCONNECTS, ( long ) MAX_PARALLEL );for( transfers = 0; transfers < MAX_PARALLEL; transfers++ ) { add_transfer( multi_handle, transfers ); }do{curl_multi_perform( multi_handle, &still_alive );while( ( msg = curl_multi_info_read( multi_handle, &msgs_left ) ) ){if( msg->msg == CURLMSG_DONE ){char *url;CURL *easy_handle = msg->easy_handle;curl_easy_getinfo( easy_handle, CURLINFO_PRIVATE, &url );fprintf( stderr, "R: %d - %s <%s>\n",msg->data.result,curl_easy_strerror( msg->data.result ),url );curl_multi_remove_handle( multi_handle, easy_handle );curl_easy_cleanup( easy_handle );}else{fprintf( stderr, "E: CURLMsg (%d)\n", msg->msg );}if( transfers < NUM_URLS ) { add_transfer( multi_handle, transfers++ ); }}if( still_alive ) { curl_multi_wait( multi_handle, NULL, 0, 1000, NULL ); }} while( still_alive || ( transfers < NUM_URLS ) );curl_multi_cleanup( multi_handle );curl_global_cleanup();return EXIT_SUCCESS;}
3、下载单个文件到本地(同步)
#include <stdio.h>#include <stdlib.h>#include <curl/curl.h>static size_t write_data( void *ptr, // 指向接收到的数据的第一个objectsize_t size, // object的数量size_t nmemb, // 每个object有几个bytevoid *stream ) // output stream,比如File{size_t written = fwrite( ptr, size, nmemb, ( FILE * ) stream );return written;}int main( int argc, char *argv[] ){CURL *curl_handle;FILE *pagefile;static const char *pagefilename = "page.out";if( argc < 2 ){printf( "Usage: %s <URL>\n", argv[0] );return 1;}curl_global_init( CURL_GLOBAL_ALL );/* init the curl session */curl_handle = curl_easy_init();/* set URL to get here */curl_easy_setopt( curl_handle, CURLOPT_URL, argv[1] );/* Switch on full protocol/debug output while testing */curl_easy_setopt( curl_handle, CURLOPT_VERBOSE, 1L );/* disable progress meter, set to 0L to enable it */curl_easy_setopt( curl_handle, CURLOPT_NOPROGRESS, 1L );/* send all data to this function */curl_easy_setopt( curl_handle, CURLOPT_WRITEFUNCTION, write_data );/* open the file */pagefile = fopen( pagefilename, "wb" );if( pagefile ){// write the page body to this file handle// 上面write function函数回调的最后一个参数curl_easy_setopt( curl_handle, CURLOPT_WRITEDATA, pagefile );/* get it! */curl_easy_perform( curl_handle );/* close the header file */fclose( pagefile );}/* cleanup curl stuff */curl_easy_cleanup( curl_handle );curl_global_cleanup();return 0;}
4、HTTP GET/POST
#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 "); // 指定urlcurl_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");}
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);// 设置文件续传的位置给libcurlcurl_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;}
