www.pudn.com > download.rar > HTTPDownload.cpp


// HTTPDownload.cpp: implementation of the CHTTPDownload class. 
// 
////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include "download.h" 
#include "HTTPDownload.h" 
 
#ifdef _DEBUG 
#undef THIS_FILE 
static char THIS_FILE[]=__FILE__; 
#define new DEBUG_NEW 
#endif 
 
 DWORD WINAPI HTTPDownloadThread(LPVOID); 
 DWORD WINAPI HTTPNotify(LPVOID);  
 
 
////////////////////////////////////////////////////////////////////// 
// Construction/Destruction 
////////////////////////////////////////////////////////////////////// 
 
/*这个类主要包含关于HTTP下载的各种函数*/ 
 CHTTPDownload::CHTTPDownload() 
{ 
   for( int i=0; i<10; i++) 
   { 
      m_hThread[i] = NULL; 
	  m_bTerminate[i] = FALSE; 
	  kk[i]=0; 
	  m_sum[i]=0; 
   } 
 
   m_SupportResume = FALSE; 
   m_bResume = FALSE; 
 
} 
 
CHTTPDownload::~CHTTPDownload() 
{ 
 
} 
 
//开始任务函数,主要进行地址的分析,并调用创建线程函数开始下载任务。 
void  CHTTPDownload::Begin(CString Allurl, CString Saveas, int nParts) 
{ 
    for(int i=0;i<10;i++){ 
			m_state.wcsize[i] = 0; 
			m_sum[i] = 0; 
			kk[i]=0; 
			m_dwFileSize=0; 
	} 
 
    if(!(AnalyseURL(Allurl))) 
	{ 
	  AfxMessageBox("URL错误,请确认地址正确"); 
	  return; 
    } 
   
    m_strSavePath = Saveas; 
    m_nParts = nParts; 
    m_strSavePath.TrimLeft(); 
    m_strSavePath.TrimRight(); 
 
    if(SendRequest() != H_LINK_OK) { AfxMessageBox("下载出现错误"); return; } 
 	 
    m_strTempSavePath = m_strSavePath; 
	m_strTempSavePath += ".down"; 
	 
	FILE* fp = NULL; 
	//判断文件是否下载过 
	if((fp = fopen(m_strTempSavePath, "r")) == NULL){ 
		m_state.range[0] = -1; 
		m_bResume = FALSE; 
	} 
	else{ 
		m_bResume = TRUE; 
	    fread(m_state.range, sizeof(LONG), 2*nParts, fp); 
		fread(m_state.threadsize, sizeof(LONG), nParts, fp); 
		fread(m_state.wcsize, sizeof(LONG), nParts, fp); 
		fclose(fp); 
		DeleteFile(m_strTempSavePath);  
	} 
 
    if(m_state.range[0] == -1){ 
	 
	//计算并分配每个线程的任务 
	for(int i = 0; i < m_nParts; i++){ 
			m_state.range[i * 2] = i * (m_dwFileSize /m_nParts); 
			m_state.range[i * 2 + 1] = (i + 1) * (m_dwFileSize / m_nParts) - 1; 
			m_state.threadsize[i] = m_state.range[i * 2 + 1] - m_state.range[i * 2];//每个线程的任务总大小 
		} 
		m_state.range[2*m_nParts-1] = m_dwFileSize; 
		m_state.threadsize[m_nParts-1] = m_state.range[2*m_nParts-1] - m_state.range[2*m_nParts-2]; 
	} 
	 
	CreateThread();  //调用创建线程的任务 
    return ; 
} 
 
//分析地址的函数,将地址分为服务器与文件绝对路径,并获得文件名 
BOOL CHTTPDownload::AnalyseURL(CString Allurl) 
{ 
    m_strServer = _T(""); 
	m_strObject = _T(""); 
	m_Port	  = 0; 
 
	Allurl.TrimLeft(); 
	Allurl.TrimRight(); 
	CString strTemp; 
	int n=Allurl.Find("://"); 
	if(n == -1)  return FALSE;  
	strTemp = Allurl.Mid(n+3); 
	 
    n=strTemp.Find('/'); 
	if(n == -1)  return FALSE; 
	m_strServer = strTemp.Left(n); 
	m_strObject = strTemp.Mid(n); 
     
	n = m_strServer.Find(':'); 
	if(n != -1) 
	{ 
        CString temp = m_strServer.Mid(n+1); 
        m_Port = (USHORT)_ttoi((LPCTSTR)temp); 
		m_strServer = m_strServer.Left(n); 
	} 
	else 
	{ 
		m_Port = 80; 
	} 
    return TRUE; 
} 
 
//向服务器发送第一次请求,假如重定向,会重复直到返回信息正确 
UINT CHTTPDownload::SendRequest() 
{ 
	CString	 strSend,strHeader; 
	int      ret; 
	 
	char	 ReadBuf[1025]; 
	 
	DWORD	 Length,Code; 
 
    while(TRUE) 
	{ 
	    if(m_pSocket.m_hSocket != NULL)   m_pSocket.Close(); 
		 
	    m_pSocket.Create(); 
		m_pSocket.Connect(m_strServer, m_Port); 
		 
		strSend  = "GET " + m_strObject + " HTTP/1.1\r\n"; 
		strSend += "Host: " + m_strServer + "\r\n"; 
		strSend += "Accept: */*\r\n"; 
		strSend += "Pragma: no-cache\r\n";  
		strSend += "Cache-Control: no-cache\r\n"; 
		strSend += "Connection: close\r\n"; 
		strSend += "Range: bytes=100-\r\n"; 
		strSend += "\r\n"; 
 
		ret = m_pSocket.Send(strSend.GetBuffer(0), strSend.GetLength()); 
		strSend.ReleaseBuffer(); 
		 
		strHeader.Empty(); 
        ZeroMemory(ReadBuf,1025); 
		ret = m_pSocket.Receive(ReadBuf, 1025); 
		strHeader += ReadBuf; 
		strHeader += "\r\n"; 
 
        //分析返回信息的代码,是否需要重定向 
		BOOL sign=AnalyseReceive(strHeader,Length, Code); 
		 
		if(sign) 
		{ 
			if(linkcode==1) 
			{ 
				if ( Code == 206 )	//支持断点续传 
				{ 
					m_SupportResume = TRUE; 
					m_dwFileSize = Length + 100;// 整个文件的长度 
				} 
				  else						//不支持断点续传 
				{ 
					m_SupportResume = FALSE; 
					m_dwFileSize =Length + 100;// 整个文件的长度 
				} 
 
				return H_LINK_OK; 
			} 
 
			if(linkcode==2)  continue; 
		} 
		else 
		{ 
			if(linkcode==-1) { AfxMessageBox("服务器返回信息错误"); return H_LINK_ERROR;} 
			if(linkcode==-2) { AfxMessageBox("服务器连接错误"); return H_LINK_ERROR;} 
		} 
	} 
 
	m_pSocket.Close(); 
} 
 
//获取信息函数,获取头信息,文件大小等 
BOOL CHTTPDownload::AnalyseReceive(CString ReadBuf, DWORD &Length, DWORD &Code) 
{ 
	linkcode = 0; 
	Length = 0; 
	Code	= 0; 
 
    CString strHeader =ReadBuf; 
	strHeader.MakeLower(); 
	 
	int n = strHeader.Find("\r\n"); 
	if (n == -1)   { linkcode =-1; return false; } 
	CString strFirstLine = strHeader.Left(n); 
 
	strFirstLine.TrimLeft(); 
	strFirstLine.TrimRight(); 
	n = strFirstLine.Find(' '); 
	if (n == -1)   { linkcode =-1; return false; } 
	strFirstLine = strFirstLine.Mid(n+1); 
	n = strFirstLine.Find(' '); 
	 
	if (n == -1)   {linkcode =-1; return false; } 
	strFirstLine = strFirstLine.Left(n); 
	Code = (DWORD)_ttoi((LPCTSTR)strFirstLine); 
	 
   if( Code >= 300 && Code < 400 )//需要重定向  
	{ 
		n = strHeader.Find("location:"); 
        if (n == -1)   { linkcode =-1; return false; } 
 
		CString strRedirectURL = strHeader.Mid(n + strlen("location:")); 
		n = strRedirectURL.Find("\r\n"); 
	    if (n == -1)   {  linkcode =-1; return false;} 
 
		strRedirectURL = strRedirectURL.Left(n); 
		strRedirectURL.TrimLeft(); 
		strRedirectURL.TrimRight(); 
	     
		n = strRedirectURL.ReverseFind('/'); 
		CString temp = strRedirectURL.Mid(n+1); 
	 
	    n = temp.Find('?'); 
	    if(n != -1) 
		{ 
          temp=temp.Left(n); 
		} 
 
		n = m_strSavePath.ReverseFind('\\'); 
        m_strSavePath = m_strSavePath.Left(n+1); 
		m_strSavePath+= temp; 
 
        if(!(AnalyseURL(strRedirectURL))) 
		{ 
	      linkcode =-1; return false;  
		} 
	    linkcode=2;	return true;   
	} 
 
	if( Code >=400  )  { linkcode =-2; return false; }//连接出现错误 
 
    //获得文件大小 
	n = strHeader.Find("content-length:"); 
    if (n == -1)   {linkcode =-1;  return false; } 
    CString strDownFileLen = strHeader.Mid(n + strlen("content-length:"));	 
	n = strDownFileLen.Find("\r\n"); 
    if (n == -1)   { linkcode =-1; return false; } 
    strDownFileLen = strDownFileLen.Left(n);	 
	strDownFileLen.TrimLeft(); 
	strDownFileLen.TrimRight(); 
	 
    Length = (DWORD) _ttoi( (LPCTSTR)strDownFileLen ); 
 
    linkcode=1; return true;   
} 
 
//创建线程函数 
void CHTTPDownload::CreateThread() 
{ 
	if(m_SupportResume){ 
		DWORD dwThread; 
		m_index = 0; 
		for(int i = 0; i < m_nParts; i++) 
		{ 
			m_bTerminate[i] = FALSE; 
			m_hThread[i] = ::CreateThread(NULL, 0, HTTPDownloadThread, (LPVOID)this, 0, &dwThread); 
		} 
	} 
	else{ 
		 
		DWORD dwThread; 
		m_index = 0; 
		m_bTerminate[0] = FALSE; 
		m_hThread[0] = ::CreateThread(NULL, 0, HTTPDownloadThread, (LPVOID)this, 0, &dwThread); 
 
	} 
	DWORD dwNotify; 
	m_hNotify = ::CreateThread(NULL, 0, HTTPNotify, (LPVOID)this, 0, &dwNotify); 
} 
 
 
DWORD WINAPI HTTPDownloadThread(LPVOID lpParam) 
{ 
	CHTTPDownload* pThis = (CHTTPDownload*)lpParam; 
	int index; 
	index = pThis->m_index; 
	InterlockedIncrement(&pThis->m_index); 
    pThis->ThreadFunc(index); 
	return 0L; 
} 
 
DWORD WINAPI HTTPNotify(LPVOID lpParam) 
{ 
    CHTTPDownload* pThis = (CHTTPDownload*)lpParam; 
    pThis->Finish(); 
	return 0L; 
} 
 
//实际下载函数,向服务器发送下载请求 
void CHTTPDownload::ThreadFunc(int index) 
{ 
	 
	if(m_state.range[2*index] ==-99) { kk[index]=1000; m_sum[index]=m_state.threadsize[index]; return; } 
	 
	m_sum[index] =0; 
 
	CSocket pSocket; 
 
	pSocket.Create(); 
	pSocket.Connect(m_strServer, m_Port); 
	 
	CString strSend, strRange; 
	char szReadBuf[1025]; 
 
	strSend  = "GET " + m_strObject + " HTTP/1.1\r\n"; 
	strSend += "Host: " + m_strServer + "\r\n"; 
	strSend += "Accept: */*\r\n"; 
	strSend += "Pragma: no-cache\r\n";  
	strSend += "Cache-Control: no-cache\r\n"; 
    strSend += "Connection: close\r\n"; 
	strRange.Format("Range: bytes=%d-%d\r\n", m_state.range[2 * index], m_state.range[2 * index + 1]); 
    if(m_SupportResume) 
		strSend += strRange; 
	strSend += "\r\n"; 
 
	int ret = pSocket.Send(strSend.GetBuffer(0), strSend.GetLength()); 
	strSend.ReleaseBuffer(); 
 
 
	ZeroMemory(szReadBuf,1025); 
	ret = pSocket.Receive(szReadBuf, 1025); 
		 
	int n = GetHeadLength(szReadBuf);		 
	 
	CFile file; 
	CString name; 
	name.Format("%d", index); 
	name = m_strTempSavePath + name; 
 
	if(!m_bResume) 
	     file.Open(name, CFile::modeCreate | CFile::modeWrite); 
	else 
	     file.Open(name, CFile::modeWrite); 
 
 
	file.SeekToEnd(); 
 
	file.Write(szReadBuf + n, ret - n); 
	int sum = ret - n, num = 0; 
     
	if(!m_SupportResume) m_state.threadsize[0]=m_dwFileSize;//如果不支持断点序传,须改变进度条的显示 
 
	while(1){ 
		if(m_bTerminate[index]){ 
			m_state.range[2 * index] = m_state.range[2 * index] + sum; 
            m_state.wcsize[index] = m_sum[index]; 
			return ; 
		} 
		ZeroMemory(szReadBuf,1025); 
		 
		if(!(num = pSocket.Receive(szReadBuf, 8)) || num == SOCKET_ERROR)  break; 
		 
		else{ 
			file.Write(szReadBuf, num); 
			sum += num;  
			//进度条显示需要的一些变量 
			m_sum[index]=sum+m_state.wcsize[index]; 
			kk[index]=long((1.0*m_sum[index])*1000/m_state.threadsize[index]); 
		} 
	} 
	 
	file.Close(); 
	pSocket.Close(); 
    m_state.range[2*index]= -99; 
	return ; 
} 
 
//配合下面的函数获取头信息的长度,以便从数据中取出 
int CHTTPDownload::GetHeadLength(char *lpData) 
{ 
	int nhead = 0,nstr;  
	while(1){ 
		nstr=0; 
		BOOL bLine = FALSE; 
	    while ( bLine == FALSE && nhead < 1025 ) 
		{ 
		  char ch = (char)(lpData[nhead]); 
		  if(ch=='\n')  bLine = TRUE; 
		  if(ch!='\n'&&ch!='\r') nstr++; 
		  nhead++; 
		 } 
		 
		if(nstr==0)  break; 
	} 
	return (nhead); 
} 
 
 
 
//全部线程结束后调用的完成函数 
void CHTTPDownload::Finish() 
{ 
	char* lpData = NULL; 
	if(m_SupportResume){ 
		HRESULT ret = WaitForMultipleObjects(m_nParts, m_hThread, TRUE, INFINITE); 
		if(m_bTerminate[0])  return; 
		 
		if(ret == 0){ 
		 
			 
			CFile file, f[10]; 
			CString name; 
			 
			file.Open(m_strSavePath, CFile::modeCreate | CFile::modeWrite); 
			for(int i = 0; i