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

    [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. }  
  • 相关阅读:
    Luogu 2254 [NOI2005]瑰丽华尔兹
    Luogu 3168 [CQOI2015]任务查询系统
    Luogu U15118 萨塔尼亚的期末考试(fail)
    POJ 2778 DNA Sequence AC自动机 + 矩阵快速幂
    HDU 2457 DNA repair AC自动机 + dp
    POJ 3522 Slim Span 暴力枚举 + 并查集
    POJ 2955 Brackets 区间dp
    hdu 2604 Queuing dp找规律 然后矩阵快速幂。坑!!
    UESTC
    java & SQL
  • 原文地址:https://www.cnblogs.com/bigben0123/p/3154406.html
Copyright © 2020-2023  润新知