libcurl

使用libcurl进行文件下载类项目开发

  (一)、libcurl简单使用介绍

  (二)、用libcurl实现获取目标文件大小, 下载进度显示, 断点续传等功能

  (三)、Libcurl中使用curl_easy_perform阻塞, 遇到无信号卡死的完美解决方案

  (四)、使用Libcurl下载文件,解决无信号中断,下载中掉电恢复后断点续传问题的源代码

Libcurl使用介绍:

       四个关键函数:

    1. curl_easy_init() 初始化curl环境,新建curl对象,返回对象句柄,使用举例:    CURL *handler = curl_easy_init();

    2. curl_easy_setopt() 各种设置, 包括URL设置等,使用举例:

         curl_easy_setopt(handler, CURLOPT_URL, “www.baidu.com”),其中中间的参数是设置的类别,比较重要,后面会列举说明.

    3. curl_easy_perform() 开始执行下载操作, 若下载失败会返回错误码.例如: CURLcode code = curl_easy_perform(handler)

    4. curl_easy_getinfo() 得到各种下载信息, 包括下载文件名,文件大小等,此操作必须放在curl_easy_perform()之后执行才能得到正确的值.使用举例: long resultCode;

       curl_easy_getinfo(handler, CURLINFO_RESPONSE_CODE, &resultCode);

  必要的参数说明

       curl_easy_setopt()参数:

         CURLOPT_URL  设置目标URL地址

         CURLOPT_HEADER 是否包含http头,包含则设置为一个非0值

         CURLOPT_NOBODY 如果你不需要http的body部分(除header头以外其他部分),设置此项为一个非0值

         CURLOPT_TIMETOU 设置一个超时时间,若超过此时间perform会立即返回,返回码为下载失败对应错误码,单位秒.注意此时间为从调用perform开始后的总的下载时间

        举例curl_easy_setopt(handler, CURLOPT_TIMEOUT, 30) 设置超时时间为30秒,即下载必须在30秒内完成,否则返回下载失败

         CURLOPT_CONNECTIONTIMEOUT 连接超时时间,单位秒.这个参数在easy curl下载中基本没什么实用价值.

         CURLOPT_RESUME_FROM_LARGE 从什么位置开始下载,断点续传主要使用此参数进行配置,使用非常简单,只需要传递一个字节偏移量即可,例如

       curl_easy_setopt(handler, CURLOPT_ RESUME_FROM, 0),表示从第0个字节开始下载, curl_easy_setopt(handler, CURLOPT_ RESUME_FROM, 500),从第500个字节开始下载

         CURLOPT_RANGE 下载指定字节的文件块,参数对应的值格式为X-Y,例如curl_easy_setopt(handler, CURLOPT_ RESUME_FROM, “500-”),下载从500个字节开始到字节结束的文件块

         CURLOPT_NOPROGRESS 如果不需要下载进度设置此项为一个非0值

         CURLOPT_PROGRESSFUNCTION 设置回调的进度函数,设置后,会不断的调用进度函数,并传递参数总大小和已下载大小给该函数

         CURLOPT_PROGRESSDATA 设置传递给回调的进度函数的一个参数,类型为字符串类型,后面jwisp会举例说明

      curl_easy_getinfo() 部分参数说明

         CURLINFO_RESPONSE_CODE 得到perform的执行结果码

         CURLINFO_CONTENT_LENGTH_DOWNLOAD 得到下载文件大小

使用libcurl的过程中,如何获取下载目标文件的大小, 下载进度条,断点续传等,这些基本的函数,将为在最后处理下载过程异常中断等问题提供支持.

  1. 编写得到下载目标文件的大小的函数

 1 long getDownloadFileLenth(const char *url){
 2     long downloadFileLenth = 0;
 3     CURL *handle = curl_easy_init();
 4     curl_easy_setopt(handle, CURLOPT_URL, url);
 5     curl_easy_setopt(handle, CURLOPT_HEADER, 1);    //只需要header头
 6     curl_easy_setopt(handle, CURLOPT_NOBODY, 1);    //不需要body
 7     if (curl_easy_perform(handle) == CURLE_OK) {
 8         curl_easy_getinfo(handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &downloadFileLenth);
 9     } else {
10         downloadFileLenth = -1;
11     }
12     return downloadFileLenth;
13 }  

  2. 下载中回调自己写的得到下载进度值的函数

    下载回调函数的原型应该为:

      int progressFunc(const char* flag, double dtotal, double dnow, double ultotal, double ulnow);    

    应该在外部声明一个远程下载文件大小的全局变量

      double downloadFileLenth = 0;  

    为了断点续传, 还应该声明一个本地文件大小的全局变

      double localFileLenth = 0;  

    编写一个得到进度值的函数getProgressValue()

 1 int getProgressValue(const char* flag, double dt, double dn, double ult, double uln){
 2     double showTotal, showNow;
 3     if (downloadFileLenth == 0){
 4         downloadFileLenth = getDownloadFileLenth(url);
 5     }
 6     showTotal = downloadFileLenth;
 7     if (localFileLenth == 0){
 8         localFileLenth = getLocalFileLenth(filePath);
 9     }
10     showNow = localFileLenth + dn;
11     //然后就可以调用你自己的进度显示函数了, 这里假设已经有一个进度函数, 那么只需要传递当前下载值和总下载值即可.
12     showProgressValue(showNow, showTotal);
13 }  

 

  c.在下载中进行三个下载参数的设置

1 curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0);
2
3 curl_easy_setopt(handle, CURLOPT_PROGRESSFUNCTION, getProgressValue);  //设置回调的进度函数
4
5 curl_easy_setopt(handle, CURLOPT_PROGRESSDATA, “flag”);   //此设置对应上面的const char *flag  

  3.  断点续传

  用libcurl实现断点续传很简单,只用两步即可实现,一是要得到本地文件已下载的大小,通过函数getLocalFileLenth()方法来得到,二是设置CURLOPT_RESUME_FROM_LARGE参数的值为已下载本地文件大小.

  得到本地文件大小的函数:

    1. long getLocalFileLenth(const char* localPath);  

  设置下载点如下即可:

 

    1. curl_easy_setopt(handle, CURLOPT_RESUME_FROM_LARGE, getLocalFileLenth(localFile));        

  libcurl使用时疑难问题

    在使用libcurl时,  curl_easy_perform是阻塞的方式进行下载的, curl_easy_perform执行后,程序会在这里阻塞等待下载结束(成功结束或者失败结束).此时若正常下载一段时间后,进行网络中断, curl_easy_perform并不会返回失败,而是阻塞整个程序卡在这里,此时即使网络连接重新恢复, curl_easy_perform也无法恢复继续下载,导致整个程序出现”死机”状态.

    但是若先断网, 然后进行curl_easy_perform的话,会直接返回失败,不会阻塞

在网上搜索后发现, 大家在网上遇到这个问题的很多,但是解决方法很少,下面jwisp就把网上建议的可以使用的解决方法罗列:

1.      使用multi模式下载,而不使用easy模式,此方法的唯一好处就是multi并不会阻塞,而是立即返回.但是缺点是带来了问题,其一就是需要自己去阻塞,当我们需要返回时再返回,其二还需要启动一个线程,需要自己控制整个过程的节奏.

2.      在下载中,另起一个线程,若发现下载状态卡死(可以通过定期检查文件大小来实现),则从外部中断下载线程.此方法需另起线程,而且直接中断线程,会给整个程序带来不稳定.

在尝试使用网上的方法失败后, jwisp终于设计出了自己的方案,并完美解决信号中断异常,下载中掉电异常,断点续传等问题.并且此方案不需要启动任何另外的线程,不需要手动进行阻塞,在信号中断后,恢复连接最快可在0.5秒内恢复下载.并且恢复下载方式全部为断点续传.

主要的设计思路如下, 下载过程中,设置超时时间为30秒, 30秒后若下载未完成就重新连接进行下载(这个可解决卡死问题),每次下载时进行判断,若不是首次下载则获得当前已下载文件大小,从该大小处进行续传,若网络仍处于断开状态,再次连接会立即返回失败,此时让当前线程等待0.5秒后进行连接(这个可以解决瞬间恢复连接的问题),连接次数不超过600次(这个用来保证5分钟后返回失败).掉电需要在程序已启动时检查是否上次未下载完如果是,则直接调用下载续传方法即可.这样基本上所有的问题的流程就都能顺利走下来,并且下载过程体验好,可随时取消.

该方案主要通过两个函数来实现, 一个负责进行断点续传和基本设置,并执行下载,一个负责控制整个下载重试次数,返回下载结果.并且需要注意的是,安装完成后,应将相应的文件删除掉.

此方案容易理解, 代码比较简单, 但是在此方案之前, jwisp试过不下10中解决方案, 最终这套方案就是由这10套方案改进出来的. 此代码可以方案移植到各位读者需要的环境中去, 只需进行小的参数的改变即可适应

源代码在下一节附上

源代码附上:

 

[cpp] view plaincopy

 

  1. //全局变量  
  2. bool resumeDownload = false;        //是否需要下载的标记位  
  3. long downloadFileLenth = 0;         //需要下载的总大小, 远程文件的大小  

[cpp] view plaincopy

 

  1. /* 得到本地文件大小的函数, 若不是续传则返回0, 否则返回指定路径地址的文件大小 */  
  2. long getLocalFileLenth(const char* localPath){  
  3.     if (!resumeDownload){  
  4.         return 0;  
  5.     }  
  6.     return fs_open(localPath).fs_size();  
  7. }  

[cpp] view plaincopy

 

  1. /* 得到远程文件的大小, 要下载的文件大小 */  
  2. long getDownloadFileLenth(const char *url){  
  3.     long downloadFileLenth = 0;  
  4.     CURL *handle = curl_easy_init();  
  5.     curl_easy_setopt(handle, CURLOPT_URL, url);  
  6.     curl_easy_setopt(handle, CURLOPT_HEADER, 1);    //只需要header头  
  7.     curl_easy_setopt(handle, CURLOPT_NOBODY, 1);    //不需要body  
  8.     if (curl_easy_perform(handle) == CURLE_OK) {  
  9.         curl_easy_getinfo(handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &downloadFileLenth);  
  10.     } else {  
  11.         downloadFileLenth = -1;  
  12.     }  
  13.     return downloadFileLenth;  
  14. }  

[cpp] view plaincopy

 

  1. /* scomoDownload回调的计算进度条的函数 */  
  2. void getProgressValue(const char* localSize, double dt, double dn, double ult, double uln){  
  3.     double showTotal, showNow;  
  4.     showTotal = downloadFileLenth;  
  5.     int localNow = atoi (localSize.c_str());  
  6.     showNow = localNow + dn;  
  7.     showProgressBar(showTotal, showNow);  
  8. }  

[cpp] view plaincopy

 

  1. /* 直接进行下载的函数 */  
  2. public CurlCode scomoDownload(long timeout) {  
  3.     long localFileLenth = getLocalFileLenth();  
  4.     const char *localFileLenthStr;  
  5.     sprint(localFileLenthStr, %ld, localFileLenth);  
  6.     curl_easy_setopt(handle, CURLOPT_URL, mUrl);  
  7.     curl_easy_setopt(handle, CURLOPT_HEADER, 0);  
  8.     curl_easy_setopt(handle, CURLOPT_TIMEOUT, timeout);  
  9.     curl_easy_setopt(handle, CURLOPT_CONNECTIONTIMEOUT, 0);  
  10.     curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, &writeDataCallback);   
  11.     curl_easy_setopt(handle, CURLOPT_WRITEDATA, this);  
  12.     curl_easy_setopt(handle, CURLOPT_RESUME_FROM_LARGE, localFileLenth);   
  13.     curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0);  
  14.     curl_easy_setopt(handle, CURLOPT_ PROGRESSFUNCTION, getProgressValue);   
  15.     curl_easy_setopt(handle, CURLOPT_PROGRESSDATA, localFileLenthStr);  
  16.     if (curl_easy_perform) {  
  17.         resumeDownload = true;  
  18.         return DS_FAILED;  
  19.     } else {  
  20.         resumeDownload = false;  
  21.         return DS_FINISHED;  
  22.     }  
  23. }  

