网络通信5:HTTP下载器

游戏开发中需要动态下载资源。甚至支持热更新修复代码。都需要引擎支持文件下载功能。写个基于http协议的多线程文件下载器。用第三方库如何选择问题?如何实现接口方便调用问题?等等。其实前面也多次用到libcurl。libcurl作为是一个多协议的便于客户端使用的URL传输库。libcurl已经非常灵活。接口设计的非常细。完全可以满足需求。

这里分享一个基于libcurl的HTTP封装类,其功能包括:同步的(HTTP/HTTPS)GET、POST请求,以及文件下载和进度报告。通过网络通信1-5中通信相关的方式。游戏开发中用到的通信基本都囊括了。引擎底层已经具备了强大的通信能力和提供完善的接口。后续的重点就是如何使用好接口。和提高开发效率。尤其是团队中多人参与研发。如何高效协助的问题。必然需要完整的工具链实现网络消息生成。使真正开发者无需关注底层。只需依葫芦画瓢接收和发送消息即可。但作为游戏开发者还是需要多花些精力去实现下。知识大爆炸时代更需要组织代码能力强的人才。尽量减少代码编程的时间。

C++实现下载器类:Gk8HttpLoader.h

#ifndef __GK8HTTPLOADER_H__
#define __GK8HTTPLOADER_H__

#pragma once
#include "Gk8BaseObj.h"
#include "Gk8ByteMaker.h"
#include "curl/curl.h"


#define GK8LOADER_COUNT     0x0001
#define GK8LOADER_SIZE      0x0002
#define GK8LOADER_ONESIZE   0x0003

class Gk8HttpLoader:public Gk8BaseObj
{
    DECLARE_TOSPP_MAP;
private:
    GK8_BOOL m_bLoading;                //是否正在文件下载
    Gk8ByteMaker m_iFileBufData;        //[文件BUFF数据]

    GK8_INT m_nLoadShowCase;            //[下载显示模式:1个数进度,2整体大小进度,3单个大小进度]
    Gk8Var m_iLoadProgressCallVar;
    Gk8Var m_iLoadFinishCallVar;

    GK8_INT m_nLoadFileCount;           //[已下载文件个数]
    GK8_INT m_nSumFileCount;            //[总文件个数]

    GK8_INT m_nLoadFileSize;            //[已下载文件大小]
    GK8_INT m_nSumFileSize;             //[总文件大小]

private:
    GK8_BOOL InitLoaderThreadSemphore();

public:
    Gk8HttpLoader();
    virtual ~Gk8HttpLoader();
     
    static Gk8HttpLoader* GetInstance();
    GK8_VOID Destroy();
    GK8_VOID TOSPPFUNC AddLoaderFile(GK8_LPCSTR lpFileName,GK8_LPCSTR lpLoaderUrl,GK8_LPCSTR lpLocalFile,GK8_INT nFileSize);
    GK8_VOID TOSPPFUNC SetHttpLoad(GK8_INT nLoadShowCase,Gk8Var& iLoadProgressCallVar,Gk8Var& iLoadFinishCallVar);
    GK8_VOID TOSPPFUNC HttpLoaderFile();
    ;
    GK8_VOID OnProgress(GK8_LPVOID lpLoaderData,GK8_CDOUBLE dlTotal,GK8_CDOUBLE dlNow);
    GK8_VOID ShowLoadBar(GK8_LPVOID lpLoaderData);

    GK8_VOID HttpLoaderTick();
};

#endif

C++实现下载器类:Gk8HttpLoader.cpp

#include "Gk8HttpLoader.h"
#include "Gk8OperSys.h"
#include "Gk8SetMb.cpp"
#include "Gk8BufEntry.cpp"
#include "Gk8FileUtil.h"

#include <queue>
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#include <fcntl.h>

typedef struct tagLOADERDATA
{
    GK8_UINT nNameId;                       //[文件名ID]
    GK8_LPCSTR pFileName;                   //[下载文件名]
    GK8_LPCSTR pLoaderUrl;                  //[下载HTTP URL]
    GK8_LPCSTR pLocalFile;                  //[本地文件存放地]
    GK8_INT nFileSize;                      //[文件大小]
    Gk8ByteMaker* pFileBufData;             //[文件BUFF数据]
    GK8_BOOL bSucceed;                      //[HTTP下载请求反馈成功]
    GK8_INT nResponseCode;                  //[HTTP下载请求反馈码]
    Gk8Str sErrorBuffer;                    //[HTTP下载请求错误信息]
}LOADERDATA,*LPLOADERDATA;


