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。

  1. curl_easy_***(...); // 这是easy版接口
  2. 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

使用示例如下:

  1. // ************************************************************
  2. // libcurl的全局初始化,在我们的程序中只需调用一次,和curl_global_cleanup首尾呼应。
  3. // 注意,这不是thread-safe的,确保在main thread执行。
  4. // ************************************************************
  5. curl_global_init( CURL_GLOBAL_ALL );
  6. // ************************************************************
  7. // 获取当前libcurl的版本信息,一般用于我们检查是否是合适的版本。
  8. // ************************************************************
  9. curl_version_info_data *data = curl_version_info( CURLVERSION_NOW );
  10. // ************************************************************
  11. // create an easy handle which starts a libcurl easy session
  12. // 必须和curl_easy_cleanup()首尾呼应。
  13. // ************************************************************
  14. CURL *easyhandle = curl_easy_init();
  15. // ************************************************************
  16. // 设置选项,和OpenGL的State类似,只要设置了就一直保持,除非显式改变。
  17. // API手册:https://curl.se/libcurl/c/curl_easy_setopt.html
  18. // 每个option的用法:https://curl.se/libcurl/c/****.html,将**替换成optionname
  19. // 每个option的例子:https://curl.se/libcurl/c/options-in-examples.html
  20. // ************************************************************
  21. curl_easy_setopt( easyhandle, optionname, ...... );
  22. // 设置error msg的输出error buffer,用于我们debug。
  23. char curl_errbuf[CURL_ERROR_SIZE];
  24. curl_easy_setopt( easyhandle, CURLOPT_ERRORBUFFER, curl_errbuf );
  25. // ************************************************************
  26. // 重置这个handle的所有选项至默认。
  27. // ************************************************************
  28. curl_easy_reset( easyhandle );
  29. // ************************************************************
  30. // 复制一个全新独立的handle,option也会复制。
  31. // ************************************************************
  32. CURL *copyEasyHandle = curl_easy_duphandle( easyhandle );
  33. // ************************************************************
  34. // perform a blocking(阻塞) file transfer,将会阻塞,直到transfer完成。
  35. // 如果触发错误,则会输出到上面的CURLOPT_ERRORBUFFER指定的buffer
  36. // 错误码解释:https://curl.se/libcurl/c/libcurl-errors.html
  37. // ************************************************************
  38. CURLcode errCode = curl_easy_perform( easyhandle );
  39. if( errCode == CURLE_OK )
  40. {
  41. }
  42. else
  43. {
  44. CCLOG( curl_errbuf );
  45. }
  46. // ************************************************************
  47. // End a libcurl easy handle,这个handle将不再有用,和curl_easy_init正好相反。
  48. // handle所使用到的connections都会关闭,除非attached to multi handle
  49. // 可能会触发progress callback或者header callback,因为有些通讯协议是请求/响应协议。比如FTP、POP3、IMAP
  50. // ************************************************************
  51. curl_easy_cleanup( easyhandle );
  52. // ************************************************************
  53. // releases resources acquired by curl_global_init.
  54. // 这不是thread-safe的,
  55. // ************************************************************
  56. 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”。
使用示例如下:

  1. curl_global_init( CURL_GLOBAL_ALL );
  2. // ******************************************************************************************
  3. // create a multi handle,和curl_multi_cleanup首尾呼应。
  4. // ******************************************************************************************
  5. CURLM* multi_handle = curl_multi_init();
  6. CURL *easyhandle = curl_easy_init();
  7. curl_easy_setopt( easyhandle, optionname, ...... );
  8. // ******************************************************************************************
  9. // add an easy handle to a multi session
  10. // easy handle将压栈multi stack,由multi handle管理,禁止再调用curl_easy_perform
  11. // easy handle will use a shared connection cache owned by the multi handle
  12. // 必须调用curl_multi_remove_handle才能从stack中去除,即使eady handle compeleted也没用。
  13. // ******************************************************************************************
  14. curl_multi_add_handle(multi_handle, easyhandle);
  15. // ******************************************************************************************
  16. // 和curl_multi_add_handle相反,记得还要调用curl_easy_cleanup才能真正release the easy hanle
  17. // ******************************************************************************************
  18. curl_multi_remove_handle(multi_handle, easyhandle);
  19. curl_easy_cleanup(easyhandle);
  20. // ******************************************************************************************
  21. // reads/writes available data from each easy handle
  22. // ******************************************************************************************
  23. int runningHandles; // perform之后还在传输数据的handle数量。
  24. CURLMcode errCode = curl_multi_perform(multi_handle, &runningHandles);
  25. // ******************************************************************************************
  26. // read multi stack informationals
  27. // ******************************************************************************************
  28. CURLMsg* msg = curl_multi_info_read(multi_handle, &msgs_in_queue);
  29. // ************************************************************
  30. // close down a multi session which removes a whole multi stack.
  31. // 如果multi_handle=nulltpr,则返回ErrCode = CURLM_BAD_HANDLE
  32. // ************************************************************
  33. CURLMCode errCode = curl_multi_cleanup(multi_handle);
  34. // ************************************************************
  35. // releases resources acquired by curl_global_init.
  36. // 这不是thread-safe的,
  37. // ************************************************************
  38. curl_global_cleanup();

