CURL库在程序中的运用浅析-nk_ysg-ChinaUnix博客 http://blog.chinaunix.net/uid-22476414-id-3286638.html

这个目录的文章转载freeeyes大牛的作品

前一段时间自己写了一个抓取网页代码的类,来满目一些项目需求,结果发现并不稳定,在海量网页抓取的时候,存在一些异常导致抓取失败。虽然能满足大概的要求,但是功能上还是不能让我100%的满意,于是在站长的建议下,下载了一个CUrl通用库。
第一次写这样的文章,有失偏颇处请谅解,呵呵。
最近把CURL运用在自己的工程里,发现效果非常理想,尤其在海量数据抓取下载的时候,失败率还是非常低的,综合自己的运用,在这里抛砖引玉。在PHP上,CUrl使用的较多,但是在C++上,使用的例子较为简单,而且参考资料较少,在这里我主要想总结一下CUrl在C++下的一些运用。(百度谷歌的资料有的不是很全,在这里补完一下吧。)
Curl是一个跨平台的库,下载地址 http://curl.haxx.se/
安装的时候,如果只需要命令行工具,请编译CUrl下的src,如果需要库引用直接编译主目录下的工程也可以,工程会生成一个src\DLL-Debug的目录,拷贝出libcurl.lib和libcurl.dll。到一个空的文件夹,然后在将include\curl文件夹下的所有.h头文件拿出来放在一个文件夹中。
行了,材料齐备了,拿着这两个文件夹,按照你自己的习惯引入到你的工程项目中,就可以了。
在linux下,你可以选择创建一个build目录.
然后 $ ./configure --prefix=你创建的bulid目录,然后,make,最后在make install一下,就可以了,所有的东西都在build目录里面给你放好了。
下面说一下它的用法,其实很简单,几个关键的API,常用的不超过4个。很方便,倒是一些配置参数相对复杂,这里强烈推荐 http://curl.haxx.se/ 下的帮助页面,里面对所有参数的运用和设置说的很清楚。
恩,呵呵,先说最简单的下载网页吧。
#include "./Include/curl.h"
#include "./Include/types.h"
#include "./Include/easy.h"