static Gk8Str sg_iLoadProgressEvent("OnLoadProgress");  //[下载进度信息]
static Gk8Str sg_iLoadFinishEvent("OnLoadFinish");      //[下载完成信息]


static Gk8HttpLoader* sg_pHttpLoader=NULL;      //[静态HTTP下载器]
static Gk8BufEntry<LOADERDATA,256>* s_pLoaderCache=NULL;

static GK8_BOOL sg_bHttpLoaderQuit=false;       //[退出HTTP下载器]
static sem_t* sg_pLoaderSem=NULL;               //[信号量的数据类型]

//[线程及互斥定义]
static pthread_t sg_LoaderNetWorkThread;                //[HTTP下载线程]
static pthread_mutex_t sg_RequestLoaderMutex;           //[HTTP请求下载互斥体]
static pthread_mutex_t sg_ResponseLoaderMutex;          //[HTTP响应下载互斥体]

static Gk8SetMb<LPLOADERDATA> sg_iRequestLoaderQueue;   //[HTTP下载请求队列]
static Gk8SetMb<LPLOADERDATA> sg_iResponseLoaderQueue;  //[HTTP下载响应队列]

static GK8_CHAR sg_szLoaderErrorBuf[CURL_ERROR_SIZE];           //[错误信息]
static Gk8Str sg_iLoaderErrorStr;

typedef size_t (*HTTPLOADERWRITE_CALLBACK)(GK8_LPVOID lpData,size_t nSize,size_t nMemBlock,GK8_LPVOID lpResponseData);

#if (GK8_TARGET_PLATFORM==GK8_PLATFORM_IOS)
#define GK8_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE 1
#else
#define GK8_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE 0
#endif

#if GK8_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE
#define GK8_ASYNC_HTTPREQUEST_SEMAPHORE "Gk8HttpAsync"
#else
static sem_t sg_iLoaderSem;
#endif


GK8_INT ProcessLoaderTask(LPLOADERDATA pHttpLoaderResponse,HTTPLOADERWRITE_CALLBACK lpCallBack,GK8_LPINT lpResponseCode);


//[接收HTTP下载数据]
size_t WriteHttpLoaderData(GK8_LPVOID lpData,size_t nSize,size_t nMemBlock,GK8_LPVOID lpResponseData)
{
    Gk8ByteMaker* pResponseData=(Gk8ByteMaker*)lpResponseData;
    size_t nLength=nSize*nMemBlock;
    pResponseData->WriteBuf(lpData,(GK8_INT)nLength);
    return nLength;
}

//[进度回调]
size_t HandleLoaderProgress(GK8_LPVOID lpBuffer,GK8_DOUBLE dlTotal,GK8_DOUBLE dlNow,GK8_DOUBLE ulTotal,GK8_DOUBLE ulNow)
{
    LPLOADERDATA pHttpLoaderResponse=(LPLOADERDATA)lpBuffer;
    Gk8HttpLoader* pHttpLoader=Gk8HttpLoader::GetInstance();
    pHttpLoader->OnProgress(pHttpLoaderResponse,dlTotal,dlNow);
    return 0;
}

//[处理POST请求:流的行事发出]
GK8_INT ProcessLoaderTask(LPLOADERDATA pHttpLoaderResponse,HTTPLOADERWRITE_CALLBACK lpWriteCallBack,GK8_LPINT lpResponseCode)
{
    CURLcode nCurlCode=CURL_LAST;
    CURL* pCurl=curl_easy_init();
    do
    {
        //远程URL,支持HTTP,HTTPS,FTP
        nCurlCode=curl_easy_setopt(pCurl,CURLOPT_URL,pHttpLoaderResponse->pLoaderUrl);
        if(nCurlCode!=CURLE_OK) break;

        //设置User-Agent
        Gk8Str iUserAgentStr="Mozilla/5.0 (Windows NT 6.1; WOW64; rv:13.0) Gecko/20100101 Firefox/13.0.1";
        nCurlCode=curl_easy_setopt(pCurl,CURLOPT_USERAGENT,iUserAgentStr);
        if(nCurlCode!=CURLE_OK) break;

        //设置重定向的最大次数
        nCurlCode=curl_easy_setopt(pCurl,CURLOPT_MAXREDIRS,5);
        if(nCurlCode!=CURLE_OK) break;

        //设置301,302跳转跟随location
        nCurlCode=curl_easy_setopt(pCurl,CURLOPT_FOLLOWLOCATION,1);
        if(nCurlCode!=CURLE_OK) break;

        nCurlCode=curl_easy_setopt(pCurl,CURLOPT_NOSIGNAL,1L);
        if(nCurlCode!=CURLE_OK) break;

        nCurlCode=curl_easy_setopt(pCurl,CURLOPT_POST,false);
        if(nCurlCode!=CURLE_OK) break;

        //下载内容回调函数
        nCurlCode=curl_easy_setopt(pCurl,CURLOPT_WRITEFUNCTION,lpWriteCallBack);
        if(nCurlCode!=CURLE_OK) break;

        nCurlCode=curl_easy_setopt(pCurl,CURLOPT_WRITEDATA,pHttpLoaderResponse->pFileBufData);
        if(nCurlCode!=CURLE_OK) break;

        //进度回调函数
        nCurlCode=curl_easy_setopt(pCurl,CURLOPT_NOPROGRESS, 0);
        if(nCurlCode!=CURLE_OK) break;

        nCurlCode=curl_easy_setopt(pCurl,CURLOPT_PROGRESSDATA,pHttpLoaderResponse);
        if(nCurlCode!=CURLE_OK) break;

        nCurlCode=curl_easy_setopt(pCurl,CURLOPT_PROGRESSFUNCTION,HandleLoaderProgress);
        if(nCurlCode!=CURLE_OK) break;

        //跳过服务器SSL验证,不使用CA证书
        nCurlCode=curl_easy_setopt(pCurl,CURLOPT_SSL_VERIFYPEER,0L);
        if(nCurlCode!=CURLE_OK) break;

        //验证服务器端发送的证书,默认是2(高),1(中),0(禁用)
        nCurlCode=curl_easy_setopt(pCurl,CURLOPT_SSL_VERIFYHOST,0L);
        if(nCurlCode!=CURLE_OK) break;

        nCurlCode=curl_easy_setopt(pCurl,CURLOPT_ERRORBUFFER,sg_szLoaderErrorBuf);
        if(nCurlCode!=CURLE_OK) return false;

        nCurlCode=curl_easy_perform(pCurl);
        if(nCurlCode!=CURLE_OK) break;

        nCurlCode=curl_easy_getinfo(pCurl,CURLINFO_RESPONSE_CODE,lpResponseCode);
        if(nCurlCode!=CURLE_OK||*lpResponseCode!=200)
        {
            nCurlCode=CURLE_HTTP_RETURNED_ERROR;
        }
    }while(0);
    if(pCurl) curl_easy_cleanup(pCurl);

    return (nCurlCode==CURLE_OK?0:1);
}
//[创建网络下载线程]
static GK8_LPVOID LoaderNetWorkThread(GK8_LPVOID lpData)
{
    LPLOADERDATA pHttpLoaderRequest=NULL;
    LPLOADERDATA pHttpLoaderResponse=NULL;

    while(true)
    {
        //Wait for http request tasks from main thread
        GK8_INT nSemWaitRet=sem_wait(sg_pLoaderSem);
        if(nSemWaitRet<0)
        {
            _GK8ERR<<"HttpRequest async thread semaphore error:"<<strerror(errno)<<CR;
            break;
        }
        if(sg_bHttpLoaderQuit) break;

        //[第一步:发送HTTP请求]
        pHttpLoaderRequest=NULL;
        pthread_mutex_lock(&sg_RequestLoaderMutex);
        if(0!=sg_iRequestLoaderQueue.GetSize())
        {
            pHttpLoaderRequest=sg_iRequestLoaderQueue.GetItemAt(0);
            sg_iRequestLoaderQueue.RemoveItemAt(0);
        }
        pthread_mutex_unlock(&sg_RequestLoaderMutex);

        if(NULL==pHttpLoaderRequest) continue;
        //[清除二进制数据]
        pHttpLoaderRequest->pFileBufData->ClearStream();

        //[第二步:libCurl同步请求]
        //pHttpLoaderResponse=new LOADERDATA();
        //memcpy(pHttpLoaderResponse,pHttpLoaderRequest,sizeof(LOADERDATA));
        pHttpLoaderResponse=pHttpLoaderRequest;
        GK8_INT nResponseCode=-1;
        GK8_INT nRetValue=0;


        nRetValue=ProcessLoaderTask(pHttpLoaderResponse,WriteHttpLoaderData,&nResponseCode);

        pHttpLoaderResponse->nResponseCode=nResponseCode;
        if(nRetValue!=0)
        {
            pHttpLoaderResponse->bSucceed=false;
            sg_iLoaderErrorStr=sg_szLoaderErrorBuf;
            pHttpLoaderResponse->sErrorBuffer=sg_iLoaderErrorStr;
        }else
        {
            pHttpLoaderResponse->bSucceed=true;
        }

        pthread_mutex_lock(&sg_ResponseLoaderMutex);
        sg_iResponseLoaderQueue.AddItem(pHttpLoaderResponse);
        pthread_mutex_unlock(&sg_ResponseLoaderMutex);
    }
    pthread_mutex_lock(&sg_RequestLoaderMutex);
    sg_iRequestLoaderQueue.Clear();
    pthread_mutex_unlock(&sg_RequestLoaderMutex);

    if(sg_pLoaderSem!=NULL)
    {
#if GK8_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE
        sem_unlink(GK8_ASYNC_HTTPREQUEST_SEMAPHORE);
        sem_close(sg_pLoaderSem);
#else
        sem_destroy(sg_pLoaderSem);
#endif
        sg_pLoaderSem=NULL;

        pthread_mutex_destroy(&sg_RequestLoaderMutex);
        pthread_mutex_destroy(&sg_ResponseLoaderMutex);

        //[依次删除数据]
        GK8_INT nIndex;
        for(nIndex=0;nIndex<sg_iRequestLoaderQueue.GetSize();nIndex++)
        {
            pHttpLoaderRequest=sg_iRequestLoaderQueue.GetItemAt(nIndex);
            delete pHttpLoaderRequest;
        }
        for(nIndex=0;nIndex<sg_iResponseLoaderQueue.GetSize();nIndex++)
        {
            pHttpLoaderResponse=sg_iResponseLoaderQueue.GetItemAt(nIndex);
            delete pHttpLoaderResponse;
        }
        sg_iRequestLoaderQueue.Destroy();
        sg_iResponseLoaderQueue.Destroy();
    }
    pthread_exit(NULL);
    return 0;
}