三、使用例子

1、下载单个文件(异步)

  1. #include <stdio.h>
  2. #include <string.h>
  3. /* curl stuff */
  4. #include <curl/curl.h>
  5. #ifdef _WIN32
  6. #define WAITMS(x) Sleep(x)
  7. #else
  8. /* Portable sleep for platforms other than Windows. */
  9. #define WAITMS(x) \
  10. struct timeval wait = { 0, ( x ) * 1000 }; \
  11. ( void ) select( 0, NULL, NULL, NULL, &wait )
  12. #endif
  13. int main()
  14. {
  15. CURL *http_handle;
  16. CURLM *multi_handle;
  17. CURLMcode errCode;
  18. int numfds; // number of file descriptors
  19. int still_running = 0; // keep number of running handles
  20. int repeats = 0;
  21. curl_global_init( CURL_GLOBAL_ALL );
  22. http_handle = curl_easy_init();
  23. curl_easy_setopt( http_handle, CURLOPT_URL, "https://www.example.com/" );
  24. multi_handle = curl_multi_init(); // create a multi stack
  25. curl_multi_add_handle( multi_handle, http_handle ); // add the individual transfers
  26. curl_multi_perform( multi_handle, &still_running ); // we start some action by calling perform right away
  27. while( still_running )
  28. {
  29. errCode = curl_multi_wait( multi_handle, NULL, 0, 1000, &numfds ); // wait for activity, timeout or "nothing"
  30. if( errCode != CURLM_OK )
  31. {
  32. fprintf( stderr, "curl_multi_wait() failed, code %d.\n", errCode );
  33. break;
  34. }
  35. // 'numfds' being zero means either a timeout or no file descriptors to wait for.
  36. // Try timeout on first occurrence, then assume no file descriptors and no file descriptors
  37. // to wait for means wait for 100 milliseconds.
  38. if( !numfds )
  39. {
  40. repeats++; // count number of repeated zero numfds
  41. if( repeats > 1 )
  42. {
  43. WAITMS( 100 ); // sleep 100 milliseconds
  44. }
  45. }
  46. else
  47. {
  48. repeats = 0;
  49. }
  50. curl_multi_perform( multi_handle, &still_running );
  51. }
  52. curl_multi_remove_handle( multi_handle, http_handle );
  53. curl_easy_cleanup( http_handle );
  54. curl_multi_cleanup( multi_handle );
  55. curl_global_cleanup();
  56. }

2、并行下载多个文件(异步)

  1. #include <errno.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #ifndef WIN32
  5. #include <unistd.h>
  6. #endif
  7. #include <curl/curl.h>
  8. static const char *urls[] =
  9. {
  10. "https://www.microsoft.com",
  11. "https://opensource.org",
  12. "https://www.google.com",
  13. "https://www.yahoo.com",
  14. "https://www.ibm.com",
  15. "https://www.mysql.com",
  16. "https://www.oracle.com",
  17. "https://www.ripe.net",
  18. "https://www.iana.org",
  19. "https://www.amazon.com",
  20. "https://www.netcraft.com",
  21. "https://www.heise.de",
  22. "https://www.chip.de",
  23. "https://www.ca.com",
  24. "https://www.cnet.com",
  25. "https://www.mozilla.org",
  26. "https://www.cnn.com",
  27. "https://www.wikipedia.org",
  28. "https://www.dell.com",
  29. "https://www.hp.com",
  30. "https://www.cert.org",
  31. "https://www.mit.edu",
  32. "https://www.nist.gov",
  33. "https://www.ebay.com",
  34. "https://www.playstation.com",
  35. "https://www.uefa.com",
  36. "https://www.ieee.org",
  37. "https://www.apple.com",
  38. "https://www.symantec.com",
  39. "https://www.zdnet.com",
  40. "https://www.fujitsu.com/global/",
  41. "https://www.supermicro.com",
  42. "https://www.hotmail.com",
  43. "https://www.ietf.org",
  44. "https://www.bbc.co.uk",
  45. "https://news.google.com",
  46. "https://www.foxnews.com",
  47. "https://www.msn.com",
  48. "https://www.wired.com",
  49. "https://www.sky.com",
  50. "https://www.usatoday.com",
  51. "https://www.cbs.com",
  52. "https://www.nbc.com/",
  53. "https://slashdot.org",
  54. "https://www.informationweek.com",
  55. "https://apache.org",
  56. "https://www.un.org",
  57. };
  58. #define MAX_PARALLEL 10
  59. #define NUM_URLS sizeof(urls)/sizeof(char *)
  60. static size_t write_cb( char *data,
  61. size_t n,
  62. size_t l,
  63. void *userp )
  64. {
  65. /* take care of the data here, ignored in this example */
  66. ( void ) data;
  67. ( void ) userp;
  68. return n * l;
  69. }
  70. static void add_transfer( CURLM *multi_handle, int index )
  71. {
  72. CURL *easy_handle = curl_easy_init();
  73. curl_easy_setopt( easy_handle, CURLOPT_WRITEFUNCTION, write_cb );
  74. curl_easy_setopt( easy_handle, CURLOPT_URL, urls[index] );
  75. curl_easy_setopt( easy_handle, CURLOPT_PRIVATE, urls[index] );
  76. curl_multi_add_handle( multi_handle, easy_handle );
  77. }
  78. int main( void )
  79. {
  80. CURLM *multi_handle;
  81. CURLMsg *msg;
  82. unsigned int transfers = 0;
  83. int msgs_left = -1;
  84. int still_alive = 1;
  85. curl_global_init( CURL_GLOBAL_ALL );
  86. multi_handle = curl_multi_init();
  87. // Limit the amount of simultaneous connections curl should allow
  88. curl_multi_setopt( multi_handle, CURLMOPT_MAXCONNECTS, ( long ) MAX_PARALLEL );
  89. for( transfers = 0; transfers < MAX_PARALLEL; transfers++ ) { add_transfer( multi_handle, transfers ); }
  90. do
  91. {
  92. curl_multi_perform( multi_handle, &still_alive );
  93. while( ( msg = curl_multi_info_read( multi_handle, &msgs_left ) ) )
  94. {
  95. if( msg->msg == CURLMSG_DONE )
  96. {
  97. char *url;
  98. CURL *easy_handle = msg->easy_handle;
  99. curl_easy_getinfo( easy_handle, CURLINFO_PRIVATE, &url );
  100. fprintf( stderr, "R: %d - %s <%s>\n",
  101. msg->data.result,
  102. curl_easy_strerror( msg->data.result ),
  103. url );
  104. curl_multi_remove_handle( multi_handle, easy_handle );
  105. curl_easy_cleanup( easy_handle );
  106. }
  107. else
  108. {
  109. fprintf( stderr, "E: CURLMsg (%d)\n", msg->msg );
  110. }
  111. if( transfers < NUM_URLS ) { add_transfer( multi_handle, transfers++ ); }
  112. }
  113. if( still_alive ) { curl_multi_wait( multi_handle, NULL, 0, 1000, NULL ); }
  114. } while( still_alive || ( transfers < NUM_URLS ) );
  115. curl_multi_cleanup( multi_handle );
  116. curl_global_cleanup();
  117. return EXIT_SUCCESS;
  118. }

