curl是一款利用URL语法进行文件传输的工具,它支持多种协议,包括FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET等,我们既可以在命令行上使用它,也可以利用 libcurl进行相关编程。相信大部分同学都应该使用过libcurl的easy 接口,easy接口的使用非常的简单,curl_easy_init用来初始化一个easy curl对象,curl_easy_setopt对easy curl对象进行相关设置,最后curl_easy_perform执行curl请求,返回相应结果。easy接口是阻塞的,也就是说必须等到上一个curl请求执行完后,下一个curl请求才能继续执行,在一般的应用场合,这种阻塞的访问方式是没有问题的,但是当程序需要进行多次curl并发请求的时候,easy接口就无能为力了,这个时候curl提供的multi接口就派上用场了,网上关于libcurl的multi接口的使用资料比较少(百度出来的大部分都是php multi curl的资料),curl官网上貌似也只有相关函数的说明,有实际demo才能让我们更快速的上手使用,所以下面结合实际例子来讲讲multi curl接口的使用方法。

相比而言,multi接口的使用会比easy 接口稍微复杂点,毕竟multi接口是依赖easy接口的,首先粗略的讲下其使用流程:curl_multi _init初始化一个multi curl对象,为了同时进行多个curl的并发访问,我们需要初始化多个easy curl对象,使用curl_easy_setopt进行相关设置,然后调用curl_multi _add_handle把easy curl对象添加到multi curl对象中,添加完毕后执行curl_multi_perform方法进行并发的访问,访问结束后curl_multi_remove_handle移除相关easy curl对象,curl_easy_cleanup清除easy curl对象,最后curl_multi_cleanup清除multi curl对象。

上面的介绍只是给大家一个大概的印象,实际使用中还有很多细节需要注意,好了,代码才能说明一切,下面的例子使用multi curl方式进行多次http并发访问,并输出访问结果。

  1. #include <string>
  2. #include <iostream>
  3. #include <curl/curl.h>
  4. #include <sys/time.h>
  5. #include <unistd.h>
  6. using namespace std;
  7. size_t curl_writer(void *buffer, size_t size, size_t count, void * stream)
  8. {
  9. std::string * pStream = static_cast<std::string *>(stream);
  10. (*pStream).append((char *)buffer, size * count);
  11. return size * count;
  12. };
  13. /**
  14. * 生成一个easy curl对象,进行一些简单的设置操作
  15. */
  16. CURL * curl_easy_handler(const std::string & sUrl,
  17. const std::string & sProxy,
  18. std::string & sRsp,
  19. unsigned int uiTimeout)
  20. {
  21. CURL * curl = curl_easy_init();
  22. curl_easy_setopt(curl, CURLOPT_URL, sUrl.c_str());
  23. curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
  24. if (uiTimeout > 0)
  25. {
  26. curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, uiTimeout);
  27. }
  28. if (!sProxy.empty())
  29. {
  30. curl_easy_setopt(curl, CURLOPT_PROXY, sProxy.c_str());
  31. }
  32. // write function //
  33. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_writer);
  34. curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sRsp);
  35. return curl;
  36. }
  37. /**
  38. * 使用select函数监听multi curl文件描述符的状态
  39. * 监听成功返回0,监听失败返回-1
  40. */
  41. int curl_multi_select(CURLM * curl_m)
  42. {
  43. int ret = 0;
  44. struct timeval timeout_tv;
  45. fd_set  fd_read;
  46. fd_set  fd_write;
  47. fd_set  fd_except;
  48. int     max_fd = -1;
  49. // 注意这里一定要清空fdset,curl_multi_fdset不会执行fdset的清空操作  //
  50. FD_ZERO(&fd_read);
  51. FD_ZERO(&fd_write);
  52. FD_ZERO(&fd_except);
  53. // 设置select超时时间  //
  54. timeout_tv.tv_sec = 1;
  55. timeout_tv.tv_usec = 0;
  56. // 获取multi curl需要监听的文件描述符集合 fd_set //
  57. curl_multi_fdset(curl_m, &fd_read, &fd_write, &fd_except, &max_fd);
  58. /**
  59. * When max_fd returns with -1,
  60. * you need to wait a while and then proceed and call curl_multi_perform anyway.
  61. * How long to wait? I would suggest 100 milliseconds at least,
  62. * but you may want to test it out in your own particular conditions to find a suitable value.
  63. */
  64. if (-1 == max_fd)
  65. {
  66. return -1;
  67. }
  68. /**
  69. * 执行监听,当文件描述符状态发生改变的时候返回
  70. * 返回0,程序调用curl_multi_perform通知curl执行相应操作
  71. * 返回-1,表示select错误
  72. * 注意:即使select超时也需要返回0,具体可以去官网看文档说明
  73. */
  74. int ret_code = ::select(max_fd + 1, &fd_read, &fd_write, &fd_except, &timeout_tv);
  75. switch(ret_code)
  76. {
  77. case -1:
  78. /* select error */
  79. ret = -1;
  80. break;
  81. case 0:
  82. /* select timeout */
  83. default:
  84. /* one or more of curl's file descriptors say there's data to read or write*/
  85. ret = 0;
  86. break;
  87. }
  88. return ret;
  89. }
  90. #define MULTI_CURL_NUM 3
  91. // 这里设置你需要访问的url //
  92. std::string     URL     = "http://website.com";
  93. // 这里设置代理ip和端口  //
  94. std::string     PROXY   = "ip:port";
  95. // 这里设置超时时间  //
  96. unsigned int    TIMEOUT = 2000; /* ms */
  97. /**
  98. * multi curl使用demo
  99. */
  100. int curl_multi_demo(int num)
  101. {
  102. // 初始化一个multi curl 对象 //
  103. CURLM * curl_m = curl_multi_init();
  104. std::string     RspArray[num];
  105. CURL *          CurlArray[num];
  106. // 设置easy curl对象并添加到multi curl对象中  //
  107. for (int idx = 0; idx < num; ++idx)
  108. {
  109. CurlArray[idx] = NULL;
  110. CurlArray[idx] = curl_easy_handler(URL, PROXY, RspArray[idx], TIMEOUT);
  111. if (CurlArray[idx] == NULL)
  112. {
  113. return -1;
  114. }
  115. curl_multi_add_handle(curl_m, CurlArray[idx]);
  116. }
  117. /*
  118. * 调用curl_multi_perform函数执行curl请求
  119. * url_multi_perform返回CURLM_CALL_MULTI_PERFORM时,表示需要继续调用该函数直到返回值不是CURLM_CALL_MULTI_PERFORM为止
  120. * running_handles变量返回正在处理的easy curl数量,running_handles为0表示当前没有正在执行的curl请求
  121. */
  122. int running_handles;
  123. while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl_m, &running_handles))
  124. {
  125. cout << running_handles << endl;
  126. }
  127. /**
  128. * 为了避免循环调用curl_multi_perform产生的cpu持续占用的问题,采用select来监听文件描述符
  129. */
  130. while (running_handles)
  131. {
  132. if (-1 == curl_multi_select(curl_m))
  133. {
  134. cerr << "select error" << endl;
  135. break;
  136. } else {
  137. // select监听到事件,调用curl_multi_perform通知curl执行相应的操作 //
  138. while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl_m, &running_handles))
  139. {
  140. cout << "select: " << running_handles << endl;
  141. }
  142. }
  143. cout << "select: " << running_handles << endl;
  144. }
  145. // 输出执行结果 //
  146. int         msgs_left;
  147. CURLMsg *   msg;
  148. while((msg = curl_multi_info_read(curl_m, &msgs_left)))
  149. {
  150. if (CURLMSG_DONE == msg->msg)
  151. {
  152. int idx;
  153. for (idx = 0; idx < num; ++idx)
  154. {
  155. if (msg->easy_handle == CurlArray[idx]) break;
  156. }
  157. if (idx == num)
  158. {
  159. cerr << "curl not found" << endl;
  160. } else
  161. {
  162. cout << "curl [" << idx << "] completed with status: "
  163. << msg->data.result << endl;
  164. cout << "rsp: " << RspArray[idx] << endl;
  165. }
  166. }
  167. }
  168. // 这里要注意cleanup的顺序 //
  169. for (int idx = 0; idx < num; ++idx)
  170. {
  171. curl_multi_remove_handle(curl_m, CurlArray[idx]);
  172. }
  173. for (int idx = 0; idx < num; ++idx)
  174. {
  175. curl_easy_cleanup(CurlArray[idx]);
  176. }
  177. curl_multi_cleanup(curl_m);
  178. return 0;
  179. }
  180. /**
  181. * easy curl使用demo
  182. */
  183. int curl_easy_demo(int num)
  184. {
  185. std::string     RspArray[num];
  186. for (int idx = 0; idx < num; ++idx)
  187. {
  188. CURL * curl = curl_easy_handler(URL, PROXY, RspArray[idx], TIMEOUT);
  189. CURLcode code = curl_easy_perform(curl);
  190. cout << "curl [" << idx << "] completed with status: "
  191. << code << endl;
  192. cout << "rsp: " << RspArray[idx] << endl;
  193. // clear handle //
  194. curl_easy_cleanup(curl);
  195. }
  196. return 0;
  197. }
  198. #define USE_MULTI_CURL
  199. struct timeval begin_tv, end_tv;
  200. int main(int argc, char * argv[])
  201. {
  202. if (argc < 2)
  203. {
  204. return -1;
  205. }
  206. int num = atoi(argv[1]);
  207. // 获取开始时间 //
  208. gettimeofday(&begin_tv, NULL);
  209. #ifdef USE_MULTI_CURL
  210. // 使用multi接口进行访问 //
  211. curl_multi_demo(num);
  212. #else
  213. // 使用easy接口进行访问 //
  214. curl_easy_demo(num);
  215. #endif
  216. // 获取结束时间  //
  217. struct timeval end_tv;
  218. gettimeofday(&end_tv, NULL);
  219. // 计算执行延时并输出,用于比较  //
  220. int eclapsed = (end_tv.tv_sec - begin_tv.tv_sec) * 1000 +
  221. (end_tv.tv_usec - begin_tv.tv_usec) / 1000;
  222. cout << "eclapsed time:" << eclapsed << "ms" << endl;
  223. return 0;
  224. }