[cpp] view plaincopy

 

  1. /* downloadControl函数用来控制整个下载过程的节奏, 控制下载的次数, 每次等待的时间等 */  
  2. public void downloadControler(){  
  3.     downloadFileLenth = getDownloadFileLenth();         //下载前得到要下载的文件大小赋值给全局变量  
  4.     int times = 605;                        //600次*50ms=5分钟, 以此确保5分钟内的重试次数, 而5次是正常下载的中断次数, 意思即是5次内能正常完成下载.  
  5.     int count = 0;  
  6.     int timeout = 30;  
  7.     DSTATUS dstatus = DS_FAILED;  
  8.     while (count++ < times){  
  9.         status = scomoDownload(timeout);  
  10.         if (dstatus == DS_FINISHED){  
  11.             break;  
  12.         }  
  13.         Thread.sleep(500);              //每次下载中间间隔500毫秒  
  14.     }  
  15.     resumeDownload = false;             //不管下载成功或失败, 完成while循环后将标志回位  
  16.     if (dstaus == DS_FINISHED) {  
  17.         updateApp();                    //执行软件安装的操作…  
  18.     }  
  19.     SAFE_DELETE(localFile);             //流程最后要确保本地文件删除  
  20. }  

resumeDownload是一个非常重要的标记位,主要用来标识是否需要续传下载, 在初始化时为false,在下载完成后也应回位成false, 下载过程中若因时间中断未下载完成也为false.

处理下载中掉电后续传也需要这个标记位, 在程序启动时,进行检测, 若上次没下载完,修改标志位为true, 然后调用下载入口函数downloadController:

      if (scomo_status == 30){

             resumeDownload = true;

             downloadController();

}

 

若下载环境正常, 1个小时内可以完成的下载可以直接使用此方案来下载,不用修改控制, 但若是超过1小时的下载,需要将本方案进行改进. 基本上就是将605那里分开判断600+x,其中600为每次断网后应重试的次数, x为正常下载应该进行的计数,分别计算即可.

时间: 2024-10-30 08:05:02

libcurl的相关文章

libcurl中使用curl_easy_getinfo 产生段错误分析

