当前位置:编程学习 > VC++ >>

WinInet多线程下载器编写历程(2)

昨天设计了一下下载部分的结构,另迫于不堪繁琐,本来准备使用API写的,现在改用MFC了
 
毕竟连CString和基本的一些容器都没有是很耽误时间的,当然最重要的原因是MSDN连很多API参数都没说清楚,
 
很多都只写了作用,没写具体的设置方式,或者可填选项,MFC把能默认的都默认了,省了不少事
 
 
一、.获取待下载文件信息,错误处理没有详细写,等先实现之后再优化吧
 
 
//获取文件大小 
if ((httpfile = (CHttpFile *)session.OpenURL(URL)) ==NULL) 

SendMessage(hwnd,WM_USER_THREAD_ERROR,2,NULL); 
return 2; 

if (! httpfile->QueryInfoStatusCode(state)) 

SendMessage(hwnd,WM_USER_THREAD_ERROR,3,NULL); 
return 3; 

 
if (state != 200) 

SendMessage(hwnd,WM_USER_THREAD_ERROR,4,state); 
return 4; 

 
 
//if(! httpfile->QueryInfo(HTTP_QUERY_FLAG_REQUEST_HEADERS|HTTP_QUERY_RAW_HEADERS_CRLF,requestheader)) 
//{ 
//<span style="white-space:pre">  </span>SendMessage(hwnd,WM_USER_THREAD_ERROR,5,NULL); 
//<span style="white-space:pre">  </span>return 5; 
//} 
//AfxMessageBox(requestheader); 
//查询文件头 
if (! httpfile->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF,responseheader)) 

SendMessage(hwnd,WM_USER_THREAD_ERROR,6,NULL); 
return 6; 

//查询文件长度 
if (! httpfile->QueryInfo(HTTP_QUERY_CONTENT_LENGTH,filelength)) 

SendMessage(hwnd,WM_USER_THREAD_ERROR,7,NULL); 
return 7; 

SendMessage(hwnd,WM_USER_THREAD_REQUEST,filelength,LPARAM((LPCTSTR)responseheader)); 
//填充localfile 
BYTE *tmp = (BYTE *)GlobalAlloc(0,filelength); 
pthis->m_localfile->Write(tmp,filelength); 
GlobalFree(tmp); 
说明:
 
1.使用标识HTTP_QUERY_FLAG_REQUEST_HEADERS查询REQUEST头会出现错误,原因我在网上搜了一下,说是MFC会自己判断一下标识时候越界,这个请求头的标识就被MFC认定是越界了(当然事实是并没有越界),应该是MFC的一个bug,使用API应该就不会出现这问题了。
 
 
2.注意到我创建本地文件的时候,随机填充了最终大小的数据,我的目的是方便后面多线程下载时候的,对文件的随即seek,毕竟多线程下载并不是按顺序来的。
 
当然也可以用网络中窗口的方法,维护一个窗口大小的Cache,就可以实现按顺序写文件了。。当然这是以后优化的目标
 
 
3.最可恶的还是遇到了昨天的问题,我不得不使用GlobalAlloc替代new,来面对未知原因的heap损坏
 
 
 
二、控制下载
 
 
while (pthis->m_state == running) 

    WaitForSingleObject(semaphore_threads,INFINITE); 
 
    //访问 进度锁 
    WaitForSingleObject(mutex_progress,INFINITE); 
    DownloadThreadParam *param = new DownloadThreadParam(); 
    param->p_this = pthis; 
    param->mutex_progress = mutex_progress; 
    param->semaphore_threads = semaphore_threads; 
    //计算线程任务 开始点 
    if (pthis->m_progress == filelength) 
    { 
        ReleaseMutex(mutex_progress); 
        delete param; 
        break; 
    } 
    param->range1 = pthis->m_progress; 
    pthis->m_progress += pthis->m_blocksize; 
 
    //计算线程任务 结束点 
    if (pthis->m_progress > filelength) 
    { 
        pthis->m_progress = filelength; 
    } 
    param->range2 = pthis->m_progress; 
    //释放进度锁 
    ReleaseMutex(mutex_progress); 
 
    CreateThread(NULL,0,DownloadProc,param,0,NULL); 

 
以上是控制任务分发,没啥好说的
下面使用了一个技巧,来等待所有下载线程结束
 
 
int t=0; 
while (true) 

    WaitForSingleObject(semaphore_threads,INFINITE); 
    t += 1; 
    if (t == pthis->m_threadnum) 
    { 
        break; 
    } 

 
不过调试发现,每次都是只能Wait到m_threadnum-1个,最后一个怎么也等不到。。囧,过会儿调试再看看吧
 
三、下载线程
 
 
DWORD WINAPI CDingHttpDownload::DownloadProc(LPVOID lpParam) 

    DownloadThreadParam *pParm = (DownloadThreadParam *)lpParam; 
    CDingHttpDownload *pthis = pParm->p_this; 
    CString slicebuffer; 
    DWORD len = pParm->range2 - pParm->range1; 
     
    //下载任务 
    CInternetSession session(_T("DownloadThread")); 
    CHttpFile *httpfile = (CHttpFile *)session.OpenURL(pthis->m_URL); 
 
    CString header(GetRangeHeader(pParm->range1,pParm->range2)); 
    AfxMessageBox(header); 
    if(!httpfile->AddRequestHeaders(header)) 
    { 
        DWORD errcode = GetLastError(); 
        CString errmsg; 
        errmsg.Format(_T("添加头失败!%d"),errcode); 
        AfxMessageBox(errmsg); 
    } 
 
    httpfile->Read(slicebuffer.GetBuffer(len),len); 
    slicebuffer.ReleaseBuffer(); 
    //AfxMessageBox(slicebuffer); 
    WaitForSingleObject(pParm->mutex_progress,100); 
    //写入任务 
    pthis->m_localfile->Seek(pParm->range1,CFile::begin); 
    pthis->m_localfile->Write(slicebuffer,len); 
 
    ReleaseMutex(pParm->mutex_progress); 
 
    ReleaseSemaphore(pParm->semaphore_threads,1,NULL); 
    delete lpParam; 
    return 0; 

 
CString CDingHttpDownload::GetRangeHeader(UINT range1,UINT range2) 

CString header; 
header.AppendFormat(_T("Range: bytes=%d-%d\r\n"),range1,range2-1); 
return header; 

 
这段代码问题就多了
 
1.通过添加request头的Range标识来实现断点下载,但是只有前5个线程(一次性最多5个线程)可以添加头成功,后面5个线程会添加失败
 
错误代码是12155  即ERROR_HTTP_HEADER_ALREADY_EXISTS
 
The header could not be added because it already exists.
可是我明明每个线程都是重新
补充:软件开发 , Vc ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,