使用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: 转载请说明出处!!! */ 感觉很不错,可以学 ...
随机推荐
- Block 的基本用法
iOS中Block的基础用法 转载自简书 本文简介 本章不会对Block做过多的实现研究.只是讲解基本的用法.纯粹基础知识.结合实际项目怎么去做举例.Block使用场景,可以在两个界面的传值,也可以对 ...
- 使用PDO进行sql的预处理和操作结果集
- Android 界面排版的5种方式
Android布局是应用界面开发的重要一环,在Android中,共有五种布局方式,分别是:FrameLayout(框架布局),LinearLayout (线性布局),AbsoluteLayout(绝对 ...
- 关于第一个Java应用
一.创建Java源文件 Java应用由一个或多个扩展名为".java"的文件构成,这些文件被称为Java源文件,从编译的角度,则被称为编译单元(Compilation Unit). ...
- Android 画布绘图
我们已经介绍了Canvas,在那里,已经学习了如何创建自己的View.在第7章中也使用了Canvas来为MapView标注覆盖. 画布(Canvas)是图形编程中一个很普通的概念,通常由三个基本的绘图 ...
- linux vmstat 系统结果说明
可以用vmstat 显示系统负载等信息. 例如 vmstat 5 5,表示在T(5)秒时间内进行N(5)次采样. procs:r-->;在运行队列中等待的进程数b-->;在等待io的进程数 ...
- C# 利用QRCode生成二维码图片
网上生成二维码的组件是真多,可是真正好用的,并且生成速度很快的没几个,QRCode就是我在众多中找到的,它的生成速度快.但是网上关于它的使用说明,真的太少了,大都是千篇一律的复制粘贴.这是本要用它做了 ...
- Matlab中的数据类型
Matlab中有15种基本数据类型,主要是整型.浮点.逻辑.字符.日期和时间.结构数组.单元格数组以及函数句柄等. 1.整型:(int8:uint8:int16:uint16:int3 ...
- struct2cell
函数功能:把结构体转换为元胞数组. 语法格式: c = struct2cell(s) 如果s是m*n(m行n列)的二维的结构体数组,每个结构体含有p个域,则转换得到一个p*m*n的元胞数组c. 如果s ...
- 20145234黄斐《信息安全系统设计基础》GDB调试汇编堆栈过程分析
堆栈跟踪 首先编辑一个程序 用gcc编译,再使用gdb调试,发现gdb尚未下载 下载后重新运行gdb 设置断点:b+行号或者"main" 运行:r frame:打印出的信息:栈的层 ...