cocos2dx 使用libcurl进行文件传输

来源:互联网 发布:矩阵分解的应用 编辑:程序博客网 时间:2024/05/22 00:40

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专栏。





原创粉丝点击