libcurl使用easy模式阻塞卡死等问题的完美解决
更新时间:2023-10-16 16:13:01 阅读量: 综合文库 文档下载
- libcurl使用方法推荐度:
- 相关推荐
使用libcurl进行文件下载类项目开发(一)libcurl简单使用介绍
引言:
由于要在android手机测进行DM开发, 其中最重要的就是FUMO和SCOMO下载, 下载使用的是linux开源库libcurl. 于是就把libcurl的使用研究了一遍, 有些心得, 并解决了一些网友的阻塞卡死问题, 于是jwisp将经验和方案分享给大家.
Libcurl使用介绍:
四个关键函数:
1. curl_easy_init()初始化curl环境, 新建curl对象, 返回对象句柄, 使用举例: CURL *handler = curl_easy_init();
2. curl_easy_setopt() 各种设置, 包括URL设置等, 使用举例:
curl_easy_setopt(handler, CURLOPT_URL, “www.http://www.wodefanwen.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-999”), 下载从500个字节开始到999字节结束的文件块
CURLOPT_NOPROGRESS 如果不需要下载进度设置此项为一个非0值
CURLOPT_PROGRESSFUNCTION 设置回调的进度函数, 设置后, 会不断的调用进度函数, 并传递参数总大小和已下载大小给该函数
CURLOPT_PROGRESSDATA 设置传递给回调的进度函数的一个参数, 类型为字符串类型, 后面jwisp会举例说明
curl_easy_getinfo() 部分参数说明
CURLINFO_RESPONSE_CODE 得到perform的执行结果码 CURLINFO_CONTENT_LENGTH_DOWNLOAD 得到下载文件大小
下一节为大家介绍, 如何使用这些libcurl函数进行文件大小的获取, 下载进度显示和断点续传.
(二)使用libcurl实现获取目标文件大小,下载进度显示, 断点续传等功能
本节jwisp为大家举例说明如果使用上节介绍的函数和参数, 在使用libcurl的过程中, 如何获取下载目标文件的大小, 下载进度条, 断点续传等, 这些基本的函数, 将为jwisp在最后处理下载过程异常中断等问题提供支持.
1. 编写得到下载目标文件的大小的函数 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; }
2. 下载中回调自己写的得到下载进度值的函数 下载回调函数的原型应该为:
int progressFunc(const char* flag, double dtotal, double dnow, double ultotal, double ulnow); a. 应该在外部声明一个远程下载文件大小的全局变量 double downloadFileLenth = 0;
为了断点续传, 还应该声明一个本地文件大小的全局变量 double localFileLenth = 0;
b. 编写一个得到进度值的函数getProgressValue()
int getProgressValue(const char* flag, double dt, double dn, double ult, double uln){
double showTotal, showNow; if (downloadFileLenth == 0){ }
showTotal = downloadFileLenth;
if (localFileLenth == 0){ }
showNow = localFileLenth + dn;
//然后就可以调用你自己的进度显示函数了, 这里假设已经有一个进度函数, 那么只需要传递当前下载值和总下载值即可.
showProgressValue(showNow, showTotal); }
c. 在下载中进行三个下载参数的设置
curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(handle, CURLOPT_PROGRESSFUNCTION, getProgressValue); //设置回调的进度函数 curl_easy_setopt(handle, CURLOPT_PROGRESSDATA, “flag”); //此设置对应上面的const char *flag
localFileLenth = getLocalFileLenth(filePath); downloadFileLenth = getDownloadFileLenth(url);
3. 断点续传
用libcurl实现断点续传很简单, 只用两步即可实现, 一是要得到本地文件已下载的大小, 通过函数getLocalFileLenth()方法来得到, 二是设置CURLOPT_RESUME_FROM_LARGE参数的值为已下载本地文件大小.
得到本地文件大小的函数:
==========================================================================================
(三)Libcurl中使用curl_easy_perform阻塞, 遇到无信号卡死的完美解决方案 项目背景:
近来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时, jwisp发现, curl_easy_perform是阻塞的方式进行下载的, curl_easy_perform执行后, 程序会在这里阻塞等待下载结束(成功结束或者失败结束).此时若正常下载一段时间后, 进行网络中断, curl_easy_perform并不会返回失败, 而是阻塞整个程序卡在这里, 此时即使网络连接重新恢复, curl_easy_perform也无法恢复继续下载, 导致整个程序出现”死机”状态.
但是若先断网, 然后进行curl_easy_perform的话, 会直接返回失败, 不会阻塞
在网上搜索后发现, 大家在网上遇到这个问题的很多, 但是解决方法很少, 下面jwisp就把网上建议的可以使
long getLocalFileLenth(const char* localPath);
设置下载点如下即可:
curl_easy_setopt(handle, CURLOPT_RESUME_FROM_LARGE, getLocalFileLenth(localFile));
用的解决方法罗列:
1. 使用multi模式下载, 而不使用easy模式, 此方法的唯一好处就是multi并不会阻塞, 而是立即返回. 但是缺点是带来了问题, 其一就是需要自己去阻塞, 当我们需要返回时再返回, 其二还需要启动一个线程, 需要自己控制整个过程的节奏.
2. 在下载中, 另起一个线程, 若发现下载状态卡死(可以通过定期检查文件大小来实现), 则从外部中断下载线程. 此方法需另起线程, 而且直接中断线程, 会给整个程序带来不稳定.
在尝试使用网上的方法失败后, jwisp终于设计出了自己的方案, 并完美解决信号中断异常, 下载中掉电异常, 断点续传等问题. 并且此方案不需要启动任何另外的线程, 不需要手动进行阻塞, 在信号中断后, 恢复连接最快可在0.5秒内恢复下载.并且恢复下载方式全部为断点续传.
主要的设计思路如下, 下载过程中, 设置超时时间为30秒, 30秒后若下载未完成就重新连接进行下载(这个可解决卡死问题), 每次下载时进行判断, 若不是首次下载则获得当前已下载文件大小, 从该大小处进行续传, 若网络仍处于断开状态, 再次连接会立即返回失败, 此时让当前线程等待0.5秒后进行连接(这个可以解决瞬间恢复连接的问题), 连接次数不超过600次(这个用来保证5分钟后返回失败). 掉电需要在程序已启动时检查是否上次未下载完如果是, 则直接调用下载续传方法即可. 这样基本上所有的问题的流程就都能顺利走下来, 并且下载过程体验好, 可随时取消.
该方案主要通过两个函数来实现, 一个负责进行断点续传和基本设置, 并执行下载, 一个负责控制整个下载重试次数, 返回下载结果. 并且需要注意的是, 安装完成后, 应将相应的文件删除掉.
源代码在下一节
=====================================================================================
(四)使用Libcurl下载文件,解决无信号中断,下载中掉电恢复后断点续传问题的源代码 源代码附上: //全局变量
bool resumeDownload = false; long downloadFileLenth = 0;
//是否需要下载的标记位
//需要下载的总大小, 远程文件的大小
/*得到本地文件大小的函数, 若不是续传则返回0, 否则返回指定路径地址的文件大小*/ long getLocalFileLenth(const char* localPath){
if (!resumeDownload){
return 0;
}
return fs_open(localPath).fs_size(); }
/*得到远程文件的大小, 要下载的文件大小*/ 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; }
/* 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);
}
/* 直接进行下载的函数 */
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 { } }
/* downloadControl函数用来控制整个下载过程的节奏, 控制下载的次数, 每次等待的时间等 */ public void downloadControler(){
downloadFileLenth = getDownloadFileLenth(); int times = 605;
//下载前得到要下载的文件大小赋值给全局变量
resumeDownload = false; return DS_FINISHED;
//600次*50ms=5分钟, 以此确保5分钟内的重试次数, 而5次是正常
下载的中断次数, 意思即是5次内能正常完成下载.
int count = 0; int timeout = 30;
DSTATUS dstatus = DS_FAILED; while (count++ < times){
status = scomoDownload(timeout); if (dstatus == DS_FINISHED){
break;
}
}
Thread.sleep(500);
//每次下载中间间隔500毫秒
resumeDownload = false; if (dstaus == DS_FINISHED) {
updateApp(); }
SAFE_DELETE(localFile); }
//不管下载成功或失败, 完成while循环后将标志回位
//执行软件安装的操作…
//流程最后要确保本地文件删除
resumeDownload是一个非常重要的标记位, 主要用来标识是否需要续传下载, 在初始化时为false, 在下载完成后也应回位成false, 下载过程中若因时间中断未下载完成也为false.
处理下载中掉电后续传也需要这个标记位, 在程序启动时, 进行检测, 若上次没下载完, 修改标志位为true, 然后调用下载入口函数downloadController:
if (scomo_status == 30){ }
若下载环境正常, 1个小时内可以完成的下载可以直接使用此方案来下载, 不用修改控制, 但若是超过1小时的下载, 需要将本方案进行改进. 基本上就是将605那里分开判断600+x, 其中600为每次断网后应重试的次数, x为正常下载应该进行的计数, 分别计算即可.
resumeDownload = true; downloadController();
正在阅读:
libcurl使用easy模式阻塞卡死等问题的完美解决10-16
samson调节阀产品简介05-13
机关优化厉行节约整改措施的相关范文02-16
服务部包间服务流程及标准05-29
2010年浙江省台州市中考试卷(科学)11-30
温州育英国际实验学校高中分校 - 图文04-09
教师外出考察学习报告03-23
沪科版八年级(下)期中试卷(附答案)03-31
学生校园文化活动管理办法2015050703-09
- 多层物业服务方案
- (审判实务)习惯法与少数民族地区民间纠纷解决问题(孙 潋)
- 人教版新课标六年级下册语文全册教案
- 词语打卡
- photoshop实习报告
- 钢结构设计原理综合测试2
- 2014年期末练习题
- 高中数学中的逆向思维解题方法探讨
- 名师原创 全国通用2014-2015学年高二寒假作业 政治(一)Word版
- 北航《建筑结构检测鉴定与加固》在线作业三
- XX县卫生监督所工程建设项目可行性研究报告
- 小学四年级观察作文经典评语
- 浅谈110KV变电站电气一次设计-程泉焱(1)
- 安全员考试题库
- 国家电网公司变电运维管理规定(试行)
- 义务教育课程标准稿征求意见提纲
- 教学秘书面试技巧
- 钢结构工程施工组织设计
- 水利工程概论论文
- 09届九年级数学第四次模拟试卷
- 阻塞
- libcurl
- 模式
- 完美
- 解决
- 使用
- 问题
- easy
- 公因数与公倍数(综合练习)
- 大学有机化学命名指导与习题
- 高分子材料研究方法练习题
- 煤气化发展历史
- 光纤资料翻译
- 参数估计与假设检验的区别和联系
- 湖北交院计算机与信息工程系090381团支部年度工作总结 - 图文
- 《现代交换技术》期末试题库
- 2010年银行从业资格考试《公共基础》模拟试题及答案汇总档
- 中国食品加工行业发展研究报告 - 图文
- 3D MAX自学 - 图文
- 行政处罚项目自由裁量权细化规范标准
- 上海中、低压电网配置原则及典型设计(修订)
- 01第一章 细胞概述
- 淮海战役烈士纪念塔
- 江苏省无锡市2016-2017学年高一上学期期末生物试卷 Word版含解析
- 学生名单
- 消费者行为分析复习资料
- 孔子圣迹图36副画中英文
- 新视野大学英语第四册story summary