使用multi curl进行http并发访问的更多相关文章

  1. php-cgi和php-fpm,Windows环境下解决Nginx+php并发访问阻塞问题。

    php-cgi 是运行php,php-fpm是守护php-cgi进程 nginx配置目录运行php        location  ~ \.php$        {                 ...

  2. Java多线程基础——对象及变量并发访问

    在开发多线程程序时,如果每个多线程处理的事情都不一样,每个线程都互不相关,这样开发的过程就非常轻松.但是很多时候,多线程程序是需要同时访问同一个对象,或者变量的.这样,一个对象同时被多个线程访问,会出 ...

  3. (实例篇)php 使用redis锁限制并发访问类示例

    1.并发访问限制问题 对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功. 例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制 ...

  4. Java多线程编程核心技术---对象及变量的并发访问(一)

    synchronized同步方法 "非线程安全"其实会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是"脏读",也就是渠道的数据其实是被更改 ...

  5. php 使用redis锁限制并发访问类

    1.并发访问限制问题 对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功. 例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制 ...

  6. Spring并发访问的线程安全性问题

    Spring并发访问的线程安全性问题 http://windows9834.blog.163.com/blog/static/27345004201391045539953/ 由于Spring MVC ...

  7. 高并发访问mysql时的问题(一):库存超减

    如果在对某行记录的更新时不采取任何防范措施,在多线程访问时,就容易出现库存为负数的错误. 以下用php.mysql,apache ab工具举例说明: mysql表结构 CREATE TABLE `yx ...

  8. Oracle 数据库中不同事务并发访问的问题

    现象 以SQL/Helper为例,打开不同的SQL窗口,对同一个表格进行操作,如下所示. 窗口1:当执行更新任务.紧接着执行查询时获得一组查询结果.结果是对的. 窗口2:而在另外一个SQL查询窗口中执 ...

  9. [转载]socket下server端支持多客户端并发访问简单实现

    /*Author: wainiwann *Source: 博客园 http://www.cnblogs.com/wainiwann *Remarks:  转载请说明出处!!! */ 感觉很不错,可以学 ...

随机推荐

  1. ClickJacking(点击劫持)

    问题: 点击劫持(ClickJacking)是一种视觉上的欺骗手段.大概有两种方式,一是攻击者使用一个透明的iframe,覆盖在一个网页上,然后诱使用户在该页面上进行操作,此时用户将在不知情的情况下点 ...

  2. c# 调用打印机

    1.本地打印机 //添加引用并using System.Management; public static void AvailablePrinters() { ManagementScope ms ...

  3. CLR执行模式之程序集代码的执行

    所知IL是与CPU无关的机器语言,其能访问和操作对象类型,并提供指令来创建和初始化对象,调用对象上的虚方法以及直接操作数组对象等,故可视为一种面向对象的机器语言.每种语言的存在都有其存在的价值和原因, ...

  4. [tools]camtasia studio8.6

    实际情况: 装了汉化补丁包后坏事,最后还是没装汉化补丁包.直接用英文版的.安装过程中输入序列号即可激活. ):输入注册码安装 用户名:大眼仔~旭(Anan) 注册码:GCABC-CPCCE-BPMMB ...

  5. ES5基础01:正则表达式

    1.功能 匹配特定模式:比如匹配手机号码,匹配身份证号码等 替换文本:比如将input中的空格全部去掉 提取字符串:将特定的字符串提取出来 2.语法

  6. <转载> 你应该更新的Java知识之常用程序库(一)

    原文出处:http://www.blogbus.com/dreamhead-logs/226738702.html 很多人眼中,Java已经是一门垂垂老矣的语言,但并不妨碍Java世界依然在前进.如果 ...

  7. html:关于表单功能的学习

    比如我在某jsp页面中写了如下表单: <form action="/MavenWeb/TestFormPost" method="get">   & ...

  8. 《Linux内核设计与实现》 Chapter4 读书笔记

    <Linux内核设计与实现> Chapter4 读书笔记 调度程序负责决定将哪个进程投入运行,何时运行以及运行多长时间,进程调度程序可看做在可运行态进程之间分配有限的处理器时间资源的内核子 ...

  9. [译]Canvas的基本用法

    在本文章中 <canvas> 元素 替换内容 </canvas> 标签不可省 渲染上下文(The rendering context如何翻译) 检查支持性 一个模板骨架 一个简 ...

  10. [C#详解] (1) 自动属性、初始化器、扩展方法

    文章来源:Slark.NET-博客园 http://www.cnblogs.com/slark/p/CSharp-focus-1.html 代码下载:点我下载 目录 前言 属性与自动属性 属性 自动属 ...