使用multi curl进行http并发访问
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并发访问,并输出访问结果。
- #include <string>
- #include <iostream>
- #include <curl/curl.h>
- #include <sys/time.h>
- #include <unistd.h>
- using namespace std;
- size_t curl_writer(void *buffer, size_t size, size_t count, void * stream)
- {
- std::string * pStream = static_cast<std::string *>(stream);
- (*pStream).append((char *)buffer, size * count);
- return size * count;
- };
- /**
- * 生成一个easy curl对象,进行一些简单的设置操作
- */
- CURL * curl_easy_handler(const std::string & sUrl,
- const std::string & sProxy,
- std::string & sRsp,
- unsigned int uiTimeout)
- {
- CURL * curl = curl_easy_init();
- curl_easy_setopt(curl, CURLOPT_URL, sUrl.c_str());
- curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
- if (uiTimeout > 0)
- {
- curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, uiTimeout);
- }
- if (!sProxy.empty())
- {
- curl_easy_setopt(curl, CURLOPT_PROXY, sProxy.c_str());
- }
- // write function //
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_writer);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sRsp);
- return curl;
- }
- /**
- * 使用select函数监听multi curl文件描述符的状态
- * 监听成功返回0,监听失败返回-1
- */
- int curl_multi_select(CURLM * curl_m)
- {
- int ret = 0;
- struct timeval timeout_tv;
- fd_set fd_read;
- fd_set fd_write;
- fd_set fd_except;
- int max_fd = -1;
- // 注意这里一定要清空fdset,curl_multi_fdset不会执行fdset的清空操作 //
- FD_ZERO(&fd_read);
- FD_ZERO(&fd_write);
- FD_ZERO(&fd_except);
- // 设置select超时时间 //
- timeout_tv.tv_sec = 1;
- timeout_tv.tv_usec = 0;
- // 获取multi curl需要监听的文件描述符集合 fd_set //
- curl_multi_fdset(curl_m, &fd_read, &fd_write, &fd_except, &max_fd);
- /**
- * When max_fd returns with -1,
- * you need to wait a while and then proceed and call curl_multi_perform anyway.
- * How long to wait? I would suggest 100 milliseconds at least,
- * but you may want to test it out in your own particular conditions to find a suitable value.
- */
- if (-1 == max_fd)
- {
- return -1;
- }
- /**
- * 执行监听,当文件描述符状态发生改变的时候返回
- * 返回0,程序调用curl_multi_perform通知curl执行相应操作
- * 返回-1,表示select错误
- * 注意:即使select超时也需要返回0,具体可以去官网看文档说明
- */
- int ret_code = ::select(max_fd + 1, &fd_read, &fd_write, &fd_except, &timeout_tv);
- switch(ret_code)
- {
- case -1:
- /* select error */
- ret = -1;
- break;
- case 0:
- /* select timeout */
- default:
- /* one or more of curl's file descriptors say there's data to read or write*/
- ret = 0;
- break;
- }
- return ret;
- }
- #define MULTI_CURL_NUM 3
- // 这里设置你需要访问的url //
- std::string URL = "http://website.com";
- // 这里设置代理ip和端口 //
- std::string PROXY = "ip:port";
- // 这里设置超时时间 //
- unsigned int TIMEOUT = 2000; /* ms */
- /**
- * multi curl使用demo
- */
- int curl_multi_demo(int num)
- {
- // 初始化一个multi curl 对象 //
- CURLM * curl_m = curl_multi_init();
- std::string RspArray[num];
- CURL * CurlArray[num];
- // 设置easy curl对象并添加到multi curl对象中 //
- for (int idx = 0; idx < num; ++idx)
- {
- CurlArray[idx] = NULL;
- CurlArray[idx] = curl_easy_handler(URL, PROXY, RspArray[idx], TIMEOUT);
- if (CurlArray[idx] == NULL)
- {
- return -1;
- }
- curl_multi_add_handle(curl_m, CurlArray[idx]);
- }
- /*
- * 调用curl_multi_perform函数执行curl请求
- * url_multi_perform返回CURLM_CALL_MULTI_PERFORM时,表示需要继续调用该函数直到返回值不是CURLM_CALL_MULTI_PERFORM为止
- * running_handles变量返回正在处理的easy curl数量,running_handles为0表示当前没有正在执行的curl请求
- */
- int running_handles;
- while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl_m, &running_handles))
- {
- cout << running_handles << endl;
- }
- /**
- * 为了避免循环调用curl_multi_perform产生的cpu持续占用的问题,采用select来监听文件描述符
- */
- while (running_handles)
- {
- if (-1 == curl_multi_select(curl_m))
- {
- cerr << "select error" << endl;
- break;
- } else {
- // select监听到事件,调用curl_multi_perform通知curl执行相应的操作 //
- while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl_m, &running_handles))
- {
- cout << "select: " << running_handles << endl;
- }
- }
- cout << "select: " << running_handles << endl;
- }
- // 输出执行结果 //
- int msgs_left;
- CURLMsg * msg;
- while((msg = curl_multi_info_read(curl_m, &msgs_left)))
- {
- if (CURLMSG_DONE == msg->msg)
- {
- int idx;
- for (idx = 0; idx < num; ++idx)
- {
- if (msg->easy_handle == CurlArray[idx]) break;
- }
- if (idx == num)
- {
- cerr << "curl not found" << endl;
- } else
- {
- cout << "curl [" << idx << "] completed with status: "
- << msg->data.result << endl;
- cout << "rsp: " << RspArray[idx] << endl;
- }
- }
- }
- // 这里要注意cleanup的顺序 //
- for (int idx = 0; idx < num; ++idx)
- {
- curl_multi_remove_handle(curl_m, CurlArray[idx]);
- }
- for (int idx = 0; idx < num; ++idx)
- {
- curl_easy_cleanup(CurlArray[idx]);
- }
- curl_multi_cleanup(curl_m);
- return 0;
- }
- /**
- * easy curl使用demo
- */
- int curl_easy_demo(int num)
- {
- std::string RspArray[num];
- for (int idx = 0; idx < num; ++idx)
- {
- CURL * curl = curl_easy_handler(URL, PROXY, RspArray[idx], TIMEOUT);
- CURLcode code = curl_easy_perform(curl);
- cout << "curl [" << idx << "] completed with status: "
- << code << endl;
- cout << "rsp: " << RspArray[idx] << endl;
- // clear handle //
- curl_easy_cleanup(curl);
- }
- return 0;
- }
- #define USE_MULTI_CURL
- struct timeval begin_tv, end_tv;
- int main(int argc, char * argv[])
- {
- if (argc < 2)
- {
- return -1;
- }
- int num = atoi(argv[1]);
- // 获取开始时间 //
- gettimeofday(&begin_tv, NULL);
- #ifdef USE_MULTI_CURL
- // 使用multi接口进行访问 //
- curl_multi_demo(num);
- #else
- // 使用easy接口进行访问 //
- curl_easy_demo(num);
- #endif
- // 获取结束时间 //
- struct timeval end_tv;
- gettimeofday(&end_tv, NULL);
- // 计算执行延时并输出,用于比较 //
- int eclapsed = (end_tv.tv_sec - begin_tv.tv_sec) * 1000 +
- (end_tv.tv_usec - begin_tv.tv_usec) / 1000;
- cout << "eclapsed time:" << eclapsed << "ms" << endl;
- return 0;
- }
使用multi curl进行http并发访问的更多相关文章
- php-cgi和php-fpm,Windows环境下解决Nginx+php并发访问阻塞问题。
php-cgi 是运行php,php-fpm是守护php-cgi进程 nginx配置目录运行php location ~ \.php$ { ...
- Java多线程基础——对象及变量并发访问
在开发多线程程序时,如果每个多线程处理的事情都不一样,每个线程都互不相关,这样开发的过程就非常轻松.但是很多时候,多线程程序是需要同时访问同一个对象,或者变量的.这样,一个对象同时被多个线程访问,会出 ...
- (实例篇)php 使用redis锁限制并发访问类示例
1.并发访问限制问题 对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功. 例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制 ...
- Java多线程编程核心技术---对象及变量的并发访问(一)
synchronized同步方法 "非线程安全"其实会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是"脏读",也就是渠道的数据其实是被更改 ...
- php 使用redis锁限制并发访问类
1.并发访问限制问题 对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功. 例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制 ...
- Spring并发访问的线程安全性问题
Spring并发访问的线程安全性问题 http://windows9834.blog.163.com/blog/static/27345004201391045539953/ 由于Spring MVC ...
- 高并发访问mysql时的问题(一):库存超减
如果在对某行记录的更新时不采取任何防范措施,在多线程访问时,就容易出现库存为负数的错误. 以下用php.mysql,apache ab工具举例说明: mysql表结构 CREATE TABLE `yx ...
- Oracle 数据库中不同事务并发访问的问题
现象 以SQL/Helper为例,打开不同的SQL窗口,对同一个表格进行操作,如下所示. 窗口1:当执行更新任务.紧接着执行查询时获得一组查询结果.结果是对的. 窗口2:而在另外一个SQL查询窗口中执 ...
- [转载]socket下server端支持多客户端并发访问简单实现
/*Author: wainiwann *Source: 博客园 http://www.cnblogs.com/wainiwann *Remarks: 转载请说明出处!!! */ 感觉很不错,可以学 ...
随机推荐
- 判断变量是否为json对象
var m ={a:'A'}; if(typeof m == 'object' && JSON.stringify(m).indexOf('{') == 0){//判断变量m是不是js ...
- win7系统电脑连接小米蓝牙音箱
一.买好蓝牙适配器,插到电脑上. 二.右下角工具栏找到蓝牙图标 三.右键这个图标,选择'显示Bluetooth设备' 四.找到小米蓝牙音箱 'NDZ-030-AA' 五.双击打开它,然后选择'服务'选 ...
- 加密算法使用(四):AES的使用
AES是一种对称加密方式,比DES更为安全,用一个秘钥加密数据之后,可以用同一个秘钥对加密后的数据解密还原,以下是一套以字符串为例子的使用全过程演示, 用到了 commons-codec.jar pa ...
- php基础02:变量
1.创建变量 <?php $num1 = 15; $num2 = 15.5; echo $num1+$num2; echo "<br>"; ?> 2.Loc ...
- [CareerCup] 5.4 Explain Expression ((n & (n-1)) == 0) 解释表达式
5.4 Explain what the following code does: ((n & (n-1)) == 0). 这道题让我们解释一个表达式((n & (n-1)) == 0 ...
- LeetCode 笔记23 Best Time to Buy and Sell Stock III
Best Time to Buy and Sell Stock III Say you have an array for which the ith element is the price of ...
- jenkins publish over ssh使用
1.在需要远程的ubuntu服务器上生成密钥,指令:ssh-keygen 一路默认下去,会在~/.ssh目录下生成 id_rsa(私钥).id_rsa.pub(公钥) 2.复制公钥文件id_rsa ...
- 通通制作Html5小游戏——第二弹(仿flappy bird像素鸟)
亲爱的博友们,我又回来啦~因为我们技术宅的思想只有技术宅懂得,好不容易写了点好玩的东西发QQ空间,结果只有11的UV,0回复....10分钟ps一个女神的素描效果发QQ空间朋友圈,一大堆回复加赞,作为 ...
- Aspose.Cells 读取受保护的Excel
最近遇到一个需求,要能够读取受密码保护的Excel内容,之前都是直接读取Excel中的数据,不需要做任何其他的处理. 当Excel双击的时候,需要输入密码,在使用Aspose.Cells 组件读取 ...
- IOS动态判断UITextField是否输入为手机号
现在使用的app大部分都用到手机号注册,很多app注册的时候会判断手机号,可以根据当前输入文本来判断“获取验证码”的按钮是否可用 判断输入文本是通过UITextField的代理的 -(BOOL)tex ...