最近再写一个hsf的代理程序.需要使用libcurl与后端的nginx通信.程序编写过程中遇到一个蹊跷的问题. 调用 code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rsp_code); 后会报段错误. 示例代码如下: static int http_proxy(std::string domain, std::string path, std::string params, std::string &rsp_cont, st

【CURL (LIBCURL) 开发 之一】COCOS2DX之LIBCURL(CURL_EASY)的编程教程(帮助手册)!

本站文章均为 李华明Himi 原创,转载务必在明显处注明:  转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/curl-libcurl/878.html      注意:如果你的服务器是Java的,那么要注意数据之间的大端小端的处理:否则无法正常获取正确的数据! 本篇介绍使用libcurl编程的一般原则和一些基本方法.本文主要是介绍 c 语言的调用接口,同时也可能很好的适用于其他类 c 语言的接口. 跨平台的可移植代码 libcurl库背后的开发人员

curl和libcurl的区别简介

  这篇文章主要介绍了curl和libcurl的区别简介,本文讲解了curl简介.libcurl简介.curl与libcurl对比."curl"的不同意思.PHP中使用curl和libcurl等内容,这么多的curl,搞都搞不懂,看了本文才知道,我们一直用的其实是libcurl,需要的朋友可以参考下 curl简介 curl是利用URL语法在命令行方式下工作的开源文件传输工具. 它支持很多协议:DICT, FILE, FTP, FTPS, Gopher, HTTP, HTTPS, IMA

libcurl.dll丢失怎么办?

  libcurl.dll丢失怎么办?不少用户在给自己的电脑杀完毒,清除一些木马后,发现电脑就一直弹出一个提窗口提醒libcurl.dll丢失,一直提醒,令人感到十分烦躁.那么,libcurl.dll丢失怎么办?快点和小编一起来看看吧. libcurl.dll丢失 libcurl.dll是系统的重要组件之一.libcurl.dll丢失极有可能是盗号木马.流氓软件等恶意程序所导致,其感染相关文件并加载起来,一旦杀毒软件删除被感染的文件,就会导致相关组件缺失,游戏等常用软件运行不起来. libcur

curl和libcurl的区别简介_php技巧

curl简介 curl是利用URL语法在命令行方式下工作的开源文件传输工具. 它支持很多协议:DICT, FILE, FTP, FTPS, Gopher, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, Telnet and TFTP. curl同样支持SSL证书,HTTP POST, HTTP PUT,FTP上传,基于表单的HTTP上传,代理(proxies).coo

C++ 用libcurl库进行http 网络通讯编程

  一.LibCurl基本编程框架libcurl是一个跨平台的网络协议库,支持http, https, ftp, gopher, telnet, dict, file, 和ldap 协议.libcurl同样支持HTTPS证书授权,HTTP POST, HTTP PUT, FTP 上传, HTTP基本表单上传,代理,cookies,和用户认证.想要知道更多关于libcurl的介绍,可以到官网http://curl.haxx.se/上去了解,在这里不再详述. win32版的libcurl下载地址:h

C++ 用libcurl库进行http通讯网络编程

目录索引: 一.LibCurl基本编程框架 二.一些基本的函数 三.curl_easy_setopt函数部分选项介绍 四.curl_easy_perform 函数说明(error 状态码) 五.libcurl使用的HTTP消息头六.获取http应答头信息 七.多线程问题 八.什么时候libcurl无法正常工作 九.关于密码 十.HTTP验证 十一.代码示例 1.基本的http GET/POST操作 2 获取html网页 3 网页下载保存实例 4 进度条实例显示文件下载进度 5 断点续传实例  

使用libcurl进行文件上传

上篇博文讲到了如何使用multicurl来进行http并发访问,今天继续有关curl的主题,来八一八如何使用curl来上传文件,在介绍具体方法之前了解下目前http文件上传的基本实现.     rfc1867描述了如何使用http协议来上传客户端文件,目前基本上所有的浏览器和web服务器都支持http文件上传,它的使用也十分的简单,具体的来说就是在页面上创建一个form表单,表单的enctype属性为multipart/form-data,action为接收上传文件的cgi url,请求方式为p

C语言 HTTP上传文件-利用libcurl库上传文件

原文  http://justwinit.cn/post/7626/ 通常情况下,一般很少使用C语言来直接上传文件,但是遇到使用C语言编程实现文件上传时,该怎么做呢? 借助开源的libcurl库,我们可以容易地实现这个功能.Libcurl是一个免费易用的客户端URL传输库,主要功能是用不同的协议连接和沟通不同的服务器,libcurl当前支持DICT, FILE, FTP, FTPS, Gopher, HTTP, HTTPS, IMAP,IMAPS, LDAP, LDAPS, POP3, POP3

CURL 和LIBCURL C++代码 上传本地文件,好不容易碰到了这种折腾我几天的代码

解决了什么问题:curl在使用各种方式上传文件到服务器.一般的文件上传是通过html表单进行的,通过CURL可以不经过浏览器,直接在服务器端模拟进行表单提交,完成POST数据.文件上传等功能. 服务器端PHP代码: <?phpif ((($_FILES["file"]["type"] == "image/gif") || ($_FILES["file"]["type"] == "image/