使用multi curl进行http并发访问

来源:互联网 发布:网络销售产品 编辑:程序博客网 时间:2024/06/06 00:21

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并发访问,并输出访问结果。

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

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 不小心打错电话怎么办 相亲发信息不回怎么办 如果一个人微信不回电话不接怎么办 跟老公吵架打电话不接怎么办 起诉离婚对方不接电话怎么办 苹果手机接电话声音小怎么办 老公不回你微信怎么办 工地欠货款不给怎么办 朋友微信借钱不还怎么办 微信上借钱不还怎么办 微信借钱不还怎么办 微信转账借钱不还怎么办 没欠条人家欠钱不给怎么办 欠货款被告没去怎么办 别人欠我钱不接电话怎么办 借给别人钱不接电话怎么办 欠钱的人玩消失怎么办 被婚介所骗了5万怎么办 贷款紧急联系人被骚扰怎么办 微信限制加好友怎么办 珍爱网的客服老打电话怎么办 百合网的人气是怎么办 苹果x来电不显示怎么办 苹果8黑屏没反应怎么办 苹果六s开不了机怎么办 苹果6s突然黑屏怎么办 苹果6s黑屏了怎么办 苹果6s手机黑屏打不开怎么办 苹果6sp手机死机怎么办 苹果6s打不开机怎么办 6s开不了机怎么办 lg背光灯坏了怎么办 苹果6s屏幕变黄怎么办 苹果8背光坏了怎么办 苹果六背光坏了怎么办 苹果5s蓝屏死机怎么办 苹果6s突然黑屏怎么办? 苹果6s经常死机怎么办 苹果6s没有背光怎么办 苹果7白屏死机怎么办 苹果6p屏膜失灵怎么办