这三个头文件是必须引用的。
CURL*         m_pCurl;    
声明一个CURL对象。这里有一个小建议,就是推荐如果你下载的是一个来源的网站地址,最好就是用一个m_pCurl,这样做的好处是,当它和网站建立链接后,会保持这个链接,如果你下载的页面都是源于此网站,它会最大程度节省你的系统资源。如果每次下载一个网页都new一个m_pCurl对象,你会在netstat -an里面看到无数Time_Wiat的链接对象,消耗资源不说其实也是没有必要的。
m_pCurl    = curl_easy_init();
初始化一个Curl对象,它会生成一个CUrl的指针返回。如果返回是NULL,就是建立链接失败。其实这里失败的可能性很小,因为它只做一个初始化的动作。初始化一个soket以及一些缓冲内存Buff,一般这里如果返回为NULL,那就看看你的网卡是否有问题吧。
此后就是最关键的获取网页信息部分,先说GET,再说POST。(一些基于Https的加密传输在这里先不做讨论)
如果是一个普通的Get方法:
bool CDownIcon:ownLoadIcon(const char* pSoftid, const char* pURL)
{
        CURLcode CUrlRes;
        CHtmlDataBuff m_HtmlBuff;
        struct curl_slist *chunk = NULL;

if(m_pCurl != NULL)
        {
                chunk = curl_slist_append(chunk, "Accept-Encoding: gzip, deflate");  
                chunk = curl_slist_append(chunk, "User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; CIBA)");
                chunk = curl_slist_append(chunk, "Connection: Keep-Alive");

//下载文件
                curl_easy_setopt(m_pCurl, CURLOPT_HTTPHEADER, chunk);     
                curl_easy_setopt(m_pCurl, CURLOPT_TIMEOUT, 120);
                curl_easy_setopt(m_pCurl, CURLOPT_URL, pURL);
                curl_easy_setopt(m_pCurl, CURLOPT_WRITEFUNCTION, Url_IconWrite);
                curl_easy_setopt(m_pCurl, CURLOPT_WRITEDATA, &m_HtmlBuff);

CUrlRes = curl_easy_perform(m_pCurl);
                if(CUrlRes == CURLE_OK)
                {
                        //网页下载成功,下载后的网页存在我的CHtmlDataBuff 对象里面,其实这个很简单,如果不涉及到gzip等一些压缩格式的下载,你完全可以用一个string去替代我的CHtmlDataBuff
                }

curl_slist_free_all(chunk);
                return FTPUpload(pSoftid);
        }
        else
        {
                return false;
        }
}
如上所述,我一点点解释上面在干什么,呵呵。
首先curl_slist_append()函数是很有用的,因为如果你什么都不写,CUrl会传输一个类似"Get /你的网页 accept: */*"之类的简单协议,在某些验证较为严格的服务器,这样的Http链接协议字会被丢弃的。也就是说啥都不给你返回,不够千万不要郁闷,Curl的设计者早就给你想好了,curl_slist_append()这个API可以让你伪装成一个标准的网页浏览器的请求,诚然,你们也看到了,我追加了一些Http的选项,这些选项将会附加在你的Http请求中。这样就能顺利的通过那些严格验证的服务器,让它给你返回正确的数据。当然,这里的前提是你必须对Http 1.1协议有一些了解。
curl_easy_setopt(m_pCurl, CURLOPT_HTTPHEADER, chunk);     
这个API是CUrl的设置选项,你可以通过它设置几乎上百个CUrl控制选项。这里只讨论最常用的几个,如果想继续深入,请去 http://curl.haxx.se/ 这里有完整的解释。(当然你的E文要足够过关)
这句话的意思是,将chunk设置的字符串,附加到Http请求消息头上。
curl_easy_setopt(m_pCurl, CURLOPT_TIMEOUT, 120);
这句话的意思是,设置超时时间,如果服务器在120秒不返回,Curl就会触发一个TimeOUT错误。这里建议设置,防止你的代码在某些特殊时刻无限的等待服务器的返回。
curl_easy_setopt(m_pCurl, CURLOPT_URL, pURL);
pURL就是你的网页地址,比如"Http://www.google.com/",当然,不仅仅是可以网页,也可以是Js,jpg等等文件,比如"http://www.163.com/1.jpg"这样也是可以的。实际上说CURLOPT_URL有些狭隘,它可以下载任何url链接可以指向的东西。包括图片以及swf等。
curl_easy_setopt(m_pCurl, CURLOPT_WRITEFUNCTION, Url_IconWrite);
curl_easy_setopt(m_pCurl, CURLOPT_WRITEDATA, &m_HtmlBuff);
接下来是这两句,Url_IconWrite其实是一个回调接口,可能你会问,为什么要这么做呢?我只要网页本身的东西就好了,呵呵,这样做其实为了服务另一个目的,那就是下载进度,你看见很多浏览器有一个下载进度条在走吧,呵呵,对了,通过这个回调函数,你可以设计你的下载进度条,它会给你下载此时此刻的进度和数据块,尤其在支持chunk模式传输返回协议的时候,它会根据每次chunk触发若干次回调。
CURLOPT_WRITEDATA选项是指定一个对象,用于你在回调函数的时候将收到的数据片拼接成一个完整的。
int Url_IconWrite(void *buffer, size_t size, size_t nmemb, void *stream)
{
        size_t stDataLen = size * nmemb;
        char* pData = new char[stDataLen];
        if(NULL == pData)
       {
            return 0;   //返回错误,触发接收失败,停止接收。
       }      
        CHtmlDataBuff* pHtmlDataBuff = (CHtmlDataBuff* )stream;

memcpy(szData, (char* )buffer, stDataLen);

pHtmlDataBuff->AddData(pData, (int)stDataLen);   //将数据包一个个的粘起来,这里可以用你自己的方法。

delete[] pData;

return stDataLen;
}
这个就是绑定的回调函数的写法,当然Url_IconWrite是我起的,你可以用你自己喜欢的名字。
pHtmlDataBuff->AddData(szData, (int)stDataLen);这句话其实就是为了将我收到的数据包拼装在一起。你可以根据你的逻辑自己写一个这样的东西,亦或简单的用一个string +=之类的也行。
buffer是当前数据块的指针,size * nmemb是当前数据的长度,stream其实就是你用CURLOPT_WRITEDATA绑定的对象。
好了,继续说。
CUrlRes = curl_easy_perform(m_pCurl);
这句话就是开始执行你的url下载活动,他返回一个CUrlRes 对象,其实感觉是一个int,如果成功,会返回一个CURLE_OK标记,反之,会给你一个数字,你可以在curl.h里面找到对应的解释。
当你一次抓取执行完毕,你必须设置curl_slist_free_all(chunk);除非你的chunk在下次使用的时候和以前一样,则不必做这样的操作。但是最终你必须curl_slist_free_all(chunk);否则会有内存泄露。
最后,当你执行完你的网页抓取,一定不要忘了curl_easy_cleanup(m_pCurl);释放你这个对象,否则同理,你的内存会泄露。
好了,以上是一个标准的url文件下载或者网页抓取的代码。当然是GET方式的。
接下来说POST,其实也很简单,只要稍微修改一点地方就可以实现POST。
curl_easy_setopt(pCurl, CURLOPT_POST, 8080);
curl_easy_setopt(pCurl, CURLOPT_POSTFIELDS, szData);
CURLOPT_POST参数设置的是你的PSOT端口地址,比如我的例子是8080。
CURLOPT_POSTFIELDS参数附加的是你的具体POST的数据内容,你可以自己去组成这部分。
当然,一般POST协议的时候,最好附加一个chunk。
m_chunk = curl_slist_append(m_chunk, “Content-Length: XXXXX”);  XXXXX为你的POST数据的长度。否则有些服务器可能会认为你的请求非法。
好了,再说一个有意思的运用。
FTP作为服务器而言,现在用在很多地方。
那么如何用CUrl做一个FTP的请求呢?这里也是可以的。(下载很容易的,说一下比较复杂的上传吧。)
以代码为例:
bool CDownIcon::FTPUpload(const char* pSoftid)
{
        CURLcode CUrlRes;
        char szServerPath[HTTP_ICONMAX_1024] = {'\0'};

sprintf(szServerPath, "ftp://127.0.0.1/%s", pSoftid);

FILE* fp = NULL;
        fp = fopen(m_szFileName, "rb");
        if(NULL == fp)
        {
                printf("[Main]fopen (%s) fail!.\n", "a.JPG");
                return false;
        }

fseek(fp, 0l, SEEK_END);
        int nFileSize = (int)ftell(fp);

if(nFileSize <= 0)
        {
                printf("[CReadFile::ReadFile]ftell error(%d)!\n", nFileSize);
        }

fseek(fp, 0l, SEEK_SET);

if(m_pFTPCurl != NULL)
        {
                //curl_easy_setopt(m_pCurl, CURLOPT_VERBOSE, TRUE);   //这个参数可以在FTP过程中显示FTP指令,如果你想看到的话。
                curl_easy_setopt(m_pFTPCurl, CURLOPT_USERPWD, "freeeyes:freeeyes");
                curl_easy_setopt(m_pFTPCurl, CURLOPT_URL, szServerPath);
                curl_easy_setopt(m_pFTPCurl, CURLOPT_PUT, 1);
                curl_easy_setopt(m_pFTPCurl, CURLOPT_INFILE, fp);
                curl_easy_setopt(m_pFTPCurl, CURLOPT_INFILESIZE, (curl_off_t)(size_t)nFileSize);
                curl_easy_setopt(m_pFTPCurl, CURLOPT_FTP_CREATE_MISSING_DIRS, 1);

CUrlRes = curl_easy_perform(m_pFTPCurl);
                if(CUrlRes == CURLE_OK)
                {
                        fclose(fp);
                        return true;
                }
                else
                {
                        fclose(fp);
                        printf("[CDownIcon::FTPUpload](%s) Upload Fail.\n", pSoftid);
                        return false;
                }
        }
        else
        {
                return false;
        }
}
看到了吧,其实和HTTP差不多,只不过要注意几个参数。
curl_easy_setopt(m_pFTPCurl, CURLOPT_USERPWD, "freeeyes:freeeyes");
这是设置你的FTP用户名和地址。当然你的ftp没有密码可以忽略这个选项。
curl_easy_setopt(m_pFTPCurl, CURLOPT_INFILE, fp);
这个fp就是你要上传的FILE*指针。
curl_easy_setopt(m_pFTPCurl, CURLOPT_INFILESIZE, (curl_off_t)(size_t)nFileSize);
这是指定你上传文件的大小。
curl_easy_setopt(m_pFTPCurl, CURLOPT_URL, szServerPath);
这指定的是你的上传后的文件名称,比如"/Img/001/1001/1001.jpg"
curl_easy_setopt(m_pFTPCurl, CURLOPT_FTP_CREATE_MISSING_DIRS, 1);
最有意思的就是这个选项了,我很喜欢,这个选项的意思是,如果你在远程FTP上不存在这样的路径,CURL会帮你建立好。省去了我很多mkdir的麻烦,哪怕是多层目录它都能帮你建立,减少了很多的代码量。
呵呵,看到了吧,其实CUrl用在FTP上也挺简单的。而且是跨平台的。
以上是我对CUrl的一些初步理解,放在这里与大家共享。希望那天大家用到的时候,这些经验能帮你的忙。
当然,这只是最基本的运用。抛砖引玉,抛砖引玉啦。

最新文章

  1. 【翻译】设计模式学习系列1---【Design Patterns Simplified: Part 1【设计模式简述:第一部分】】
  2. 将页面打印成excel
  3. ubantu eclipe
  4. Odoo 8.0 实施开发指南 第一版 试读
  5. enum 枚举的使用
  6. iOS开发笔记10:圆点缩放动画、强制更新、远程推送加语音提醒及UIView截屏
  7. VS2013 越来越慢
  8. HashCode equals
  9. phpStorm使用技巧及快捷键
  10. AS问题解决系列1—Unable to execute DX错误
  11. Ubuntu 14.04 eclipse 提示框背景色更改
  12. XtraForm中更换皮肤
  13. WPFDispatcher示例
  14. Area of Simple Polygons
  15. How to append files to a .tar archive using Apache Commons Compress?(转)
  16. 腾讯视频QLV格式转换mp4的方法
  17. 写你自己 android 多通道打包工具 可以包libs和.so文件
  18. 简单的理解deflate算法
  19. iOS 开发者旅途中的指南针 - LLDB 调试技术
  20. left join 后的条件 位置不同,查询的结果不同

热门文章

  1. JavaScript-4.7-friendly_table---ShinePans
  2. MySQL 压缩解决方案
  3. [转] git clone 远程分支
  4. 一些常用的shell
  5. 使用Python与数据库交互
  6. 基于websocket实现的web聊天室
  7. 转_Greenplum 数据库安装部署(生产环境)
  8. MFC HTTP(S)请求笔记
  9. 阻止SSIS import excel时的默认行为
  10. 【Windows核心编程】一个使用内存映射文件进行进程间通信的例子