/////////////////////////////////////////////CLASS-TOSPP////////////////////////////////////////////////////
BEGIN_TOSPP_MAP(Gk8HttpLoader,Gk8BaseObj)
    TOSPP_FUNC(Gk8HttpLoader,AddLoaderFile,' ',"sssd","AddLoaderFile(lpFileName,lpLoaderUrl,lpLocalFile,nFileSize)")
    TOSPP_FUNC(Gk8HttpLoader,HttpLoaderFile,' ',"","HttpLoaderFile()")
    TOSPP_FUNC(Gk8HttpLoader,SetHttpLoad,' ',"d&v&v","SetHttpLoad(nLoadShowCase,iLoadProgressCallVar,iLoadFinishCallVar)")
END_TOSPP_MAP()

BEGIN_TOSPP_STATIC_FUN_MAP(Gk8HttpLoader)
    TOSPP_STATIC_FUNC(Gk8HttpLoader,GetInstance,'p',"","[Gk8HttpLoader* GetInstance()?创建对象]")
END_TOSPP_STATIC_FUN_MAP
/////////////////////////////////////////////////////////////////////////////////////////////////
Gk8HttpLoader::Gk8HttpLoader()
{
    m_bLoading=false;
    m_nLoadShowCase=1;

    m_nLoadFileCount=0;
    m_nSumFileCount=0;

    m_nLoadFileSize=0;
    m_nSumFileSize=0;

    s_pLoaderCache=new Gk8BufEntry<LOADERDATA,256>;
}

