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))
//{
//	SendMessage(hwnd,WM_USER_THREAD_ERROR,5,NULL);
//	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.

可是我明明每个线程都是重新连接的。

我目前的想法是:CSession在创建是有一个名字字符串,我怀疑问题在这里。


2.我httpfile read得到的串貌似并不是我指定范围的,而且连原始数据的任意一部分也不是,我猜测可能是编码问题

3.WaitForSingleObject(pParm->mutex_progress,INFINITE) 会一直等待,我是为了调试将INFINITE改为100

我查了一下,貌似不同线程之间 使用mutex 需要设置一些安全设置。。


希望大牛看到以上问题可以指点一二~



你可能感兴趣的:(thread,多线程,header,Semaphore,null,mfc)