3、下载单个文件到本地(同步)

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <curl/curl.h>
  4. static size_t write_data( void *ptr, // 指向接收到的数据的第一个object
  5. size_t size, // object的数量
  6. size_t nmemb, // 每个object有几个byte
  7. void *stream ) // output stream,比如File
  8. {
  9. size_t written = fwrite( ptr, size, nmemb, ( FILE * ) stream );
  10. return written;
  11. }
  12. int main( int argc, char *argv[] )
  13. {
  14. CURL *curl_handle;
  15. FILE *pagefile;
  16. static const char *pagefilename = "page.out";
  17. if( argc < 2 )
  18. {
  19. printf( "Usage: %s <URL>\n", argv[0] );
  20. return 1;
  21. }
  22. curl_global_init( CURL_GLOBAL_ALL );
  23. /* init the curl session */
  24. curl_handle = curl_easy_init();
  25. /* set URL to get here */
  26. curl_easy_setopt( curl_handle, CURLOPT_URL, argv[1] );
  27. /* Switch on full protocol/debug output while testing */
  28. curl_easy_setopt( curl_handle, CURLOPT_VERBOSE, 1L );
  29. /* disable progress meter, set to 0L to enable it */
  30. curl_easy_setopt( curl_handle, CURLOPT_NOPROGRESS, 1L );
  31. /* send all data to this function */
  32. curl_easy_setopt( curl_handle, CURLOPT_WRITEFUNCTION, write_data );
  33. /* open the file */
  34. pagefile = fopen( pagefilename, "wb" );
  35. if( pagefile )
  36. {
  37. // write the page body to this file handle
  38. // 上面write function函数回调的最后一个参数
  39. curl_easy_setopt( curl_handle, CURLOPT_WRITEDATA, pagefile );
  40. /* get it! */
  41. curl_easy_perform( curl_handle );
  42. /* close the header file */
  43. fclose( pagefile );
  44. }
  45. /* cleanup curl stuff */
  46. curl_easy_cleanup( curl_handle );
  47. curl_global_cleanup();
  48. return 0;
  49. }

4、HTTP GET/POST

  1. #include <stdio.h>
  2. #include <curl/curl.h>
  3. bool getUrl(char *filename)
  4. {
  5. CURL *curl;
  6. CURLcode res;
  7. FILE *fp;
  8. if ((fp = fopen(filename, "w")) == NULL) // 返回结果用文件存储
  9. return false;
  10. struct curl_slist *headers = NULL;
  11. headers = curl_slist_append(headers, "Accept: Agent-007");
  12. curl = curl_easy_init(); // 初始化
  13. if (curl)
  14. {
  15. //curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:8080");// 代理
  16. curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);// 改协议头
  17. curl_easy_setopt(curl, CURLOPT_URL,"http://www.baidu.com");
  18. curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); //将返回的http头输出到fp指向的文件
  19. curl_easy_setopt(curl, CURLOPT_HEADERDATA, fp); //将返回的html主体数据输出到fp指向的文件
  20. res = curl_easy_perform(curl); // 执行
  21. if (res != 0) {
  22. curl_slist_free_all(headers);
  23. curl_easy_cleanup(curl);
  24. }
  25. fclose(fp);
  26. return true;
  27. }
  28. }
  29. bool postUrl(char *filename)
  30. {
  31. CURL *curl;
  32. CURLcode res;
  33. FILE *fp;
  34. if ((fp = fopen(filename, "w")) == NULL)
  35. return false;
  36. curl = curl_easy_init();
  37. if (curl)
  38. {
  39. curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt"); // 指定cookie文件
  40. curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "&logintype=uid&u=xieyan&psw=xxx86"); // 指定post内容
  41. //curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:8080");
  42. curl_easy_setopt(curl, CURLOPT_URL, " http://mail.sina.com.cn/cgi-bin/login.cgi "); // 指定url
  43. curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
  44. res = curl_easy_perform(curl);
  45. curl_easy_cleanup(curl);
  46. }
  47. fclose(fp);
  48. return true;
  49. }
  50. int main(void)
  51. {
  52. getUrl("/tmp/get.html");
  53. postUrl("/tmp/post.html");
  54. }

