i007.cc

i007.cc

优先队列-降维打击

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接口就派上用场了.

相比而言,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. }

curl官网上提供的文件上传例子:

  1. /* This is an example application source code using the multi interface
  2.  * to do a multipart formpost without “blocking”. */
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <sys/time.h>
  6. #include <curl/curl.h>
  7. int main(void)
  8. {
  9.   CURL *curl;
  10.   CURLM *multi_handle;
  11.   int still_running;
  12.   struct curl_httppost *formpost=NULL;
  13.   struct curl_httppost *lastptr=NULL;
  14.   struct curl_slist *headerlist=NULL;
  15.   static const char buf[] = “Expect:”;
  16.   /* Fill in the file upload field. This makes libcurl load data from
  17.      the given file name when curl_easy_perform() is called. */
  18.   curl_formadd(&formpost,
  19.                &lastptr,
  20.                CURLFORM_COPYNAME, “sendfile”,
  21.                CURLFORM_FILE, “postit2.c”,
  22.                CURLFORM_END);
  23.   /* Fill in the filename field */
  24.   curl_formadd(&formpost,
  25.                &lastptr,
  26.                CURLFORM_COPYNAME, “filename”,
  27.                CURLFORM_COPYCONTENTS, “postit2.c”,
  28.                CURLFORM_END);
  29.   /* Fill in the submit field too, even if this is rarely needed */
  30.   curl_formadd(&formpost,
  31.                &lastptr,
  32.                CURLFORM_COPYNAME, “submit”,
  33.                CURLFORM_COPYCONTENTS, “send”,
  34.                CURLFORM_END);
  35.   curl = curl_easy_init();
  36.   multi_handle = curl_multi_init();
  37.   /* initalize custom header list (stating that Expect: 100-continue is not
  38.      wanted */
  39.   headerlist = curl_slist_append(headerlist, buf);
  40.   if(curl && multi_handle) {
  41.     /* what URL that receives this POST */
  42.     curl_easy_setopt(curl, CURLOPT_URL, “http://www.example.com/upload.cgi”);
  43.     curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
  44.     curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
  45.     curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
  46.     curl_multi_add_handle(multi_handle, curl);
  47.     curl_multi_perform(multi_handle, &still_running);
  48.     do {
  49.       struct timeval timeout;
  50.       int rc; /* select() return code */
  51.       fd_set fdread;
  52.       fd_set fdwrite;
  53.       fd_set fdexcep;
  54.       int maxfd = -1;
  55.       long curl_timeo = -1;
  56.       FD_ZERO(&fdread);
  57.       FD_ZERO(&fdwrite);
  58.       FD_ZERO(&fdexcep);
  59.       /* set a suitable timeout to play around with */
  60.       timeout.tv_sec = 1;
  61.       timeout.tv_usec = 0;
  62.       curl_multi_timeout(multi_handle, &curl_timeo);
  63.       if(curl_timeo >= 0) {
  64.         timeout.tv_sec = curl_timeo / 1000;
  65.         if(timeout.tv_sec > 1)
  66.           timeout.tv_sec = 1;
  67.         else
  68.           timeout.tv_usec = (curl_timeo % 1000) * 1000;
  69.       }
  70.       /* get file descriptors from the transfers */
  71.       curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
  72.       /* In a real-world program you OF COURSE check the return code of the
  73.          function calls.  On success, the value of maxfd is guaranteed to be
  74.          greater or equal than -1.  We call select(maxfd + 1, …), specially in
  75.          case of (maxfd == -1), we call select(0, …), which is basically equal
  76.          to sleep. */
  77.       rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
  78.       switch(rc) {
  79.       case -1:
  80.         /* select error */
  81.         break;
  82.       case 0:
  83.       default:
  84.         /* timeout or readable/writable sockets */
  85.         printf(“perform!\n”);
  86.         curl_multi_perform(multi_handle, &still_running);
  87.         printf(“running: %d!\n”, still_running);
  88.         break;
  89.       }
  90.     } while(still_running);
  91.     curl_multi_cleanup(multi_handle);
  92.     /* always cleanup */
  93.     curl_easy_cleanup(curl);
  94.     /* then cleanup the formpost chain */
  95.     curl_formfree(formpost);
  96.     /* free slist */
  97.     curl_slist_free_all (headerlist);
  98.   }
  99.   return 0;
  100. }

发表回复