cocos2dx 使用libcurl进行文件传输

CURL是cocos2dx推荐的网络传输库。它包括阻塞传输方式和非阻塞传输方式。在这里值用到了阻塞方式。

本文基于资源在线更新的工作内容讲了3个问题:

设置文件搜寻路径

文件对比方式

非阻塞传输在网络中断情况的解决方法。

文章所用的方法参考了jwisp专栏。网址如下   http://blog.csdn.net/javarat/article/details/8002198点击打开链接


CURL简单传输的方法如下:

CURL *curl_easy_init(void); //初始化一个传输
CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...); //设置传输参数
CURLcode curl_easy_perform(CURL *curl); //执行传输
void curl_easy_cleanup(CURL *curl); //清理

注意:执行完一次传输后,如果进行了清理操作,下一次传输前还要进行初始化。


(一)  设置文件搜寻路径

    

资源的存储方式将分为一开始就打包到App中的部分,和从网络下载的部分。从网络下载的部分存储位置是docmennt目录,这个位置是CCFileUtil::sharedFileUtils()->getWritablePath()获取的。CCFileUtil类提供了设置搜寻路径方法,把document路径加入CCFileUtil的搜寻路径集合中,把它设置成第一搜寻路径,则通过CCFileUtil读取文件时,会先从document目录下查找,没有的话再从默认路径(app)查找。

设置方式如下:



_storagePath =CCFileUtils::sharedFileUtils()->getWritablePath();
void CurlTest::setSearchPath()
{
    vector<string> searchPaths = CCFileUtils::sharedFileUtils()->getSearchPaths();
    vector<string>::iterator iter = searchPaths.begin();
    searchPaths.insert(iter, _storagePath); 
    CCFileUtils::sharedFileUtils()->setSearchPaths(searchPaths);
}


(二)文件对比更新

有了搜寻路径,下一步对比版本文件,把版本更新的文件下载到document目录下。客户端app打包默认打进一个version.plist文件,对比操作过程如下:

1)从服务器端下载 version.plis文件,先命名为version_temp.plist

2)查找document目录下有没有version.plist,没有的话查找app中的这个文件

(3)对比version_temp.plist 和 version.plist ,plist文件中每个文件对应一个版本号,每下载一个文件,就跟更新一次version.plist,并且修改完后存储       version.plist文件。 防止掉电重新开始的时候要从头下载。

(4) 下载完成后,删除旧版的version.plist把 version_temp.plist重命名为version.plist


(三)非阻塞传输在网络中断下的解决方式

我们使用的是单线程的阻塞方式传输,即在传输过程中线程是阻塞的。如果在请求传输之前,也就是

curl_easy_perform( CURL *curl)操作之前,网络是不可用的,这个方法能立刻返回失败,不导致阻塞。如果传输过程中网络中断,则会一直处于curl_easy_perform( CURL *curl)状态,不能退出,导致卡死状态。关于这点,我们利用了断点续传,解决方式如下:


设置一个传输时间timeout,单位是秒。在timeout时间内,如果文件传输完毕,返回成功,否则超过了timenout,返回失败。

设置传输次数times,在times时间内可以可以一直请求传输,超过这个请求次数,判定网络故障无法连接。

那么一个文件最长的传输时间是 T = timeout * 传输次数 ; 


我们设定每隔0.5秒请求一次传输,如果发生网络中断,请求连接时间超过1分钟判定为网络故障。则请求的次数应该是  0.5秒 * 120次 =60秒。 

可以根据网速设置一个单次传输时间,我们先设置成timeout = 30秒。网速先按照10k/s来计算。一个2M的文件,它的传输时间是205秒,那么传输次数至少是7次。


这样在我们设定的网速(10k/s),最大文件(2M),和单次传输时间(30秒)上,计算传传输次数(7次),再加上请求连接1分钟判定网络故障。

得出 times = 7 + 120;   这个参数可以根据具体情况修改。


发生网络故障时候,最长阻塞时间应该是  单次传输时间 + 大约1分钟请求连接时间。



代码如下

单个文件下载:

