libcurl使用easy模式阻塞卡死等问题的完美解决

您所在的位置:网站首页 libcurl超时时间设置 libcurl使用easy模式阻塞卡死等问题的完美解决

libcurl使用easy模式阻塞卡死等问题的完美解决

2024-05-09 01:46| 来源: 网络整理| 查看: 265

[cpp] view plaincopy   curl_easy_setopt(handle, CURLOPT_RESUME_FROM_LARGE, getLocalFileLenth(localFile));        

项目背景:

近来jwisp在做OMA-DM的android终端侧,要实现的功能包括FUMO, SCOMO下载管理.由于项目是基于funambol的开源代码,在实现FUMO和SCOMO时,使用了libcurl的库来实现下载服务测的指定文件,下载后实现android固件升级或android应用下载安装功能,最后将FUMO/SCOMO的下载安装结果上报给服务端.

下载流程比较简单, 使用curl_easy_perform即可实现完整的下载流程,安装后,使用OMA DM协议上报给平台侧.但是jwisp这里的需求还有两个异常情况需要处理:

1.      下载过程中,遇手机突然掉电(操作:扣电池), android终端再次启动后应能恢复现场然后自动下载,下载方式为建议断点续传

2.      下载过程中信号中断,中断时间在5分钟之内,终端应尝试重新连接,连接次数在3次以上.重新连接后建议使用断点续传方式继续下载.

libcurl使用时疑难问题

在使用libcurl时, jwisp发现, 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   //全局变量   bool resumeDownload = false;        //是否需要下载的标记位   long downloadFileLenth = 0;         //需要下载的总大小, 远程文件的大小   [cpp] view plaincopy   /* 得到本地文件大小的函数, 若不是续传则返回0, 否则返回指定路径地址的文件大小 */   long getLocalFileLenth(const char* localPath){       if (!resumeDownload){           return 0;       }       return fs_open(localPath).fs_size();   }   [cpp] view plaincopy   /* 得到远程文件的大小, 要下载的文件大小 */   long getDownloadFileLenth(const char *url){       long downloadFileLenth = 0;       CURL *handle = curl_easy_init();       curl_easy_setopt(handle, CURLOPT_URL, url);       curl_easy_setopt(handle, CURLOPT_HEADER, 1);    //只需要header头       curl_easy_setopt(handle, CURLOPT_NOBODY, 1);    //不需要body       if (curl_easy_perform(handle) == CURLE_OK) {           curl_easy_getinfo(handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &downloadFileLenth);       } else {           downloadFileLenth = -1;       }       return downloadFileLenth;   }   [cpp] view plaincopy   /* scomoDownload回调的计算进度条的函数 */   void getProgressValue(const char* localSize, double dt, double dn, double ult, double uln){       double showTotal, showNow;       showTotal = downloadFileLenth;       int localNow = atoi (localSize.c_str());       showNow = localNow + dn;       showProgressBar(showTotal, showNow);   }   [cpp] view plaincopy   /* 直接进行下载的函数 */   public CurlCode scomoDownload(long timeout) {       long localFileLenth = getLocalFileLenth();       const char *localFileLenthStr;       sprint(localFileLenthStr, %ld, localFileLenth);       curl_easy_setopt(handle, CURLOPT_URL, mUrl);       curl_easy_setopt(handle, CURLOPT_HEADER, 0);       curl_easy_setopt(handle, CURLOPT_TIMEOUT, timeout);       curl_easy_setopt(handle, CURLOPT_CONNECTIONTIMEOUT, 0);       curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, &writeDataCallback);        curl_easy_setopt(handle, CURLOPT_WRITEDATA, this);       curl_easy_setopt(handle, CURLOPT_RESUME_FROM_LARGE, localFileLenth);        curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0);       curl_easy_setopt(handle, CURLOPT_ PROGRESSFUNCTION, getProgressValue);        curl_easy_setopt(handle, CURLOPT_PROGRESSDATA, localFileLenthStr);       if (curl_easy_perform) {           resumeDownload = true;           return DS_FAILED;       } else {           resumeDownload = false;           return DS_FINISHED;       }   }   [cpp] view plaincopy   /* downloadControl函数用来控制整个下载过程的节奏, 控制下载的次数, 每次等待的时间等 */   public void downloadControler(){       downloadFileLenth = getDownloadFileLenth();         //下载前得到要下载的文件大小赋值给全局变量       int times = 605;                        //600次*50ms=5分钟, 以此确保5分钟内的重试次数, 而5次是正常下载的中断次数, 意思即是5次内能正常完成下载.       int count = 0;       int timeout = 30;       DSTATUS dstatus = DS_FAILED;       while (count++ 


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3