5、断点续传

  1. //采用CURLOPT_RESUME_FROM_LARGE 实现文件断点续传功能
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <sys/stat.h>
  5. #include <curl/curl.h>
  6. //这个函数为CURLOPT_HEADERFUNCTION参数构造
  7. /* 从http头部获取文件size*/
  8. size_t getcontentlengthfunc(void *ptr, size_t size, size_t nmemb, void *stream) {
  9. int r;
  10. long len = 0;
  11. /* _snscanf() is Win32 specific */
  12. // r = _snscanf(ptr, size * nmemb, "Content-Length: %ld\n", &len);
  13. r = sscanf(ptr, "Content-Length: %ld\n", &len);
  14. if (r) /* Microsoft: we don't read the specs */
  15. *((long *) stream) = len;
  16. return size * nmemb;
  17. }
  18. /* 保存下载文件 */
  19. size_t wirtefunc(void *ptr, size_t size, size_t nmemb, void *stream)
  20. {
  21. return fwrite(ptr, size, nmemb, stream);
  22. }
  23. /*读取上传文件 */
  24. size_t readfunc(void *ptr, size_t size, size_t nmemb, void *stream)
  25. {
  26. FILE *f = stream;
  27. size_t n;
  28. if (ferror(f))
  29. return CURL_READFUNC_ABORT;
  30. n = fread(ptr, size, nmemb, f) * size;
  31. return n;
  32. }
  33. // 下载 或者上传文件函数
  34. int download(CURL *curlhandle, const char * remotepath, const char * localpath,
  35. long timeout, long tries)
  36. {
  37. FILE *f;
  38. curl_off_t local_file_len = -1 ;
  39. long filesize =0 ;
  40. CURLcode r = CURLE_GOT_NOTHING;
  41. int c;
  42. struct stat file_info;
  43. int use_resume = 0;
  44. /* 得到本地文件大小 */
  45. //if(access(localpath,F_OK) ==0)
  46. if(stat(localpath, &file_info) == 0)
  47. {
  48. local_file_len = file_info.st_size;
  49. use_resume = 1;
  50. }
  51. //采用追加方式打开文件,便于实现文件断点续传工作
  52. f = fopen(localpath, "ab+");
  53. if (f == NULL) {
  54. perror(NULL);
  55. return 0;
  56. }
  57. //curl_easy_setopt(curlhandle, CURLOPT_UPLOAD, 1L);
  58. curl_easy_setopt(curlhandle, CURLOPT_URL, remotepath);
  59. curl_easy_setopt(curlhandle, CURLOPT_CONNECTTIMEOUT, timeout); // 设置连接超时,单位秒
  60. //设置http 头部处理函数
  61. curl_easy_setopt(curlhandle, CURLOPT_HEADERFUNCTION, getcontentlengthfunc);
  62. curl_easy_setopt(curlhandle, CURLOPT_HEADERDATA, &filesize);
  63. // 设置文件续传的位置给libcurl
  64. curl_easy_setopt(curlhandle, CURLOPT_RESUME_FROM_LARGE, use_resume?local_file_len:0);
  65. curl_easy_setopt(curlhandle, CURLOPT_WRITEDATA, f);
  66. curl_easy_setopt(curlhandle, CURLOPT_WRITEFUNCTION, wirtefunc);
  67. //curl_easy_setopt(curlhandle, CURLOPT_READFUNCTION, readfunc);
  68. //curl_easy_setopt(curlhandle, CURLOPT_READDATA, f);
  69. curl_easy_setopt(curlhandle, CURLOPT_NOPROGRESS, 1L);
  70. curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 1L);
  71. r = curl_easy_perform(curlhandle);
  72. fclose(f);
  73. if (r == CURLE_OK)
  74. return 1;
  75. else {
  76. fprintf(stderr, "%s\n", curl_easy_strerror(r));
  77. return 0;
  78. }
  79. }
  80. int main(int c, char **argv) {
  81. CURL *curlhandle = NULL;
  82. curl_global_init(CURL_GLOBAL_ALL);
  83. curlhandle = curl_easy_init();
  84. //download(curlhandle, "ftp://user:pass@host/path/file", "C:\\file", 0, 3);
  85. download(curlhandle , "http://software.sky-union.cn/index.asp","/work/index.asp",1,3);
  86. curl_easy_cleanup(curlhandle);
  87. curl_global_cleanup();
  88. return 0;
  89. }

6、更多

https://curl.se/libcurl/c/example.html