bool CurlTest:: download(long timeout,std::string filename)
{

std::string fullpath = _storagePath + filename;

FILE *fp = NULL;
if(access(fullpath.c_str(), 0)==0)
{
fp = fopen(fullpath.c_str(), "ab+"); //追加方式打开
}else
{
fp = fopen(fullpath.c_str(), "wb"); //创建方式打开
}

if(fp==NULL)
{
        return false ;
}
long localFileLenth = getLocalFileLenth(filename.c_str()); //已经下载的大小

CURLcode res;
std::string packageUrl = downloadUrl + filename; //下载地址+下载文件名
curl_easy_setopt(_curl, CURLOPT_URL, packageUrl.c_str());
curl_easy_setopt(_curl, CURLOPT_TIMEOUT, timeout); //timeout秒下载时间
curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, downLoadPackage);   //写文件回调方法
curl_easy_setopt(_curl, CURLOPT_WRITEDATA, fp);
curl_easy_setopt(_curl, CURLOPT_RESUME_FROM, localFileLenth);  //断点续传位置
curl_easy_setopt(_curl, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(_curl, CURLOPT_PROGRESSFUNCTION, progressFunc ); //下载进度回调方法

res = curl_easy_perform(_curl);

fclose(fp);

if (res!=0) {

return false;
} else {

return true;
}
}


//控制方法
void CurlTest::downloadControler(std::string filename)
{

int count = 0;
bool  ret = false;
while (count++ < DOWNLOAD_TIMES){
ret = download(DOWNLOAD_TIMEOUT,filename); //直接下载
if (ret ){ //下载完成
break;
}

sleep(0.5); //每次下载中间间隔0.5秒

}

if (ret ) {

if( std::string::npos != filename.find(".zip") && uncompress(filename.c_str()) )
{
CCLog(" 解压成功 %s",filename.c_str());

remove((_storagePath+filename).c_str()); //删除 .zip
}
CCLog(" 下载完成");
}else{
CCLog("下载失败 网络故障");
}

}



获取本地文件大小

long CurlTest::getLocalFileLenth(const char* filename)
{ 
std::string fullPath = CCFileUtils::sharedFileUtils()->getWritablePath() + filename;

FILE *fp = fopen(fullPath.c_str(), "r");
fseek(fp, 0, SEEK_END);
long length = ftell(fp);
fclose(fp);
return length;
}



字典存储成pilst方法

void CurlTest::dictionaryWriteToPlist(CCDictionary *dictionary,const char * filename)
{
std::string plist_save_path = CCFileUtils::sharedFileUtils()->getWritablePath()+filename;
XMLDocument doc;
XMLDeclaration *declare =doc.NewDeclaration("xml version=\"1.0\" encoding=\"UTF-8\"");
doc.LinkEndChild(declare);
XMLElement *element = doc.NewElement("dict");
doc.LinkEndChild(element);
CCArray *array = dictionary->allKeys();

for (int i=0; i<dictionary->count(); i++) {
XMLElement *eleKey = doc.NewElement("key");
XMLText *textKey = doc.NewText(((CCString*)array->objectAtIndex(i))->getCString());
eleKey->LinkEndChild(textKey);
XMLElement *eleValue = doc.NewElement("integer");
std::string valueKey = ((CCString*)(array->objectAtIndex(i)))->m_sString;
CCLOG(valueKey.c_str());
XMLText *textValue = doc.NewText(((CCString*)(dictionary->valueForKey(valueKey)))->getCString());
eleValue->LinkEndChild(textValue);
element->LinkEndChild(eleKey);
element->LinkEndChild(eleValue);
}
doc.SaveFile(plist_save_path.c_str());

}




curl回调方法:这个两个方法必须是静态全局的。

static int progressFunc(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded)
{
//当前下载文件的总体大小totalToDownload
        //当前下载的大小nowDownloaded
//当前下载的文件进度
  float  curpercent =nowDownloaded / totalToDownload *100;
    return 0;
}




static size_t downLoadPackage(void *ptr, size_t size, size_t nmemb, void *userdata)
{
FILE *fp = (FILE*)userdata;
size_t written = fwrite(ptr, size, nmemb, fp);
return written;
}



感谢jwisp专栏。





  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值