Gk8HttpLoader::~Gk8HttpLoader()
{
    sg_bHttpLoaderQuit=true;
    if(sg_pLoaderSem!=NULL)
    {
        sem_post(sg_pLoaderSem);
    }
    Destroy();
}
//[初始化线程]
GK8_BOOL Gk8HttpLoader::InitLoaderThreadSemphore()
{
    if(sg_pLoaderSem!=NULL)
    {
        return true;
    }else
    {
#if GK8_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE
        sg_pLoaderSem=sem_open(GK8_ASYNC_HTTPREQUEST_SEMAPHORE,O_CREAT,0644,0);
        if(sg_pLoaderSem==SEM_FAILED)
        {
            _GK8ERR<<"Open HttpLoader Semaphore Failed"<<CR;
            sg_pLoaderSem=NULL;
            return false;
        }
#else
        GK8_INT nSemRet=sem_init(&sg_iLoaderSem,0,0);
        if(nSemRet<0)
        {
            _GK8ERR<<"Init HttpLoader Semaphore Failed"<<CR;
            return false;
        }
        sg_pLoaderSem=&sg_iLoaderSem;
#endif
        pthread_mutex_init(&sg_RequestLoaderMutex,NULL);
        pthread_mutex_init(&sg_ResponseLoaderMutex,NULL);

        pthread_create(&sg_LoaderNetWorkThread,NULL,LoaderNetWorkThread,NULL);
        pthread_detach(sg_LoaderNetWorkThread);

        sg_bHttpLoaderQuit=false;
    }
    return true;
}

//[增加HTTP下载文件]
GK8_VOID Gk8HttpLoader::AddLoaderFile(GK8_LPCSTR lpFileName,GK8_LPCSTR lpLoaderUrl,GK8_LPCSTR lpLocalFile,GK8_INT nFileSize)
{
    GK8_UINT nNameId=Gk8OperSys::Str2UINT(lpFileName);
    LPLOADERDATA lpLoaderData=s_pLoaderCache->AddEntryWithNameId(nNameId);
    lpLoaderData->nNameId=nNameId;
    lpLoaderData->pFileName=lpFileName;
    lpLoaderData->pLoaderUrl=lpLoaderUrl;
    lpLoaderData->pLocalFile=lpLocalFile;
    lpLoaderData->nFileSize=nFileSize;
    lpLoaderData->pFileBufData=&m_iFileBufData;

    //[准备显示数据]
    m_nSumFileCount++;
    m_nSumFileSize+=nFileSize;
    m_iFileBufData.WriteAlloc(nFileSize);
}

GK8_VOID Gk8HttpLoader::SetHttpLoad(GK8_INT nLoadShowCase,Gk8Var& iLoadProgressCallVar,Gk8Var& iLoadFinishCallVar)
{
    m_nLoadShowCase=nLoadShowCase;
    m_iLoadProgressCallVar=iLoadProgressCallVar;
    m_iLoadFinishCallVar=iLoadFinishCallVar;

    Gk8Var iEmptyVar;
    if(iLoadProgressCallVar.GetSize()==1 && iLoadProgressCallVar[0].IfInt()) SetEvent(sg_iLoadProgressEvent,iLoadProgressCallVar[0],iEmptyVar);
    if(iLoadFinishCallVar.GetSize()==1 && iLoadFinishCallVar[0].IfInt()) SetEvent(sg_iLoadFinishEvent,iLoadFinishCallVar[0],iEmptyVar);
}

//[开始HTTP下载文件]
GK8_VOID Gk8HttpLoader::HttpLoaderFile()
{
    //正在下载中
    if(m_bLoading) return;
    if(s_pLoaderCache->GetBufNum()<1)
    {
        //[通知脚本下载完成]
        static Gk8Var sl_iParamVar;
        sl_iParamVar.RemoveAll();
        if(m_iLoadFinishCallVar.GetSize()==1)
        {
            RunEventWithArgs(sg_iLoadFinishEvent,sl_iParamVar);
        }else
        {
            if(!m_iLoadFinishCallVar[0].IfPtr()||!IfObjPtr(m_iLoadFinishCallVar[0].GetPtr(),m_iLoadFinishCallVar[0].GetPtrId())) return;

            Gk8Obj* pBindObj=m_iLoadFinishCallVar[0].GetSafePtr();

            pBindObj->OnCall(m_iLoadFinishCallVar[1],sl_iParamVar);
        }
        m_nLoadFileCount=0;
        m_nSumFileCount=0;

        m_nLoadFileSize=0;
        m_nSumFileSize=0;
        return;
    }
    LPLOADERDATA lpLoaderData=s_pLoaderCache->GetBufEntryAt(0);
    if(false==InitLoaderThreadSemphore()) return;
    if(!lpLoaderData) return;
    m_bLoading=true;
    pthread_mutex_lock(&sg_RequestLoaderMutex);
    sg_iRequestLoaderQueue.AddItem(lpLoaderData);
    pthread_mutex_unlock(&sg_RequestLoaderMutex);

    sem_post(sg_pLoaderSem);
}
//[处理进度条]
GK8_VOID Gk8HttpLoader::OnProgress(GK8_LPVOID lpLoaderData,GK8_CDOUBLE dlTotal,GK8_CDOUBLE dlNow)
{
    LPLOADERDATA pLoaderData=(LPLOADERDATA)lpLoaderData;

    static Gk8Var sl_iParamVar;
    sl_iParamVar.RemoveAll();
    sl_iParamVar<<m_nLoadShowCase<<pLoaderData->pFileName<<m_nLoadFileCount<<m_nSumFileCount
        <<(m_nLoadFileSize+(GK8_INT)dlNow)<<m_nSumFileSize<<(GK8_INT)dlTotal<<(GK8_INT)dlNow;
    if(m_iLoadProgressCallVar.GetSize()==1)
    {
        RunEventWithArgs(sg_iLoadProgressEvent,sl_iParamVar);
    }else
    {
        if(!m_iLoadProgressCallVar[0].IfPtr()||!IfObjPtr(m_iLoadProgressCallVar[0].GetPtr(),m_iLoadProgressCallVar[0].GetPtrId())) return;

        Gk8Obj* pBindObj=m_iLoadProgressCallVar[0].GetSafePtr();
        
        pBindObj->OnCall(m_iLoadProgressCallVar[1],sl_iParamVar);
    }
}
//[显示进度条]
GK8_VOID Gk8HttpLoader::ShowLoadBar(GK8_LPVOID lpLoaderData)
{
    LPLOADERDATA pLoaderData=(LPLOADERDATA)lpLoaderData;
    GK8_DOUBLE dlTotal=pLoaderData->nFileSize;
    GK8_DOUBLE dlNow=pLoaderData->nFileSize;
    OnProgress(lpLoaderData,dlTotal,dlNow);
}
//[获取HTTP下载器单例]
Gk8HttpLoader* Gk8HttpLoader::GetInstance()
{
    if(sg_pHttpLoader==NULL) sg_pHttpLoader=new Gk8HttpLoader();
    return sg_pHttpLoader;
}

GK8_VOID Gk8HttpLoader::Destroy()
{
    if(s_pLoaderCache!=NULL)
    {
        delete s_pLoaderCache;
        s_pLoaderCache=NULL;
    }
}
//[HTTP下载器响应帧控制]
GK8_VOID Gk8HttpLoader::HttpLoaderTick()
{
    if(sg_pLoaderSem==NULL) return;

    LPLOADERDATA pLoaderData=NULL;

    pthread_mutex_lock(&sg_ResponseLoaderMutex);
    if(sg_iResponseLoaderQueue.GetSize()>0)
    {
        pLoaderData=sg_iResponseLoaderQueue.GetItemAt(0);
        sg_iResponseLoaderQueue.RemoveItemAt(0);
    }
    pthread_mutex_unlock(&sg_ResponseLoaderMutex);

    if(!pLoaderData) return;

    //[把服务器数据派遣到脚本中]
    Gk8Str iResponstStr;
    if(!pLoaderData->bSucceed)
    {
        _GK8ERR<<"Gk8HttpLoader Response Failed Error Is "<<pLoaderData->sErrorBuffer<<" nResponseCode:"<<pLoaderData->nResponseCode<<CR;
        iResponstStr="UnKown Error";
    }else
    {
        //保存文件
        Gk8File iFile;
        if(iFile.Open(pLoaderData->pLocalFile,Gk8File::modeWriteAbs))
        {
            iFile.Write(pLoaderData->pFileBufData->GetBuf(),pLoaderData->pFileBufData->GetStreamSize());
            iFile.Close();
        }
    }
    m_nLoadFileCount++;
    ShowLoadBar(pLoaderData);
    m_nLoadFileSize+=pLoaderData->nFileSize;

    //移除已下载数据:继续下载
    s_pLoaderCache->RemoveEntryWithNameId(pLoaderData->nNameId);
    m_bLoading=false;
    HttpLoaderFile();
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容