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的一些初步理解,放在这里与大家共享。希望那天大家用到的时候,这些经验能帮你的忙。
当然,这只是最基本的运用。抛砖引玉,抛砖引玉啦。

转:CURL库在程序中的运用浅析的更多相关文章

  1. CURL库在C++程序中的运用浅析

    最近由于要做一个爬虫项目,要对很多网站进行爬取,所以一直都在看这方面的文章.在翻阅了很多资料后,下载了一个curl库,着实对项目有了很大的帮助. 一.LibCurl基本编程框架 二.一些基本的函数 三 ...

  2. CocoaPods:管理Objective-c 程序中各种第三方开源库关联

    在我们的iOS程序中,经常会用到多个第三方的开源库,通常做法是去下载最新版本的开源库,然后拖拽到工程中. 但是,第三方开源库的数量一旦比较多,版本的管理就非常的麻烦.有没有什么办法可以简化对第三方库的 ...

  3. 将动态库添加到VC程序中

    应用程序使用DLL可以采用两种方式:一种是隐式链接,另一种是显式链接.在使用DLL之前首先要知道DLL中函数的结构信息.Visual C++6.0在VC\bin目录下提供了一个名为Dumpbin.ex ...

  4. (转)CocoaPods:管理Objective-c 程序中各种第三方开源库关联

    在我们的iOS程序中,经常会用到多个第三方的开源库,通常做法是去下载最新版本的开源库,然后拖拽到工程中. 但是,第三方开源库的数量一旦比较多,版本的管理就非常的麻烦.有没有什么办法可以简化对第三方库的 ...

  5. 小程序中使用阿里图标库iconfont

    小程序中使用阿里图标库iconfont 项目中常常需要使用到字体图标,微信小程序中使用字体图标与在平常的web前端中类似但是又有区别.下面以使用阿里图标为例子讲解如何在微信小程序中使用字体图标. 第一 ...

  6. 如何在 .NET 库的代码中判断当前程序运行在 Debug 下还是 Release 下

    我们经常会使用条件编译符 #if DEBUG 在 Debug 下执行某些特殊代码.但是一旦我们把代码打包成 dll,然后发布给其他小伙伴使用的时候,这样的判断就失效了,因为发布的库是 Release ...

  7. Live555 中的客户端动态库.so的调用方式之一 程序中调用

    1.  打开动态链接库:    #include <dlfcn.h>    void *dlopen(const char *filename, int flag);    该函数返回操作 ...

  8. java 编写hadoop程序中使用第三方libxx.so库

    在使用java编写hadoop处理程序时遇到了,java使用依赖的第三方libxx.so库的情况,找到了一种可行的方法,记录一下,希望对别人也有帮助: 加入需要使用的lib库为libxxx.so 1. ...

  9. 在微信小程序中使用阿里图标库Iconfont

    首先想要使用图标,只用上图的五个iconfont相关文件就可以了.(下下来的文件iconfont.wxss开始是.css的后缀,手动改成.wxss就可以在小程序中使用) 然后在app.wxss中引入i ...

随机推荐

  1. ggplot2-为图形加入直线

    本文更新地址:http://blog.csdn.net/tanzuozhev/article/details/51112057 本文在 http://www.cookbook-r.com/Graphs ...

  2. https双向认证訪问管理后台,採用USBKEY进行系统訪问的身份鉴别,KEY的证书长度大于128位,使用USBKEY登录

    近期项目需求,须要实现用USBKEY识别用户登录,採用https双向认证訪问管理后台管理界面,期间碰到过一些小问题,写出来给大家參考下. 1:前期准备工作 USBKEY 硬件:我买的是飞天诚信 epa ...

  3. 【凯子哥带你夯实应用层】使用ActionProvider实现子菜单时遇到的一个坑

    转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992 近期在重温Android基础.在看到ActionProvider的时候遇到一个坑.分享到大家,避免入坑. 首 ...

  4. C99_变长结构体实现

    /************************************************************************* > File Name: C99_lengt ...

  5. swich-----case语句的用法

    转:  http://xinzhi.wenda.so.com/a/1517927252619839

  6. 通过串口工具下发指令的Python脚本

    前言 最近一段时间在测试物联网相关的App自动化,涉及通过串口工具给硬件设备下发指令. 使用的串口工具:SecureCRT 解决办法 通过引用Python的第三方库:serial,通过编写Python ...

  7. linux关机命令详解(转载)

    在linux下一些常用的关机/重启命令有shutdown.halt.reboot.及init,它们都可以达到重启系统的目的,但每个命令的内部工作过程是不同的. Linux centos重启命令: 1. ...

  8. Eclipse-----Eclipse断点调试

  9. Storage,Memcache,KVDB都是存储服务,如何区分何时用何种服务

    Storage :是SAE为开发者提供的分布式文件存储服务,用来存放用户的持久化存储的文件.用户需要先在在线管理平台创建Domain(相当于一级子目录).    Storage为开发者提供分布式文件存 ...

  10. Linux把查询结果写入到文本

    在Linux命令模式下,可以将查询结果写入文件.大概有两种方式,增量写入和覆盖写入. 增量写入: #iostat -m >> /tmp/iostat.txt 覆盖写入: #iostat - ...