📄 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 <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 + -