1200 lines
34 KiB
C++
1200 lines
34 KiB
C++
//Download by http://www.NewXing.com
|
|
// HttpDownload.cpp: implementation of the HttpDownload class.
|
|
//
|
|
//------------------------------------HEADER-----------------------------
|
|
|
|
//#ifndef HTTPDOWNLOAD_H
|
|
//#define HTTPDOWNLOAD_H
|
|
|
|
|
|
#include "stdafx.h"
|
|
#include "HttpDownload.h"
|
|
#include "Constant.h"
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[]=__FILE__;
|
|
#define new DEBUG_NEW
|
|
#endif
|
|
extern HHOOK hHook;
|
|
extern LRESULT __stdcall CBTHookProc(long nCode, WPARAM wParam, LPARAM lParam);
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
//////////////////////////////////////////////////////////////////////
|
|
int threadPercent[10]={0};
|
|
|
|
HttpDownload::HttpDownload()
|
|
{
|
|
m_bSupportResume = FALSE;
|
|
m_bResume = FALSE;
|
|
m_bStop=FALSE;
|
|
m_bIsUseFile=FALSE;
|
|
m_bFileLocked =FALSE;
|
|
m_bAddSize=FALSE;
|
|
m_dwDownloadSize=0;
|
|
runningThreadCnt = 0;
|
|
nComplete = 2;//different with initial();
|
|
}
|
|
|
|
HttpDownload::~HttpDownload()
|
|
{
|
|
// fclose(globalFile); //关闭文件
|
|
// if(inforImpl.fromToImpl)
|
|
// delete[] inforImpl.fromToImpl;
|
|
}
|
|
////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////
|
|
//MY CODES STARTS HERE//////////////////////////////////
|
|
///////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////
|
|
|
|
//释放内存
|
|
void HttpDownload::FreeMemory()
|
|
{
|
|
if(globalFile)
|
|
fclose(globalFile);
|
|
if(inforImpl.fromToImpl)
|
|
delete[] inforImpl.fromToImpl;
|
|
}
|
|
|
|
void HttpDownload::SetStop(bool stop)
|
|
{
|
|
m_bStop = stop;
|
|
}
|
|
|
|
//-------------------StartHttpTask()---------------------------------------------------------------------
|
|
//下载前的准备工作
|
|
//初始化变量、连接、分析返回信息、分配任务、写入inforImpl、建立文件
|
|
BOOL HttpDownload::StartHttpTask( NewTask task)
|
|
{
|
|
AfxSocketInit();
|
|
nComplete = 0;
|
|
// brushed=1;
|
|
// asked=0;
|
|
//-----------(1)------------
|
|
//从NewTask获得信息,处理一系列成员变量
|
|
m_nPort=task.port;
|
|
m_strServer=task.server;//(服务器地址)
|
|
m_strObject=task.object;//文件远端路径
|
|
|
|
m_dwThreadCnt=task.threadCnt;
|
|
m_strSavePath = task.savepath;
|
|
m_strSavePath.TrimLeft();//删除字符串前面的空格
|
|
m_strSavePath.TrimRight();//删除字符串后面的空格
|
|
if(m_strSavePath.IsEmpty() ) { //AfxMessageBox("请输入文件保存路径!");
|
|
return FALSE;
|
|
}
|
|
m_strTempSavePath = m_strSavePath;//临时文件...路径
|
|
m_strTempSavePath+= ".adam";
|
|
|
|
|
|
//----------(2)-----------
|
|
//发送请求(按照NewTask的信息)
|
|
//获得文件大小、时间等信息。
|
|
if(SendRequest(0) != SENDREQUEST_SUCCESS) {
|
|
hHook = SetWindowsHookEx(WH_CBT,(HOOKPROC)CBTHookProc,AfxGetInstanceHandle(),NULL);
|
|
// AfxMessageBox("连接失败!\n");
|
|
return FALSE;
|
|
} else {
|
|
//AfxMessageBox("连接成功!\n");
|
|
}
|
|
|
|
|
|
//-------------(3)----------
|
|
// 判断之前没有下过文件.
|
|
|
|
FILE* fp= NULL;
|
|
|
|
//配置文件不存在
|
|
if((fp = fopen(m_strTempSavePath, "r")) == NULL) {
|
|
|
|
m_bResume = FALSE; //配置文件不存在说明之前没有下过.
|
|
//AfxMessageBox("不存在"+m_strTempSavePath);
|
|
//AfxMessageBox("正在创建配置文件...");
|
|
|
|
globalFile = fopen(m_strTempSavePath,"w+b");
|
|
|
|
if(globalFile == NULL) {
|
|
//hHook = SetWindowsHookEx(WH_CBT,(HOOKPROC)CBTHookProc,AfxGetInstanceHandle(),NULL);
|
|
// AfxMessageBox("创建文件"+m_strTempSavePath+"失败");
|
|
return FALSE;
|
|
}
|
|
|
|
//创建本地文件
|
|
if(!CreateNewFile(m_strSavePath ,m_dwFileSize)) {
|
|
return false;
|
|
}
|
|
|
|
//根据文件大小和线程数分配任务写入inforImpl
|
|
GetInfofromDevision(); //
|
|
|
|
//将InforImpl中的信息写入配置文件
|
|
if(!WriteInfoToFile()) {
|
|
return false;
|
|
}
|
|
|
|
}//if()配置文件存在的情况结束
|
|
|
|
|
|
//-------------(4)----------
|
|
//配置文件存在
|
|
else {
|
|
//关闭配置文件
|
|
fclose(fp);
|
|
|
|
//从配置文件中获取下载信息,写入InforImpl
|
|
if(!GetInfofromTemp(m_strTempSavePath)) {
|
|
return false;
|
|
}
|
|
//GetInfofromtemp中调用CreateInforImpl()获取下载任务参数写入inforImpl;
|
|
|
|
|
|
//文件大小相等,并问用户是否要断点续传
|
|
if(m_dwFileSize==inforImpl.fileSize)
|
|
{
|
|
// if(asked==0)
|
|
// {
|
|
// if(AfxMessageBox("该文件曾经下过.\nURL为"+
|
|
// inforImpl.server+inforImpl.object+"\n是否断点续传?",MB_YESNO)==IDNO)
|
|
// {
|
|
// //如果用户不续传
|
|
// //删除文件
|
|
// DeleteFile(m_strSavePath+".chr");
|
|
//
|
|
//
|
|
// //创建本地文件
|
|
// if(!CreateNewFile(m_strSavePath ,m_dwFileSize)) {
|
|
// return false;
|
|
// }
|
|
//
|
|
// //根据文件大小和线程数分配任务写入inforImpl
|
|
// GetInfofromDevision(); //
|
|
//
|
|
// //将InforImpl中的信息写入配置文件
|
|
// if(!WriteInfoToFile()) {
|
|
// return false;
|
|
// }
|
|
// }
|
|
// }
|
|
// brushed=0;
|
|
}
|
|
//文件大小不相等,不续传
|
|
else {
|
|
|
|
DeleteFile(m_strSavePath+".chr");
|
|
//创建本地文件
|
|
if(!CreateNewFile(m_strSavePath ,m_dwFileSize)) {
|
|
return false;
|
|
}
|
|
|
|
//根据文件大小和线程数分配任务
|
|
GetInfofromDevision(); //
|
|
|
|
//将InforImpl中的信息写入配置文件
|
|
if(!WriteInfoToFile()) {
|
|
return false;
|
|
}
|
|
}//else
|
|
|
|
//用户选择续传问件,将inforImpl中信息写入成员变量
|
|
m_strServer=inforImpl.server;
|
|
m_strObject=inforImpl.object;
|
|
m_dwDownloadSize=inforImpl.downloadSize;
|
|
m_dwFileSize=inforImpl.fileSize;
|
|
m_dwThreadCnt=inforImpl.threadCnt;
|
|
m_bResume = TRUE;
|
|
|
|
|
|
|
|
}//if()配置文件存在的情况结束
|
|
|
|
|
|
//--------------(5)--------------
|
|
//多线程下载
|
|
CreateThread();
|
|
|
|
//--------------(6)--------------
|
|
//成功返回true
|
|
return true;
|
|
}
|
|
//-------------------------------CreateNewFile()------------------------------------------
|
|
|
|
|
|
//创建大小为size的新内容文件,加后缀.chr并使其内容为全为0
|
|
|
|
bool HttpDownload::CreateNewFile(CString fileName, DWORD size)
|
|
{
|
|
CString tempFileName = fileName + ".chr";
|
|
FILE *file;
|
|
file = fopen(tempFileName,"w+b"); //以读写方式打开空文件
|
|
if(file == NULL) {
|
|
hHook = SetWindowsHookEx(WH_CBT,(HOOKPROC)CBTHookProc,AfxGetInstanceHandle(),NULL);
|
|
// AfxMessageBox("创建文件失败!");
|
|
return false;
|
|
}
|
|
//AfxMessageBox("正在创建文件,请稍候...");
|
|
char * buffer = new char[60000];
|
|
memset(buffer,'0',60000);
|
|
DWORD writeLen = 0;
|
|
int needLen = 60000;
|
|
while(writeLen < size) {
|
|
|
|
if(writeLen + 60000 > size) {
|
|
|
|
needLen = size -writeLen;
|
|
} else
|
|
needLen = 60000;
|
|
int len = fwrite(buffer,sizeof(char),needLen,file);
|
|
if(len > 0)
|
|
writeLen += len;
|
|
}
|
|
delete[] buffer;
|
|
fclose(file);
|
|
return true;
|
|
}
|
|
|
|
|
|
//-------------------------GetInfofromDevision()---------------------------------------------------------
|
|
|
|
//下载前分配各线程任务,准备工作
|
|
void HttpDownload::GetInfofromDevision()
|
|
{
|
|
|
|
//如果不支持断点续传,则用单线程下载,令线程数为一。
|
|
if(!m_bSupportResume) {
|
|
m_dwThreadCnt=1;
|
|
hHook = SetWindowsHookEx(WH_CBT,(HOOKPROC)CBTHookProc,AfxGetInstanceHandle(),NULL);
|
|
// AfxMessageBox("服务器不支持断点续传!");
|
|
}
|
|
|
|
//在之前没下过的情况下,根据文件大小和线程数分配线程任务.
|
|
if(!m_bResume) {
|
|
inforImpl.server=m_strServer;
|
|
inforImpl.object=m_strObject;
|
|
inforImpl.savePath=m_strSavePath; //保存路径
|
|
inforImpl.fileSize=m_dwFileSize; //文件大小
|
|
inforImpl.threadCnt=m_dwThreadCnt; //线程数
|
|
inforImpl.downloadSize=0; //已经下载文件大小
|
|
DWORD perCnt = inforImpl.fileSize / inforImpl.threadCnt;
|
|
|
|
inforImpl.fromToImpl = new FromToImpl[m_dwThreadCnt];
|
|
//依次写入各线程下载的始末地址
|
|
int i = 0;
|
|
for(i = 0 ;i <inforImpl.threadCnt-1 ; i++) {
|
|
inforImpl.fromToImpl[i].from = perCnt * i;
|
|
inforImpl.fromToImpl[i].to = perCnt*(i+1);
|
|
}
|
|
inforImpl.fromToImpl[i].from = perCnt*(i);
|
|
inforImpl.fromToImpl[i].to =inforImpl.fileSize;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------GetInfofromtemp()---------------------------------------------------------
|
|
|
|
//从配置文件中获取下载任务参数
|
|
bool HttpDownload::GetInfofromTemp(CString fileName)
|
|
{
|
|
|
|
char *buffer = new char[5000];
|
|
while(m_bFileLocked)
|
|
Sleep(50);
|
|
m_bFileLocked = true;
|
|
globalFile=fopen(fileName,"r+b");
|
|
if(globalFile==NULL) {
|
|
// AfxMessageBox("打开配置文件失败!");
|
|
return false;
|
|
}
|
|
fseek(globalFile,0,2);
|
|
int fileLen = ftell(globalFile);
|
|
fseek(globalFile,0,0);
|
|
int readLen = fread(buffer,sizeof(char),fileLen,globalFile);
|
|
buffer[readLen] = 0;
|
|
CString str(buffer);
|
|
delete[] buffer;
|
|
|
|
if(str.IsEmpty()) {
|
|
m_bResume=false;
|
|
m_bFileLocked = false;
|
|
// AfxMessageBox("配置文件为空!\n重新下载");
|
|
//根据文件大小和线程数分配任务
|
|
GetInfofromDevision(); //
|
|
|
|
//将InforImpl中的信息写入配置文件
|
|
if(!WriteInfoToFile()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
m_bFileLocked = false;
|
|
|
|
CreateInforImpl(str);//...获得参数,写入InforImpl
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
//--------------------------CreateInforImpl()---------------------------------------------------------
|
|
//...获得参数,写入InforImpl
|
|
void HttpDownload::CreateInforImpl(CString str)
|
|
{
|
|
|
|
//获得断点续传的参数
|
|
int index;
|
|
CString temp;
|
|
CString server;
|
|
CString object;
|
|
DWORD fileSize;
|
|
DWORD downloadSize;
|
|
int threadCnt;
|
|
|
|
index = str.Find("\r\n");
|
|
server=str.Left(index); //获得服务器地址
|
|
str = str.Mid(index+2,str.GetLength()-index);
|
|
|
|
index = str.Find("\r\n");
|
|
object=str.Left(index); //获得文件远端路径
|
|
str = str.Mid(index+2,str.GetLength()-index);
|
|
|
|
index = str.Find("\r\n");
|
|
m_strSavePath =str.Left(index); //获取文件的保存地址
|
|
str = str.Mid(index+2,str.GetLength()-index);
|
|
|
|
|
|
index = str.Find("\r\n");
|
|
fileSize =(DWORD)_ttoi((LPCTSTR)str.Left(index)); //获取文件大小
|
|
str = str.Mid(index+2,str.GetLength() -index);
|
|
|
|
index = str.Find("\r\n");
|
|
threadCnt =(DWORD)_ttoi((LPCTSTR)str.Left(index)); //获取线程数目
|
|
str = str.Mid(index+2,str.GetLength() - index);
|
|
|
|
index = str.Find("\r\n");
|
|
downloadSize=(DWORD)_ttoi((LPCTSTR)str.Left(index)); //获取已经下载文件的大小
|
|
str = str.Mid(index+2,str.GetLength() - index);
|
|
|
|
//赋值给inforImpl结构
|
|
inforImpl.server =server;
|
|
inforImpl.object=object;
|
|
inforImpl.fromToImpl = new FromToImpl[m_dwThreadCnt];
|
|
inforImpl.savePath = m_strSavePath;
|
|
inforImpl.fileSize = fileSize;
|
|
inforImpl.threadCnt = threadCnt;
|
|
inforImpl.downloadSize = downloadSize;
|
|
CString from ,to;
|
|
|
|
for(int i =0 ;i<inforImpl.threadCnt ; i++) {
|
|
|
|
index = str.Find("\r\n");
|
|
temp = str.Left(index); // 获取from - to 值
|
|
str = str.Mid(index +2 ,str.GetLength() - index);
|
|
index = temp.Find("-");
|
|
from = temp.Left(index);
|
|
to = temp.Mid(index+1,temp.GetLength() - index);
|
|
inforImpl.fromToImpl[i].from =(DWORD)_ttoi((LPCTSTR)from);
|
|
inforImpl.fromToImpl[i].to =(DWORD)_ttoi((LPCTSTR)to);
|
|
}
|
|
//===============debug==============================
|
|
//CWnd OutPut;
|
|
//CString stri;
|
|
//stri.Format("readLen=%d",readLen);
|
|
|
|
|
|
//OutPut.MessageBox(stri);
|
|
//===============debug end===========================
|
|
|
|
|
|
}
|
|
|
|
|
|
//---------------------------SendRequest()-------------------------------------------------------------
|
|
//发送请求,并分析响应消息
|
|
UINT HttpDownload::SendRequest(BOOL bHead)//...bHead=1,HEAD;bHead=0,GET .
|
|
{
|
|
CString strVerb;
|
|
if( bHead )
|
|
strVerb = _T("HEAD ");
|
|
else
|
|
strVerb = _T("GET ");
|
|
|
|
CString strSend,strHeader,strRange;
|
|
|
|
int iStatus = 0,nRet;
|
|
char szReadBuf[1025];
|
|
DWORD dwContentLength,dwStatusCode;
|
|
|
|
while (TRUE)
|
|
{
|
|
|
|
if(m_pSocket.m_hSocket != NULL)
|
|
m_pSocket.Close();
|
|
//建立Socket
|
|
m_pSocket.Create();
|
|
//连接
|
|
|
|
m_pSocket.Connect(m_strServer, m_nPort);
|
|
|
|
|
|
|
|
|
|
//建立发送信息串strSend
|
|
strSend = strVerb + 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";
|
|
if( !m_strReferer.IsEmpty() )
|
|
strSend += "Referer: " + m_strReferer + "\r\n";
|
|
strSend += "Connection: close\r\n";
|
|
strRange = "Range: bytes=100-\r\n";
|
|
strSend += strRange;
|
|
//必须要加一个空行,否则Http服务器将不会应答
|
|
strSend += "\r\n";
|
|
//=====================debug=======================
|
|
|
|
//AfxMessageBox("---strsend---\n"+strSend);
|
|
//=====================debug end===================
|
|
|
|
int ret = m_pSocket.Send(strSend.GetBuffer(0), strSend.GetLength());
|
|
strSend.ReleaseBuffer();
|
|
|
|
strHeader.Empty();
|
|
|
|
ZeroMemory(szReadBuf,1025);
|
|
ret = m_pSocket.Receive(szReadBuf, 1025);
|
|
strHeader += szReadBuf;
|
|
|
|
//====================debug=========================
|
|
//AfxMessageBox("receive strHeader:\n"+strHeader);
|
|
//=================debug end========================
|
|
|
|
nRet = GetInfo(strHeader,dwContentLength,
|
|
dwStatusCode,m_TimeLastModified);//直接把时间写入http类
|
|
switch ( nRet ) {
|
|
|
|
case HTTP_FAIL:
|
|
return SENDREQUEST_FAIL;
|
|
break;
|
|
case HTTP_ERROR:
|
|
return SENDREQUEST_ERROR;
|
|
break;
|
|
case HTTP_REDIRECT:
|
|
continue;
|
|
break;
|
|
case HTTP_OK:
|
|
//m_dwDownloadSize = dwContentLength + 100;
|
|
// 判断一下服务器是否支持断点续传
|
|
if( strRange.IsEmpty() )
|
|
m_dwFileSize = dwContentLength + 100; // 整个文件的长度
|
|
else {
|
|
|
|
if ( dwStatusCode == 206 ) { //支持断点续传
|
|
|
|
m_bSupportResume = TRUE;
|
|
m_dwFileSize = dwContentLength + 100;
|
|
} else { //不支持断点续传
|
|
|
|
m_bSupportResume = FALSE;
|
|
m_dwFileSize = dwContentLength + 100;
|
|
}
|
|
}
|
|
|
|
return SENDREQUEST_SUCCESS;//......成功!!!!
|
|
break;
|
|
default:
|
|
return SENDREQUEST_FAIL;
|
|
break;
|
|
}
|
|
}
|
|
m_pSocket.Close();
|
|
return SENDREQUEST_SUCCESS;
|
|
}
|
|
|
|
//---------------------------GetInfo()----------------------------------------------------------------
|
|
//分析响应消息,获得返回码,时间,文件长度等信息
|
|
|
|
|
|
UINT HttpDownload::GetInfo(LPCTSTR lpszHeader, DWORD &dwContentLength,
|
|
DWORD &dwStatusCode, CTime &TimeLastModified)
|
|
{
|
|
|
|
dwContentLength = 0;
|
|
dwStatusCode = 0;
|
|
TimeLastModified= CTime::GetCurrentTime();
|
|
|
|
CString strHeader = lpszHeader;
|
|
strHeader.MakeLower();//...将strHeader转为小写.
|
|
|
|
//拆分出HTTP应答的头信息的第一行
|
|
int nPos = strHeader.Find("\r\n");
|
|
if (nPos == -1)
|
|
return HTTP_FAIL;
|
|
|
|
CString strFirstLine = strHeader.Left(nPos);
|
|
|
|
// 获得返回码: Status Code
|
|
strFirstLine.TrimLeft();
|
|
strFirstLine.TrimRight();
|
|
nPos = strFirstLine.Find(' ');
|
|
if( nPos == -1 )
|
|
return HTTP_FAIL;
|
|
strFirstLine = strFirstLine.Mid(nPos+1);
|
|
nPos = strFirstLine.Find(' ');
|
|
if( nPos == -1 )
|
|
return HTTP_FAIL;
|
|
strFirstLine = strFirstLine.Left(nPos);
|
|
dwStatusCode = (DWORD)_ttoi((LPCTSTR)strFirstLine);//...转换为整型
|
|
|
|
// 检查返回码
|
|
if( dwStatusCode >= 300 && dwStatusCode < 400 ) { //检测服务器的应答是否为重定向
|
|
|
|
nPos = strHeader.Find("location:");
|
|
if (nPos == -1)
|
|
return HTTP_FAIL;
|
|
|
|
CString strRedirectFileName = strHeader.Mid(nPos + strlen("location:"));
|
|
nPos = strRedirectFileName.Find("\r\n");
|
|
if (nPos == -1)
|
|
return HTTP_FAIL;
|
|
|
|
strRedirectFileName = strRedirectFileName.Left(nPos);
|
|
strRedirectFileName.TrimLeft();
|
|
strRedirectFileName.TrimRight();//为重定向文件路径
|
|
|
|
// 设置Referer
|
|
m_strReferer = m_strDownloadUrl;//..???????这里的m_strDownloadUrl初始化了吗?
|
|
|
|
// 判断是否重定向到其他的服务器
|
|
nPos = strRedirectFileName.Find("http://");
|
|
|
|
// 重定向到其他服务器
|
|
if( nPos != -1 ) {
|
|
|
|
m_strDownloadUrl = strRedirectFileName;
|
|
// 检验要下载的URL是否有效
|
|
if ( !ParseURL(m_strDownloadUrl))
|
|
return HTTP_FAIL;
|
|
return HTTP_REDIRECT;
|
|
}
|
|
|
|
|
|
// 重定向到本服务器的其他地方
|
|
strRedirectFileName.Replace("\\","/");
|
|
|
|
//相对于根目录
|
|
if( strRedirectFileName[0] == '/' ) {
|
|
|
|
m_strObject = strRedirectFileName;
|
|
return HTTP_REDIRECT;
|
|
}
|
|
|
|
// 相对当前目录
|
|
int nParentDirCount = 0;
|
|
nPos = strRedirectFileName.Find("../");
|
|
while (nPos != -1) {
|
|
|
|
strRedirectFileName = strRedirectFileName.Mid(nPos+3);
|
|
nParentDirCount++;
|
|
nPos = strRedirectFileName.Find("../");
|
|
}
|
|
for (int i=0; i<=nParentDirCount; i++) {
|
|
|
|
nPos = m_strDownloadUrl.ReverseFind('/');
|
|
if (nPos != -1)
|
|
m_strDownloadUrl = m_strDownloadUrl.Left(nPos);
|
|
}
|
|
m_strDownloadUrl = m_strDownloadUrl+"/"+strRedirectFileName;
|
|
|
|
if ( !ParseURL(m_strDownloadUrl))
|
|
return HTTP_FAIL;
|
|
return HTTP_REDIRECT;
|
|
}
|
|
|
|
// 服务器错误,可以重试
|
|
if( dwStatusCode >= 500 )
|
|
return HTTP_ERROR;
|
|
|
|
// 客户端错误,重试无用
|
|
if( dwStatusCode >=400 && dwStatusCode <500 )
|
|
return HTTP_FAIL;
|
|
|
|
// 获取ContentLength
|
|
|
|
nPos = strHeader.Find("content-length:");
|
|
if (nPos == -1)
|
|
return HTTP_FAIL;
|
|
|
|
|
|
CString strDownFileLen = strHeader.Mid(nPos + strlen("content-length:"));
|
|
nPos = strDownFileLen.Find("\r\n");
|
|
if (nPos == -1)
|
|
return HTTP_FAIL;
|
|
|
|
strDownFileLen = strDownFileLen.Left(nPos);
|
|
strDownFileLen.TrimLeft();
|
|
strDownFileLen.TrimRight();
|
|
|
|
// Content-Length:
|
|
dwContentLength = (DWORD) _ttoi( (LPCTSTR)strDownFileLen );//转换为整型
|
|
|
|
// 获取Last-Modified:
|
|
nPos = strHeader.Find("last-modified:");
|
|
if (nPos != -1) {
|
|
|
|
CString strTime = strHeader.Mid(nPos + strlen("last-modified:"));
|
|
nPos = strTime.Find("\r\n");
|
|
if (nPos != -1) {
|
|
|
|
strTime = strTime.Left(nPos);
|
|
strTime.TrimLeft();
|
|
strTime.TrimRight();
|
|
TimeLastModified = GetTime(strTime);
|
|
}
|
|
}
|
|
|
|
return HTTP_OK;
|
|
}
|
|
|
|
|
|
|
|
//------------------------------ParseURL()-----------------------------------------------------------
|
|
//地址解析
|
|
|
|
BOOL HttpDownload::ParseURL(CString str)
|
|
{
|
|
|
|
str.TrimLeft();
|
|
if(str.IsEmpty())
|
|
return FALSE;
|
|
|
|
CString strURL = str;
|
|
// 清除数据
|
|
m_strServer = _T("");
|
|
m_strObject = _T("");
|
|
m_nPort = 0;
|
|
|
|
int nPos = strURL.Find("://");
|
|
if( nPos == -1 )
|
|
return FALSE;
|
|
|
|
// 进一步验证是否为http://
|
|
CString strTemp = strURL.Left( nPos+lstrlen("://") );
|
|
strTemp.MakeLower();
|
|
if( strTemp.Compare("http://") != 0 )
|
|
return FALSE;
|
|
|
|
strURL = strURL.Mid( strTemp.GetLength() );
|
|
nPos = strURL.Find('/');
|
|
if ( nPos == -1 )
|
|
return FALSE;
|
|
|
|
m_strObject = strURL.Mid(nPos);
|
|
strTemp = strURL.Left(nPos);
|
|
|
|
|
|
|
|
// 查找是否有端口号
|
|
nPos = strTemp.Find(":");
|
|
if( nPos == -1 ) {
|
|
|
|
m_strServer = strTemp;
|
|
m_nPort = DEFAULT_HTTP_PORT;
|
|
} else {
|
|
|
|
m_strServer = strTemp.Left( nPos );
|
|
strTemp = strTemp.Mid( nPos+1 );
|
|
m_nPort = (USHORT)_ttoi((LPCTSTR)strTemp);//...字符串转化为短整型如果是Unicode,
|
|
//编译时编译为_atoi如果是ANSI,编译为_wtoi
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//--------------------------GetTime-----------------------------------------------------------------
|
|
|
|
//获得CTime时间
|
|
CTime HttpDownload::GetTime(LPCTSTR lpszTime)
|
|
{
|
|
|
|
int nDay,nMonth,nYear,nHour,nMinute,nSecond;
|
|
|
|
CString strTime = lpszTime;
|
|
int nPos = strTime.Find(',');
|
|
if (nPos != -1) {
|
|
|
|
strTime = strTime.Mid(nPos+1);
|
|
strTime.TrimLeft();
|
|
|
|
CString strDay,strMonth,strYear,strHour,strMinute,strSecond;
|
|
CString strAllMonth = "jan,feb,mar,apr,may,jan,jul,aug,sep,oct,nov,dec";
|
|
strDay = strTime.Left(2);
|
|
nDay = atoi(strDay);
|
|
strMonth = strTime.Mid(3,3);
|
|
strMonth.MakeLower();
|
|
nPos = strAllMonth.Find(strMonth);
|
|
if (nPos != -1) {
|
|
|
|
strMonth.Format("%d",((nPos/4)+1));
|
|
nMonth = atoi(strMonth);
|
|
} else
|
|
nMonth = 1;
|
|
strTime = strTime.Mid(6);
|
|
strTime.TrimLeft();
|
|
nPos = strTime.FindOneOf(" \t");
|
|
if (nPos != -1) {
|
|
|
|
strYear = strTime.Left(nPos);
|
|
nYear = atoi(strYear);
|
|
} else
|
|
nYear = 2000;
|
|
strTime = strTime.Mid(nPos+1);
|
|
strHour = strTime.Left(2);
|
|
nHour = atoi(strHour);
|
|
strMinute = strTime.Mid(3,2);
|
|
nMinute = atoi(strMinute);
|
|
strSecond = strTime.Mid(6,2);
|
|
nSecond = atoi(strSecond);
|
|
}
|
|
|
|
CTime time(nYear,nMonth,nDay,nHour,nMinute,nSecond);
|
|
return time;
|
|
}
|
|
//--------------------------WriteInfoToFile()---------------------------------------------------------
|
|
|
|
//将下载任务的信息按固定格式写入配置文件
|
|
bool HttpDownload::WriteInfoToFile()
|
|
{
|
|
CString writeStr;
|
|
CString fileSizeStr;
|
|
CString threadCntStr;
|
|
CString downloadSizeStr;
|
|
CString from;
|
|
CString to;
|
|
fileSizeStr.Format("%d",inforImpl.fileSize);
|
|
|
|
threadCntStr.Format("%d",inforImpl.threadCnt);
|
|
|
|
downloadSizeStr.Format("%d",m_dwDownloadSize);
|
|
|
|
//写入配置文件的内容
|
|
//文件保存路径
|
|
//文件大小
|
|
//线程数
|
|
//已下载大小
|
|
//各线程的始末位置 “*****-*****”
|
|
writeStr =m_strServer+"\r\n"+m_strObject+"\r\n"+m_strSavePath+"\r\n"+fileSizeStr+"\r\n"+threadCntStr+"\r\n"+downloadSizeStr+"\r\n";
|
|
for(int i = 0; i< inforImpl.threadCnt ; i++) {
|
|
|
|
from.Format("%d",inforImpl.fromToImpl[i].from);
|
|
to.Format("%d",inforImpl.fromToImpl[i].to);
|
|
writeStr +=from +"-"+to+"\r\n";
|
|
}
|
|
while(m_bFileLocked)
|
|
Sleep(10);
|
|
m_bFileLocked = true;
|
|
if(globalFile == NULL) {
|
|
|
|
m_bFileLocked = false;
|
|
return false;
|
|
}
|
|
try {
|
|
|
|
fseek(globalFile,0,0);
|
|
fwrite(writeStr,sizeof(char),writeStr.GetLength(),globalFile);
|
|
m_bFileLocked = false;
|
|
return true;
|
|
} catch(...) {
|
|
|
|
m_bFileLocked = false;
|
|
return false;
|
|
// AfxMessageBox("将信息写入配置文件出错!");
|
|
}
|
|
|
|
}
|
|
|
|
//--------------------------------CreateThread()--------------------------------------------------------
|
|
|
|
|
|
|
|
//多线程下载部分
|
|
void HttpDownload::CreateThread()
|
|
{
|
|
|
|
if(!m_bStop) {
|
|
|
|
CString s_ThreadCnt;
|
|
//s_ThreadCnt.Format("将创建 %d 个线程!\n",m_dwThreadCnt);
|
|
//AfxMessageBox(s_ThreadCnt);
|
|
//DWORD dwThread;
|
|
m_index = 0;
|
|
CWinThread* Thread[10];
|
|
for(int i = 0; i<m_dwThreadCnt; i++) {
|
|
|
|
Thread[i]=AfxBeginThread(threadFun,(LPVOID)this);//启动线程
|
|
runningThreadCnt++;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------Download()-------------------------------------------------------
|
|
|
|
//每个线程的下载函数
|
|
bool HttpDownload::Download(int index)
|
|
{
|
|
AfxSocketInit();
|
|
nComplete = 0;
|
|
CString s_index;
|
|
s_index.Format("线程%d",index);
|
|
//AfxMessageBox(s_index+"启动");
|
|
|
|
if(inforImpl.fromToImpl[index].from==inforImpl.fromToImpl[index].to) {
|
|
|
|
threadPercent[index]=100;
|
|
runningThreadCnt--;
|
|
return true;
|
|
}
|
|
|
|
CSocket pSocket;
|
|
pSocket.Create();
|
|
if(!m_bStop&&inforImpl.fromToImpl[index].from < inforImpl.fromToImpl[index].to) {
|
|
//发送请求
|
|
int firstLen;
|
|
pSocket.Connect(m_strServer, m_nPort);
|
|
|
|
CString strSend, strRange;
|
|
|
|
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";
|
|
if( !m_strReferer.IsEmpty() )
|
|
strSend += "Referer: " + m_strReferer + "\r\n";
|
|
strSend += "Connection: close\r\n";
|
|
strRange.Format("Range: bytes=%d-%d\r\n",inforImpl.fromToImpl[index].from, inforImpl.fromToImpl[index].to);
|
|
if(m_bSupportResume)
|
|
strSend += strRange;
|
|
//必须要加一个空行,否则服务器将不应答
|
|
strSend += "\r\n";
|
|
|
|
int re =pSocket.Send(strSend.GetBuffer(0), strSend.GetLength());//
|
|
strSend.ReleaseBuffer();//??GetBuffer的用法
|
|
//AfxMessageBox(strSend);
|
|
|
|
DWORD lpArgument;
|
|
if(!pSocket.AsyncSelect(0) && !pSocket.IOCtl(FIONBIO, &lpArgument)) {
|
|
// AfxMessageBox("Error!");
|
|
runningThreadCnt--;
|
|
return false; //...
|
|
//The lpArgument parameter points at a DWORD, which is nonzero if nonblocking mode
|
|
//is to be enabled and zero if it is to be disabled.
|
|
}
|
|
|
|
char szReadBuf[1024];
|
|
ZeroMemory(szReadBuf,1024);
|
|
DWORD ret = pSocket.Receive(szReadBuf, 1024);//...ret=the number of bytes received
|
|
DWORD headLen= GetHeadLength(szReadBuf);
|
|
//szReadBuf[ret]=0;
|
|
//取响应消息的长度,这部分不写入文件
|
|
|
|
if(ret-headLen<inforImpl.fromToImpl[index].to-inforImpl.fromToImpl[index].from) {
|
|
firstLen=ret-headLen;//第一次下载的文件的长度
|
|
} else {
|
|
firstLen=inforImpl.fromToImpl[index].to-inforImpl.fromToImpl[index].from;
|
|
}
|
|
|
|
|
|
while(m_bAddSize) {
|
|
Sleep(50);
|
|
}
|
|
m_bAddSize=TRUE;
|
|
m_dwDownloadSize+=firstLen;
|
|
m_bAddSize=FALSE;
|
|
inforImpl.fromToImpl[index].from+=firstLen;
|
|
|
|
WriteToFile(m_strSavePath,inforImpl.fromToImpl[index].from-firstLen,
|
|
szReadBuf+headLen,firstLen);//第一次下载的写入内容文件
|
|
|
|
WriteInfoToFile();//...将下载信息写入adam文件
|
|
|
|
|
|
}//if
|
|
|
|
|
|
|
|
while(!m_bStop&&inforImpl.fromToImpl[index].from < inforImpl.fromToImpl[index].to) {
|
|
|
|
|
|
DWORD pos=inforImpl.fromToImpl[index].from;
|
|
DWORD len=PER_GETLEN;
|
|
if(inforImpl.fromToImpl[index].to-inforImpl.fromToImpl[index].from>PER_GETLEN) {
|
|
len=PER_GETLEN;
|
|
} else {
|
|
len=inforImpl.fromToImpl[index].to-inforImpl.fromToImpl[index].from;
|
|
}
|
|
|
|
char *buffer = new char[len];
|
|
DWORD cnt =0 ;
|
|
while(!m_bStop && cnt < len) { //下载没有中止并且没存满,先将len存到缓冲区
|
|
|
|
int recLen =pSocket.Receive(buffer+cnt,len-cnt,0); //接收数据
|
|
|
|
if(recLen > 0) {
|
|
|
|
cnt += recLen;
|
|
while(m_bAddSize) {
|
|
Sleep(50);
|
|
}
|
|
m_bAddSize=TRUE;
|
|
m_dwDownloadSize+= recLen;
|
|
m_bAddSize=FALSE;
|
|
inforImpl.fromToImpl[index].from += recLen;
|
|
|
|
threadPercent[index]=(int)( ( (double)inforImpl.fromToImpl[index].from-
|
|
m_dwFileSize/m_dwThreadCnt*index )*100/( inforImpl.fromToImpl[index].to-
|
|
m_dwFileSize/m_dwThreadCnt*index ) );
|
|
} else {
|
|
|
|
if(recLen < 0) {
|
|
//AfxMessageBox("下载有误,请检查连接");
|
|
runningThreadCnt--;
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
}//else
|
|
|
|
}//while
|
|
|
|
|
|
if(cnt < len || cnt == 0) {
|
|
// AfxMessageBox("下载文件失败!");
|
|
Initial(1);
|
|
delete[] buffer;
|
|
runningThreadCnt--;
|
|
return false;
|
|
} else {
|
|
// "下载了一部分数据"
|
|
|
|
//...将数据写入文件
|
|
WriteToFile(m_strSavePath,pos,buffer,len);
|
|
WriteInfoToFile();//...将下载信息写入文件
|
|
|
|
if(m_dwDownloadSize==m_dwFileSize) {//...下载完毕,删除配置文件,重命名内容文件
|
|
|
|
//this->DoOnComplete();
|
|
WriteInfoToFile();
|
|
CString profile =m_strTempSavePath;
|
|
fclose(globalFile);
|
|
CString oldName =m_strSavePath+ ".chr";
|
|
rename(oldName,m_strSavePath);
|
|
|
|
DeleteFile(profile);
|
|
|
|
fclose(globalFile); //关闭文件
|
|
if(inforImpl.fromToImpl)
|
|
delete[] inforImpl.fromToImpl;
|
|
// AfxMessageBox("下载完成\n\n已保存到");
|
|
Initial(2);
|
|
}
|
|
}
|
|
delete[] buffer;
|
|
}
|
|
runningThreadCnt --;
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
|
|
bool HttpDownload::realDownload(DWORD pos ,DWORD len,int index,CSocket sock) //下载数据
|
|
{
|
|
|
|
char *buffer = new char[len];
|
|
DWORD cnt =0 ;
|
|
while(!m_bStop && cnt < len){ //下载没有中止并且没存满,先将len存到缓冲区
|
|
|
|
int recLen =sock.Receive(buffer+cnt,len-cnt,0); //接收数据
|
|
|
|
if(recLen > 0){
|
|
|
|
cnt += recLen;
|
|
while(m_bAddSize){
|
|
Sleep(50);
|
|
}
|
|
m_bAddSize=TRUE;
|
|
m_dwDownloadSize+= recLen;
|
|
m_bAddSize=FALSE;
|
|
inforImpl.fromToImpl[index].from += recLen;
|
|
}
|
|
else{
|
|
|
|
if(recLen < 0){
|
|
AfxMessageBox("下载有误");
|
|
break;
|
|
}
|
|
else{
|
|
continue;
|
|
}
|
|
}//else
|
|
}//while
|
|
|
|
|
|
if(cnt < len || cnt == 0){
|
|
|
|
AfxMessageBox("下载文件失败!");
|
|
delete[] buffer;
|
|
return false;
|
|
}
|
|
else{
|
|
// "下载了一部分数据"
|
|
|
|
//...将数据写入文件
|
|
WriteToFile(m_strSavePath,pos,buffer,len);
|
|
WriteInfoToFile();//...将下载信息写入文件
|
|
|
|
if(m_dwDownloadSize==m_dwFileSize)//...下载完毕,删除配置文件,重命名内容文件
|
|
{
|
|
//this->DoOnComplete();
|
|
WriteInfoToFile();
|
|
CString profile =m_strTempSavePath;
|
|
fclose(globalFile);
|
|
CString oldName =m_strSavePath+ ".chr";
|
|
rename(oldName,m_strSavePath);
|
|
DeleteFile(profile);
|
|
|
|
AfxMessageBox("下载完成!");
|
|
}
|
|
}
|
|
delete[] buffer;
|
|
return true;
|
|
}//realDownload
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
//-----------------------------------GetHeadLength()-------------------------------------------
|
|
|
|
int HttpDownload::GetHeadLength(char *lpData)
|
|
{
|
|
|
|
//...得到Head串的长度,遇到空行停止
|
|
int ndx = 0;
|
|
CString str;
|
|
while(1) {
|
|
str.Empty();
|
|
str = GetLine(lpData, ndx);
|
|
if(str.IsEmpty())
|
|
break;
|
|
}
|
|
return (ndx);
|
|
}
|
|
|
|
|
|
|
|
CString HttpDownload::GetLine(char *lpData, int& ndx)
|
|
{
|
|
//...从lpData串中取出一行,ndx为该行最后一个字符下标。
|
|
|
|
BOOL bLine = FALSE;
|
|
CString str;
|
|
while ( bLine == FALSE && ndx < 1025 ) {
|
|
|
|
char ch = (char)(lpData[ndx]);
|
|
switch( ch ) {
|
|
|
|
case '\r': // ignore
|
|
break;
|
|
case '\n': // end-of-line
|
|
bLine = TRUE;
|
|
break;
|
|
default: // other....
|
|
str += ch;
|
|
break;
|
|
}
|
|
|
|
++ndx;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
void HttpDownload::Initial(int n)
|
|
{
|
|
m_bSupportResume = FALSE;
|
|
m_bResume = FALSE;
|
|
m_bStop=FALSE;
|
|
m_bIsUseFile=FALSE;
|
|
m_bFileLocked =FALSE;
|
|
m_bAddSize=FALSE;
|
|
m_dwDownloadSize=0;
|
|
runningThreadCnt = 0;
|
|
nComplete = n;//different with HttpDownload();
|
|
|
|
}
|
|
//--------------------------------WriteToFile()------------------------------------------------------------------
|
|
|
|
|
|
//将buffer中的数据写入filePath的pos位置
|
|
bool HttpDownload::WriteToFile(CString filePath,DWORD pos ,char *buffer , int len)
|
|
{
|
|
CString tempFileName = filePath + ".chr";
|
|
while(m_bIsUseFile)
|
|
Sleep(50);
|
|
m_bIsUseFile = true;
|
|
FILE *file ;
|
|
file = fopen(tempFileName,"r+b");
|
|
if(file == NULL) {
|
|
CString str=m_strSavePath + ".adam";
|
|
fclose(globalFile);
|
|
//删掉配置文件
|
|
DeleteFile(str);
|
|
// AfxMessageBox("写入内容文件失败!");
|
|
m_bIsUseFile = false;
|
|
return false;
|
|
}
|
|
fseek(file,pos,0);
|
|
fwrite(buffer,sizeof(char),len,file);
|
|
fclose(file);
|
|
m_bIsUseFile = false;
|
|
return true;
|
|
}
|
|
|
|
//=============================threadFun()============================================================
|
|
|
|
UINT threadFun(LPVOID lparam)
|
|
{
|
|
|
|
HttpDownload* pThis = (HttpDownload*)lparam;
|
|
|
|
int index= pThis->m_index;
|
|
|
|
|
|
//pThis->m_index++;
|
|
|
|
InterlockedIncrement(&pThis->m_index);
|
|
|
|
//CString s_index;
|
|
|
|
//s_index.Format("%d",index);
|
|
|
|
//AfxMessageBox("调用threadFun"+s_index);
|
|
|
|
int ret=pThis->Download(index);
|
|
|
|
return true;
|
|
}
|