转载自: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并发访问,并输出访问结果。
- #include<string>
- #include<iostream>
-
- #include<curl/curl.h>
- #include<sys/time.h>
- #include<unistd.h>
- usingnamespacestd;
- size_tcurl_writer(void*buffer,size_tsize,87); background-color:inherit; font-weight:bold">size_tcount,void*stream)
- {
- std::string*pStream=static_cast<std::string*>(stream);
- (*pStream).append((char*)buffer,size*count);
-
- returnsize*count;
- };
-
-
- */
- CURL*curl_easy_handler(conststd::string&sUrl,
- conststd::string&sProxy,
- std::string&sRsp,
- unsignedintuiTimeout)
- CURL*curl=curl_easy_init();
- curl_easy_setopt(curl,CURLOPT_URL,sUrl.c_str());
- curl_easy_setopt(curl,CURLOPT_NOSIGNAL,1);
- if(uiTimeout>0)
- {
- }
- if(!sProxy.empty())
- //writefunction//
- returncurl;
- }
- /**
- *使用select函数监听multicurl文件描述符的状态
- *监听成功返回0,监听失败返回-1
- intcurl_multi_select(CURLM*curl_m)
- intret=0;
- structtimevaltimeout_tv;
- fd_setfd_read;
- fd_setfd_write;
- fd_setfd_except;
- intmax_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;
- //获取multicurl需要监听的文件描述符集合fd_set//
- curl_multi_fdset(curl_m,&fd_read,&fd_write,&fd_except,&max_fd);
- *Whenmax_fdreturnswith-1,0); background-color:inherit">*youneedtowaitawhileandthenproceedandcallcurl_multi_performanyway.
- *Howlongtowait?Iwouldsuggest100millisecondsatleast,0); background-color:inherit">*butyoumaywanttotestitoutinyourownparticularconditionstofindasuitablevalue.
- if(-1==max_fd)
- return-1;
- *执行监听,当文件描述符状态发生改变的时候返回
- *返回0,程序调用curl_multi_perform通知curl执行相应操作
- *返回-1,表示select错误
- *注意:即使select超时也需要返回0,具体可以去官网看文档说明
- */
- intret_code=::select(max_fd+1,&timeout_tv);
- switch(ret_code)
- case-1:
- /*selecterror*/
- ret=-1;
- break;
- case0:
- /*selecttimeout*/
- default:
- /*oneormoreofcurl'sfiledescriptorssaythere'sdatatoreadorwrite*/
- ret=0;
- returnret;
- #defineMULTI_CURL_NUM3
- //这里设置你需要访问的url//
- std::stringURL="http://website.com";
- //这里设置代理ip和端口//
- std::stringPROXY="ip:port";
- //这里设置超时时间//
- unsignedintTIMEOUT=2000;
- *multicurl使用demo
- intcurl_multi_demo(intnum)
- //初始化一个multicurl对象//
- CURLM*curl_m=curl_multi_init();
- std::stringRspArray[num];
- CURL*CurlArray[num];
- //设置easycurl对象并添加到multicurl对象中//
- for(intidx=0;idx<num;++idx)
- CurlArray[idx]=NULL;
- CurlArray[idx]=curl_easy_handler(URL,PROXY,RspArray[idx],TIMEOUT);
- if(CurlArray[idx]==NULL)
- 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变量返回正在处理的easycurl数量,running_handles为0表示当前没有正在执行的curl请求
- intrunning_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<<"selecterror"<<endl;
- break;
- }else{
- //select监听到事件,调用curl_multi_perform通知curl执行相应的操作//
- cout<<"select:"<<running_handles<<endl;
- cout<<"select:"<<running_handles<<endl;
- //输出执行结果//
- intmsgs_left;
- CURLMsg*msg;
- while((msg=curl_multi_info_read(curl_m,&msgs_left)))
- if(CURLMSG_DONE==msg->msg)
- intidx;
- for(idx=0;idx<num;++idx)
- if(msg->easy_handle==CurlArray[idx])if(idx==num)
- cerr<<"curlnotfound"<<endl;
- else
- cout<<"curl["<<idx<<"]completedwithstatus:"
- <<msg->data.result<<endl;
- cout<<"rsp:"<<RspArray[idx]<<endl;
- //这里要注意cleanup的顺序//
- intidx=0;idx<num;++idx)
- curl_multi_remove_handle(curl_m,CurlArray[idx]);
- curl_easy_cleanup(CurlArray[idx]);
- curl_multi_cleanup(curl_m);
- return0;
- *easycurl使用demo
- intcurl_easy_demo( std::stringRspArray[num];
- 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);
- <<code<<endl;
- //clearhandle//
- curl_easy_cleanup(curl);
- #defineUSE_MULTI_CURL
- structtimevalbegin_tv,end_tv;
- intmain(intargc,87); background-color:inherit; font-weight:bold">char*argv[])
- if(argc<2)
- intnum=atoi(argv[1]);
- //获取开始时间//
- gettimeofday(&begin_tv,NULL);
- #ifdefUSE_MULTI_CURL
- //使用multi接口进行访问//
- curl_multi_demo(num);
- #else
- //使用easy接口进行访问//
- curl_easy_demo(num);
- #endif
- //获取结束时间//
- structtimevalend_tv;
- gettimeofday(&end_tv,0); background-color:inherit">//计算执行延时并输出,用于比较//
- inteclapsed=(end_tv.tv_sec-begin_tv.tv_sec)*1000+
- (end_tv.tv_usec-begin_tv.tv_usec)/1000;
- cout<<"eclapsedtime:"<<eclapsed<<"ms"<<endl;
- }
上面的代码在关键位置都做了详细的注释,相信应该不难看懂。
上篇博文讲到了如何使用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表单示例:
<formenctype="multipart/form-data"action="http://host:port/UploadFile"method=post>
- Upload:br>
- 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
curl_formadd(&post,&last,CURLFORM_COPYNAME,"name",CURLFORM_COPYCONTENTS,"content",CURLFORM_END);
2、添加name/content/contenttype section
"type",51); font-family:Arial; font-size:14px; line-height:26px"> 3、添加 file/filename section
"pic" ,CURLFORM_FILE,"demo.jpg",CURLFORM_FILENAME,"upload.pic",51); font-family:Arial; font-size:14px; line-height:26px"> 4、添加file/contenttype section
"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官网上提供的文件上传例子:
/*Thisisanexampleapplicationsourcecodeusingthemultiinterface
- *todoamultipartformpostwithout"blocking".*/
- #include<stdio.h>
- #include<string.h>
- #include<curl/curl.h>
- intmain(void)
- CURL*curl;
- CURLM*multi_handle;
- intstill_running;
- structcurl_httppost*formpost=NULL;
- structcurl_httppost*lastptr=NULL;
- structcurl_slist*headerlist=NULL;
- staticconstcharbuf[]="Expect:";
- /*Fillinthefileuploadfield.Thismakeslibcurlloaddatafrom
- thegivenfilenamewhencurl_easy_perform()iscalled.*/
- curl_formadd(&formpost,248); line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> &lastptr,
- 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);
- /*Fillinthefilenamefield*/
- 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);
- /*Fillinthesubmitfieldtoo,evenifthisisrarelyneeded*/
- "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();
- multi_handle=curl_multi_init();
- /*initalizecustomheaderlist(statingthatExpect:100-continueisnot
- wanted*/
- headerlist=curl_slist_append(headerlist,buf);
- if(curl&&multi_handle){
- /*whatURLthatreceivesthisPOST*/
- "http://www.example.com/upload.cgi");
- curl_multi_add_handle(multi_handle,curl);
- curl_multi_perform(multi_handle,&still_running);
- do{
- structtimevaltimeout;
- intrc;
- fd_setfdread;
- fd_setfdwrite;
- fd_setfdexcep;
- intmaxfd=-1;
- longcurl_timeo=-1;
- FD_ZERO(&fdread);
- FD_ZERO(&fdwrite);
- FD_ZERO(&fdexcep);
- /*setasuitabletimeouttoplayaroundwith*/
- timeout.tv_sec=1;
- timeout.tv_usec=0;
- curl_multi_timeout(multi_handle,&curl_timeo);
- if(curl_timeo>=0){
- timeout.tv_sec=curl_timeo/1000;
- if(timeout.tv_sec>1)
- timeout.tv_sec=1;
- timeout.tv_usec=(curl_timeo%1000)*1000;
- /*getfiledescriptorsfromthetransfers*/
- curl_multi_fdset(multi_handle,&fdread,&fdwrite,&fdexcep,&maxfd);
- /*Inareal-worldprogramyouOFCOURSEcheckthereturncodeofthe
- functioncalls.Onsuccess,thevalueofmaxfdisguaranteedtobe
- greaterorequalthan-1.Wecallselect(maxfd+1,...),speciallyin
- caSEOf(maxfd==-1),wecallselect(0,whichisbasicallyequal
- tosleep.*/
- rc=select(maxfd+1,&timeout);
- switch(rc){
- case-1:
- /*selecterror*/
- default:
- /*timeoutorreadable/writablesockets*/
- printf("perform!n");
- curl_multi_perform(multi_handle,&still_running);
- printf("running:%d!n",still_running);
- }while(still_running);
- curl_multi_cleanup(multi_handle);
- /*alwayscleanup*/
- curl_easy_cleanup(curl);
- /*thencleanuptheformpostchain*/
- curl_formfree(formpost);
- /*freeslist*/
- curl_slist_free_all(headerlist);
- }
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|