加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

使用libcurl进行异步并发访问与文件上传

发布时间:2020-12-14 19:47:32 所属栏目:百科 来源:网络整理
导读:转载自:http://blog.csdn.net/zxgfa/article/details/8302059 curl是一款利用URL语法进行文件传输的工具,它支持多种协议,包括FTP,FTPS,HTTP,HTTPS,GOPHER,TELNET等,我们既可以在命令行上使用它,也可以利用 libcurl进行相关编程。相信大部分同学都应该使

转载自:http://blog.csdn.net/zxgfa/article/details/8302059

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 plain copy
  1. #include<string>
  2. #include<iostream>
  3. #include<curl/curl.h>
  4. #include<sys/time.h>
  5. #include<unistd.h>
  6. usingnamespacestd;
  7. size_tcurl_writer(void*buffer,size_tsize,87); background-color:inherit; font-weight:bold">size_tcount,void*stream)
  8. {
  9. std::string*pStream=static_cast<std::string*>(stream);
  10. (*pStream).append((char*)buffer,size*count);
  11. returnsize*count;
  12. };
  13. /**
  14. *生成一个easycurl对象,进行一些简单的设置操作
  15. */
  16. CURL*curl_easy_handler(conststd::string&sUrl,
  17. conststd::string&sProxy,
  18. std::string&sRsp,
  19. unsignedintuiTimeout)
  20. CURL*curl=curl_easy_init();
  21. curl_easy_setopt(curl,CURLOPT_URL,sUrl.c_str());
  22. curl_easy_setopt(curl,CURLOPT_NOSIGNAL,1);
  23. if(uiTimeout>0)
  24. {
  25. }
  26. if(!sProxy.empty())
  27. //writefunction//
  28. returncurl;
  29. }
  30. /**
  31. *使用select函数监听multicurl文件描述符的状态
  32. *监听成功返回0,监听失败返回-1
  33. intcurl_multi_select(CURLM*curl_m)
  34. intret=0;
  35. structtimevaltimeout_tv;
  36. fd_setfd_read;
  37. fd_setfd_write;
  38. fd_setfd_except;
  39. intmax_fd=-1;
  40. //注意这里一定要清空fdset,curl_multi_fdset不会执行fdset的清空操作//
  41. FD_ZERO(&fd_read);
  42. FD_ZERO(&fd_write);
  43. FD_ZERO(&fd_except);
  44. //设置select超时时间//
  45. timeout_tv.tv_sec=1;
  46. timeout_tv.tv_usec=0;
  47. //获取multicurl需要监听的文件描述符集合fd_set//
  48. curl_multi_fdset(curl_m,&fd_read,&fd_write,&fd_except,&max_fd);
  49. *Whenmax_fdreturnswith-1,0); background-color:inherit">*youneedtowaitawhileandthenproceedandcallcurl_multi_performanyway.
  50. *Howlongtowait?Iwouldsuggest100millisecondsatleast,0); background-color:inherit">*butyoumaywanttotestitoutinyourownparticularconditionstofindasuitablevalue.
  51. if(-1==max_fd)
  52. return-1;
  53. *执行监听,当文件描述符状态发生改变的时候返回
  54. *返回0,程序调用curl_multi_perform通知curl执行相应操作
  55. *返回-1,表示select错误
  56. *注意:即使select超时也需要返回0,具体可以去官网看文档说明
  57. */
  58. intret_code=::select(max_fd+1,&timeout_tv);
  59. switch(ret_code)
  60. case-1:
  61. /*selecterror*/
  62. ret=-1;
  63. break;
  64. case0:
  65. /*selecttimeout*/
  66. default:
  67. /*oneormoreofcurl'sfiledescriptorssaythere'sdatatoreadorwrite*/
  68. ret=0;
  69. returnret;
  70. #defineMULTI_CURL_NUM3
  71. //这里设置你需要访问的url//
  72. std::stringURL="http://website.com";
  73. //这里设置代理ip和端口//
  74. std::stringPROXY="ip:port";
  75. //这里设置超时时间//
  76. unsignedintTIMEOUT=2000;/*ms*/
  77. *multicurl使用demo
  78. intcurl_multi_demo(intnum)
  79. //初始化一个multicurl对象//
  80. CURLM*curl_m=curl_multi_init();
  81. std::stringRspArray[num];
  82. CURL*CurlArray[num];
  83. //设置easycurl对象并添加到multicurl对象中//
  84. for(intidx=0;idx<num;++idx)
  85. CurlArray[idx]=NULL;
  86. CurlArray[idx]=curl_easy_handler(URL,PROXY,RspArray[idx],TIMEOUT);
  87. if(CurlArray[idx]==NULL)
  88. curl_multi_add_handle(curl_m,CurlArray[idx]);
  89. /*
  90. *调用curl_multi_perform函数执行curl请求
  91. *url_multi_perform返回CURLM_CALL_MULTI_PERFORM时,表示需要继续调用该函数直到返回值不是CURLM_CALL_MULTI_PERFORM为止
  92. *running_handles变量返回正在处理的easycurl数量,running_handles为0表示当前没有正在执行的curl请求
  93. intrunning_handles;
  94. while(CURLM_CALL_MULTI_PERFORM==curl_multi_perform(curl_m,&running_handles))
  95. cout<<running_handles<<endl;
  96. *为了避免循环调用curl_multi_perform产生的cpu持续占用的问题,采用select来监听文件描述符
  97. while(running_handles)
  98. if(-1==curl_multi_select(curl_m))
  99. cerr<<"selecterror"<<endl;
  100. break;
  101. }else{
  102. //select监听到事件,调用curl_multi_perform通知curl执行相应的操作//
  103. cout<<"select:"<<running_handles<<endl;
  104. cout<<"select:"<<running_handles<<endl;
  105. //输出执行结果//
  106. intmsgs_left;
  107. CURLMsg*msg;
  108. while((msg=curl_multi_info_read(curl_m,&msgs_left)))
  109. if(CURLMSG_DONE==msg->msg)
  110. intidx;
  111. for(idx=0;idx<num;++idx)
  112. if(msg->easy_handle==CurlArray[idx])if(idx==num)
  113. cerr<<"curlnotfound"<<endl;
  114. else
  115. cout<<"curl["<<idx<<"]completedwithstatus:"
  116. <<msg->data.result<<endl;
  117. cout<<"rsp:"<<RspArray[idx]<<endl;
  118. //这里要注意cleanup的顺序//
  119. intidx=0;idx<num;++idx)
  120. curl_multi_remove_handle(curl_m,CurlArray[idx]);
  121. curl_easy_cleanup(CurlArray[idx]);
  122. curl_multi_cleanup(curl_m);
  123. return0;
  124. *easycurl使用demo
  125. intcurl_easy_demo( std::stringRspArray[num];
  126. CURL*curl=curl_easy_handler(URL,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> CURLcodecode=curl_easy_perform(curl);
  127. <<code<<endl;
  128. //clearhandle//
  129. curl_easy_cleanup(curl);
  130. #defineUSE_MULTI_CURL
  131. structtimevalbegin_tv,end_tv;
  132. intmain(intargc,87); background-color:inherit; font-weight:bold">char*argv[])
  133. if(argc<2)
  134. intnum=atoi(argv[1]);
  135. //获取开始时间//
  136. gettimeofday(&begin_tv,NULL);
  137. #ifdefUSE_MULTI_CURL
  138. //使用multi接口进行访问//
  139. curl_multi_demo(num);
  140. #else
  141. //使用easy接口进行访问//
  142. curl_easy_demo(num);
  143. #endif
  144. //获取结束时间//
  145. structtimevalend_tv;
  146. gettimeofday(&end_tv,0); background-color:inherit">//计算执行延时并输出,用于比较//
  147. inteclapsed=(end_tv.tv_sec-begin_tv.tv_sec)*1000+
  148. (end_tv.tv_usec-begin_tv.tv_usec)/1000;
  149. cout<<"eclapsedtime:"<<eclapsed<<"ms"<<endl;
  150. }


上面的代码在关键位置都做了详细的注释,相信应该不难看懂。


上篇博文讲到了如何使用multicurl来进行http并发访问,今天继续有关curl的主题,来八一八如何使用curl来上传文件,在介绍具体方法之前了解下目前http文件上传的基本实现。

rfc1867描述了如何使用http协议来上传客户端文件,目前基本上所有的浏览器和web服务器都支持http文件上传,它的使用也十分的简单,具体的来说就是在页面上创建一个form表单,表单的enctype属性为multipart/form-data,action为接收上传文件的cgi url,请求方式为post,在表单中添加type属性为file的input,file input里面选择需要上传的文件,选择好后点击submit,服务器端收到multipart post请求后,会根据相关协议解析请求,然后保存上传的文件内容,Multipart表单示例:

[html] view plain copy
    <formenctype="multipart/form-data"action="http://host:port/UploadFile"method=post>
  1. Upload:br>
  2. inputname="userfile"type="file"> textfield:inputtype="text"name="text"value="text"inputtype="submit"value="提交"inputtype=reset </form>


好了,现在来讲一讲curl的文件上传,对于curl来讲,其实它要完成的任务就是构建一个multipart/formdata HTTP POST请求。类似于往multipart form表单中添加type为file或者text的input item一样,curl也需要我们构造表单中的input item,curl_formadd函数可以帮助我们完成这个任务,它即可以添加普通的name-value section,也可以添加file upload section,下面举几个具体例子:

1、添加name/content section

[cpp] copy
    curl_formadd(&post,&last,CURLFORM_COPYNAME,"name",CURLFORM_COPYCONTENTS,"content",CURLFORM_END);

2、添加name/content/contenttype section

copy
    "type",51); font-family:Arial; font-size:14px; line-height:26px"> 3、添加 file/filename section

    copy
      "pic",CURLFORM_FILE,"demo.jpg",CURLFORM_FILENAME,"upload.pic",51); font-family:Arial; font-size:14px; line-height:26px"> 4、添加file/contenttype section

      copy
        "image/jpeg",51); font-family:Arial; font-size:14px; line-height:26px">
        上面的post 和 last都是指向curl_httppost对象的指针, post指向的就是一个由所有section组成的链表的开端,last是该链表的尾指针。当我们添加完所有的form section之后,使用curl_easy_setopt(curl,CURLOPT_HTTPPOST,post)函数设置curl的http post,最后就是调用curl_easy_perform执行请求。需要注意的是,当使用libcurl的POST方式时,如果POST数据的大小大于1024个字节,libcurl不会直接发送POST请求,而是会分为两步执行请求:
        1、发送一个请求,该请求头部包含一个Expect: 100-continue的字段,用来询问server是否愿意接受数据
        2、当接收到从server返回的100-continue的应答后,它才会真正的发起POST请求,将数据发送给server。
        对于文件上传来说,文件大小往往会超过1024个字节,所以如果你确认你的服务器不会拒绝你的文件上传请求的话,可以禁止curl的Expect请求头,具体方法可以去看看我的另外一篇文章《libcurl的使用问题“Expect100-continue” 》。

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

        copy
          /*Thisisanexampleapplicationsourcecodeusingthemultiinterface
        1. *todoamultipartformpostwithout"blocking".*/
        2. #include<stdio.h>
        3. #include<string.h>
        4. #include<curl/curl.h>
        5. intmain(void)
        6. CURL*curl;
        7. CURLM*multi_handle;
        8. intstill_running;
        9. structcurl_httppost*formpost=NULL;
        10. structcurl_httppost*lastptr=NULL;
        11. structcurl_slist*headerlist=NULL;
        12. staticconstcharbuf[]="Expect:";
        13. /*Fillinthefileuploadfield.Thismakeslibcurlloaddatafrom
        14. thegivenfilenamewhencurl_easy_perform()iscalled.*/
        15. curl_formadd(&formpost,248); line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> &lastptr,
        16. CURLFORM_COPYNAME,"sendfile",248); line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> CURLFORM_FILE,"postit2.c",108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> CURLFORM_END);
        17. /*Fillinthefilenamefield*/
        18. curl_formadd(&formpost,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> &lastptr,248); line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> CURLFORM_COPYNAME,"filename",108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> CURLFORM_COPYCONTENTS,248); line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> CURLFORM_END);
        19. /*Fillinthesubmitfieldtoo,evenifthisisrarelyneeded*/
        20. "submit",248); line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> CURLFORM_COPYCONTENTS,"send",108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> curl=curl_easy_init();
        21. multi_handle=curl_multi_init();
        22. /*initalizecustomheaderlist(statingthatExpect:100-continueisnot
        23. wanted*/
        24. headerlist=curl_slist_append(headerlist,buf);
        25. if(curl&&multi_handle){
        26. /*whatURLthatreceivesthisPOST*/
        27. "http://www.example.com/upload.cgi");
        28. curl_multi_add_handle(multi_handle,curl);
        29. curl_multi_perform(multi_handle,&still_running);
        30. do{
        31. structtimevaltimeout;
        32. intrc;/*select()returncode*/
        33. fd_setfdread;
        34. fd_setfdwrite;
        35. fd_setfdexcep;
        36. intmaxfd=-1;
        37. longcurl_timeo=-1;
        38. FD_ZERO(&fdread);
        39. FD_ZERO(&fdwrite);
        40. FD_ZERO(&fdexcep);
        41. /*setasuitabletimeouttoplayaroundwith*/
        42. timeout.tv_sec=1;
        43. timeout.tv_usec=0;
        44. curl_multi_timeout(multi_handle,&curl_timeo);
        45. if(curl_timeo>=0){
        46. timeout.tv_sec=curl_timeo/1000;
        47. if(timeout.tv_sec>1)
        48. timeout.tv_sec=1;
        49. timeout.tv_usec=(curl_timeo%1000)*1000;
        50. /*getfiledescriptorsfromthetransfers*/
        51. curl_multi_fdset(multi_handle,&fdread,&fdwrite,&fdexcep,&maxfd);
        52. /*Inareal-worldprogramyouOFCOURSEcheckthereturncodeofthe
        53. functioncalls.Onsuccess,thevalueofmaxfdisguaranteedtobe
        54. greaterorequalthan-1.Wecallselect(maxfd+1,...),speciallyin
        55. caSEOf(maxfd==-1),wecallselect(0,whichisbasicallyequal
        56. tosleep.*/
        57. rc=select(maxfd+1,&timeout);
        58. switch(rc){
        59. case-1:
        60. /*selecterror*/
        61. default:
        62. /*timeoutorreadable/writablesockets*/
        63. printf("perform!n");
        64. curl_multi_perform(multi_handle,&still_running);
        65. printf("running:%d!n",still_running);
        66. }while(still_running);
        67. curl_multi_cleanup(multi_handle);
        68. /*alwayscleanup*/
        69. curl_easy_cleanup(curl);
        70. /*thencleanuptheformpostchain*/
        71. curl_formfree(formpost);
        72. /*freeslist*/
        73. curl_slist_free_all(headerlist);
        74. }

        (编辑:李大同)

        【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读