⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 httpdownload.cpp

📁 多线程下载工具可支持http,ftp,https多协议....vc编写
💻 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 <m_nParts; i++){
				name.Empty();
				name.Format(".down%d", i);
				name = m_strSavePath + name;
				f[i].Open(name, CFile::modeRead);
				DWORD len = f[i].GetLength();
				if(lpData){
					delete [] lpData;
					lpData = NULL;
				}
				if(lpData == NULL){
					lpData = new char[len];
				}
				f[i].Read(lpData, len);
				file.Write(lpData, len);
				f[i].Close();
				DeleteFile(name);
			}
			if(lpData){
				delete [] lpData;
				lpData = NULL;
			}
			file.Close();
			name.Empty();
			name = m_strSavePath + ".down";
			DeleteFile(name);
			AfxMessageBox("下载完成");
		}
	}
	else{
		HRESULT ret = WaitForSingleObject(m_hThread[0], INFINITE);
		if(m_bTerminate[0]){
			return ;
		}
		if(ret == 0){
		
		
			CFile file, f;
			CString name;
			
			file.Open(m_strSavePath, CFile::modeCreate | CFile::modeWrite);
			name = m_strSavePath + ".down0";
			f.Open(name, CFile::modeRead);
			DWORD len = f.GetLength();
			if(lpData){
				delete [] lpData;
				lpData = NULL;
			}
			if(lpData == NULL){
				lpData = new char[len];
			}
			f.Read(lpData, len);
			file.Write(lpData, len);
			f.Close();
			DeleteFile(name);
		
			if(lpData){
				delete [] lpData;
				lpData = NULL;
			}
			file.Close();
			AfxMessageBox("下载完成");
		}
	}
	if(lpData){
		delete [] lpData;
		lpData = NULL;
	}
	return ;
}
//暂停函数
void CHTTPDownload::OnCancel() 
{
	
    for(int i = 0; i < m_nParts; i++)  m_bTerminate[i] = TRUE;
    HRESULT ret = WaitForSingleObject(m_hNotify, INFINITE);
	if(ret != WAIT_OBJECT_0) return;
	
	
	FILE* fp = NULL;
	CString name;
	name = m_strSavePath + ".down";
    fp = fopen(name, "w+") ;
	fwrite(m_state.range, sizeof(LONG), 2*m_nParts, fp);
	fwrite(m_state.threadsize, sizeof(LONG), m_nParts, fp);
	fwrite(m_state.wcsize, sizeof(LONG), m_nParts, fp);
	fclose(fp);
}



⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -