转载自: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;
- };
- /**
- *生成一个easycurl对象,进行一些简单的设置操作
- */
- 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;/*ms*/
- *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表单示例: