📄 httpdownload.cpp
字号:
// HttpDownLoad.cpp : implementation file
//
#include "stdafx.h"
#include "LeoBlock2004.h"
#include "HttpDownLoad.h"
#include "winsock2.h"
#include "afxinet.h"
#include "HttpSocket.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CHttpDownLoad
UINT TestSocketLink(void *lParam); //测试Socket是否连接成功
//UINT StartHttpDownLoad(void *lParam);
UINT DownLoad(void *lParam); //下载文件的核心子线程!!!
void CalueFloat(CString &f1,CString &b2,CString ss); //float 123.46=sz=123,sz2=46
void CalueSpareTimer(long &hh,long &mm,long &ss,long lComp,long lbps);
void CalueTimertoSZ(int ihh,int imm,int iss,char sz[]);
void CalueName(CString &f1,CString &b2,CString ss);
UINT DownLoad(void *lParam)
{
//=================Define Main Info============================
LEODOWN_INFO* LeoDownInfo=(LEODOWN_INFO*)lParam;
CWnd *pMainFrame=(CWnd*)LeoDownInfo->pMainFrame;
CHttpDownLoad *pHttpDL=(CHttpDownLoad*)LeoDownInfo->the;
HKEY nhKeyRoot=HKEY_CURRENT_USER;
CLRegistry DownRegistry;
//=============================================================
//=======================Socket Define Type=====================
CString strServer,strObject;
DWORD dwServiceType;
DWORD dwStartTime,dwEndTime,dwTime;
BOOL bIsStartTime=FALSE;
const char *pRequestHeader = NULL;
char *pResponseHeader=NULL;
char *pAcceptType=NULL;
unsigned short nPort;
int nTimeout=pHttpDL->m_stLeoDownInfo.stSocketType.nTimeOut;
long nLength;
long lRevLength=0;
long lSizeLength=1024;
int nSocketLinkNum=1;
long nCompletedSize = LeoDownInfo->lBreakPointByte; //开始的字节数
long nFileSize=LeoDownInfo->lToByte; //结束的字节数
long CompleOffset=nCompletedSize; //nBreakRev=1 "Percent" -Compleoffet
int nBreakRev=LeoDownInfo->nBreakRev;
CString strFileName=LeoDownInfo->strFilePath;
LPCTSTR strLocatea=(LPCTSTR)LeoDownInfo->strLocationUrl;
CString sRegPath=LeoDownInfo->strRegSubPath;
int nPID=LeoDownInfo->nPID;
int nCID=LeoDownInfo->nCID;
pHttpDL->m_stHttpDlInfo.lFileLength=LeoDownInfo->lFileLength;
pHttpDL->m_stHttpDlInfo.strRegPath=sRegPath;
if(nCID==0)
{
pHttpDL->m_stHttpDlInfo.BreakInfo0.nCID=LeoDownInfo->nCID;
pHttpDL->m_stHttpDlInfo.BreakInfo0.lFromeByte=nCompletedSize;
pHttpDL->m_stHttpDlInfo.BreakInfo0.lToByte=nFileSize;
}
else if(nCID==1)
{
pHttpDL->m_stHttpDlInfo.BreakInfo1.nCID=LeoDownInfo->nCID;
pHttpDL->m_stHttpDlInfo.BreakInfo1.lFromeByte=nCompletedSize;
pHttpDL->m_stHttpDlInfo.BreakInfo1.lToByte=nFileSize;
}
else if(nCID==2)
{
pHttpDL->m_stHttpDlInfo.BreakInfo2.nCID=LeoDownInfo->nCID;
pHttpDL->m_stHttpDlInfo.BreakInfo2.lFromeByte=nCompletedSize;
pHttpDL->m_stHttpDlInfo.BreakInfo2.lToByte=nFileSize;
}
else if(nCID==3)
{
pHttpDL->m_stHttpDlInfo.BreakInfo3.nCID=LeoDownInfo->nCID;
pHttpDL->m_stHttpDlInfo.BreakInfo3.lFromeByte=nCompletedSize;
pHttpDL->m_stHttpDlInfo.BreakInfo3.lToByte=nFileSize;
}
else if(nCID==4)
{
pHttpDL->m_stHttpDlInfo.BreakInfo4.nCID=LeoDownInfo->nCID;
pHttpDL->m_stHttpDlInfo.BreakInfo4.lFromeByte=nCompletedSize;
pHttpDL->m_stHttpDlInfo.BreakInfo4.lToByte=nFileSize;
}
//===============================================================
DownRegistry.Open(nhKeyRoot,sRegPath);
CString sTemp,sTemp1;
CFile DownloadFile; //打开在StartHttpDownLoad()中创建的文件
DownloadFile.Open((LPCTSTR)strFileName,CFile::modeWrite|CFile::shareDenyNone);
//while(TRUE)
while(nCompletedSize < nFileSize && nSocketLinkNum<=LeoDownInfo->nSocketLinkNum)
{
nSocketLinkNum++;
AfxParseURL(strLocatea,dwServiceType,strServer,strObject,nPort); //分解网址
//http://www.xxx.xxx/xx/xxx/x.zip
//strServer=www.xxx.xxx,strObject=/xx/xxx/x.zip,nPort=端口
CHttpSocket HttpSocket;
//开始格式化Socket头
pRequestHeader = HttpSocket.FormatRequestHeader((LPTSTR)(LPCTSTR)strServer,(LPTSTR)(LPCTSTR)strObject,nLength,NULL,NULL,nCompletedSize,nFileSize,0,NULL);
HttpSocket.CloseSocket(); //关闭Socket,防止以前打开过
HttpSocket.Socket(); //初始化Socket
HttpSocket.Connect((LPTSTR)(LPCTSTR)strServer,nPort); //连接Socket
HttpSocket.SendRequest(); //向服务器发送请求信息
HttpSocket.SetTimeout(nTimeout,0); //设置连接超时
pResponseHeader=HttpSocket.GetResponseCharPoint(); //返回服务器的回应信息
int nSvrState = HttpSocket.GetServerState(); //获得服务器的状态值
LeoDownInfo->nSvrState=nSvrState;
CString csState;
csState.Format("%d",nSvrState);
csState=csState.Left(1);
if(csState=="2")
{
//连接成功,向列表控件发送服务器回应信息
int nLineSize = 0;
char szLine[256];
while(nLineSize != -1)
{
nLineSize = HttpSocket.GetResponseLine(szLine,256);
if(nLineSize > -1)
{
szLine[nLineSize] = '\0';
::SendMessage(pMainFrame->m_hWnd,WM_TESTLIST,0,(LPARAM)szLine);
}
}
//以下if语句用于判断子线程是否下载完毕(请看StartHttpDownLoad()中的"注意1"后面的语句)
if(nCID==0)
{
pHttpDL->m_stHttpDlInfo.BreakInfo0.bRevData0=TRUE;
}
else if(nCID==1)
{
pHttpDL->m_stHttpDlInfo.BreakInfo1.bRevData1=TRUE;
}
else if(nCID==2)
{
pHttpDL->m_stHttpDlInfo.BreakInfo2.bRevData2=TRUE;
}
else if(nCID==3)
{
pHttpDL->m_stHttpDlInfo.BreakInfo3.bRevData3=TRUE;
}
else if(nCID==4)
{
pHttpDL->m_stHttpDlInfo.BreakInfo4.bRevData4=TRUE;
}
//File Seek to per thread begin byte
char pData[1024]; //用于存放接收数据的字符数组
long nReceSize = 0; //实际接收数据的长度(服务器返回来的值)
if(!bIsStartTime)
{
dwStartTime = GetTickCount(); //1:用于下载速率
bIsStartTime=TRUE; //2:用于下载速率
}
DownloadFile.Seek(LeoDownInfo->lBreakPointByte,CFile::begin); //设置文件指针位置
while(nCompletedSize <= nFileSize)
{
nReceSize = HttpSocket.Receive(pData,1024); //!!!开始从服务器接收数据
dwEndTime = GetTickCount(); //3:用于下载速率
if(nReceSize<=0)
{
HttpSocket.CloseSocket(); //没有可以接收的数据,关闭Socket
break; //return while(TRUE) !!!
}
DownloadFile.Write(pData,nReceSize); //如果接收成功,开始向文件当前位置写入数据
nCompletedSize += nReceSize; //累计已经下载的数据(用于判断是否等于要下载文件的长度,如果等于证明下载结束)
lRevLength+=nReceSize; //4:用于下载速率
LeoDownInfo->lBreakPointByte=nCompletedSize; //!!!保存此子线程已经下载的字节数(用于下次下载的开始位置)
//5:用于下载速率
dwTime=dwEndTime-dwStartTime;
if(dwTime==0)
dwTime=10;
float fSpeed = 0.0f;
fSpeed=(float)lRevLength;
fSpeed = fSpeed/(((float)dwTime)/1000.0f);
LeoDownInfo->fSpeed=fSpeed;
//6:用于下载速率,存入当前的子线程中
if(nCID==0)
{
pHttpDL->m_stHttpDlInfo.BreakInfo0.BreakLength0=LeoDownInfo->lBreakPointByte;
pHttpDL->m_stHttpDlInfo.BreakInfo0.lSpeed0=fSpeed;
}
else if(nCID==1)
{
pHttpDL->m_stHttpDlInfo.BreakInfo1.BreakLength1=LeoDownInfo->lBreakPointByte;
pHttpDL->m_stHttpDlInfo.BreakInfo1.lSpeed1=fSpeed;
}
else if(nCID==2)
{
pHttpDL->m_stHttpDlInfo.BreakInfo2.BreakLength2=LeoDownInfo->lBreakPointByte;
pHttpDL->m_stHttpDlInfo.BreakInfo2.lSpeed2=fSpeed;
}
else if(nCID==3)
{
pHttpDL->m_stHttpDlInfo.BreakInfo3.BreakLength3=LeoDownInfo->lBreakPointByte;
pHttpDL->m_stHttpDlInfo.BreakInfo3.lSpeed3=fSpeed;
}
else if(nCID==4)
{
pHttpDL->m_stHttpDlInfo.BreakInfo4.BreakLength4=LeoDownInfo->lBreakPointByte;
pHttpDL->m_stHttpDlInfo.BreakInfo4.lSpeed4=fSpeed;
}
//写入注册表中的数据 {BreakLength?,ToLength?,bps/s}
//"Software\\BLeo2004\\DownLoad\\Down?";
sTemp="";
sTemp1="";
sTemp.Format("%ld",LeoDownInfo->lBreakPointByte);
sTemp1.Format("BreakLength%d",nCID);
DownRegistry.Write((LPCTSTR)sTemp1,(LPCTSTR)sTemp);
sTemp.Empty();
sTemp1.Empty();
sTemp.Format("%ld",LeoDownInfo->lToByte);
sTemp1.Format("ToLength%d",nCID);
DownRegistry.Write((LPCTSTR)sTemp1,(LPCTSTR)sTemp);
sTemp1.Empty();
sTemp.Empty();
if(nCID==0)
{
sTemp.Format("%.0f",fSpeed);
DownRegistry.Write("bps0",(LPCTSTR)sTemp);
}
} //while
} //if
HttpSocket.CloseSocket();
} //while
DownRegistry.Close();
DownloadFile.Close();
LeoDownInfo->bIsFileDown=FALSE; //设置标志,此线程下载结束
AfxEndThread(0,TRUE);
return 0;
}
UINT TestSocketLink(void *lParam)
{
//此函数中的一些说明在DownLoad()中可以找到
CHttpDownLoad *the=(CHttpDownLoad*)lParam;
CHttpSocket HttpSocket;
CString strServer,strObject;
DWORD dwServiceType;
unsigned short nPort;
long nLength;
const char *pRequestHeader = NULL;
char *pResponseHeader=NULL;
char *pAcceptType=NULL;
int nTimeout=the->m_stLeoDownInfo.stSocketType.nTimeOut;
AfxParseURL((LPCTSTR)(the->m_stLeoDownInfo.strLocationUrl),dwServiceType,strServer,strObject,nPort);
//------如果要下载文件,将文件名存入结构变量中用于自动更名算法------
if(strObject.Mid(strObject.GetLength()-4,1)=="." || strObject.Mid(strObject.GetLength()-3,1)==".")
the->m_stLeoDownInfo.strAutoFilePath=strObject;
the->m_stLeoDownInfo.stSocketType.strServer=strServer; //保存服务器名 www.xxx.xx
pRequestHeader = HttpSocket.FormatRequestHeader((LPTSTR)(LPCTSTR)strServer,(LPTSTR)(LPCTSTR)strObject,nLength,NULL,NULL,the->m_stLeoDownInfo.lFromeByte,the->m_stLeoDownInfo.lToByte,0,NULL);
HttpSocket.Socket();
HttpSocket.Connect((LPTSTR)(LPCTSTR)strServer,nPort);
HttpSocket.SendRequest();
HttpSocket.SetTimeout(nTimeout,0);
pResponseHeader=HttpSocket.GetResponseCharPoint();
//::SendMessage(the->m_pMainFrame->m_hWnd,WM_TESTLIST,0,(LPARAM)pResponseHeader);
int nLineSize = 0;
char szLine[256];
while(nLineSize != -1)
{
nLineSize = HttpSocket.GetResponseLine(szLine,256);
if(nLineSize > -1)
{
szLine[nLineSize] = '\0';
::SendMessage(the->m_pMainFrame->m_hWnd,WM_TESTLIST,0,(LPARAM)szLine);
}
}
int nSvrState = HttpSocket.GetServerState();
the->m_stLeoDownInfo.nSvrState=nSvrState;
CString csState;
csState.Format("%d",nSvrState);
csState=csState.Left(1);
if(csState=="3")
{
//重新定位下载网址
char szValue[30];
HttpSocket.GetField("Location",szValue,30);
the->m_stLeoDownInfo.strLocationUrl.Format("%s",szValue);
AfxParseURL((LPCTSTR)(the->m_stLeoDownInfo.strLocationUrl),dwServiceType,strServer,strObject,nPort);
if(strServer.IsEmpty())
{
CString strLocation;
strLocation="http://";
strLocation+=the->m_stLeoDownInfo.stSocketType.strServer;
strLocation+=the->m_stLeoDownInfo.strLocationUrl;
the->m_stLeoDownInfo.strLocationUrl=strLocation; //!!!新网址存入结构变量中
}
}
else if(csState=="5")
{
}
else if(csState=="2")
{
//成功连接服务器,线程结束
char szLength[30];
HttpSocket.GetField("Content-Length",szLength,30);
long nFileSize = atol(szLength);
the->m_stLeoDownInfo.lFileLength=nFileSize;
}
HttpSocket.CloseSocket();
AfxEndThread(0,TRUE);
return 0;
}
UINT StartHttpDownLoad(void *lParam)
{
//--------------------第一步--------------------
//******定义一个注册表变量******
CLRegistry DownRegistry;
HKEY nhKeyRoot=HKEY_CURRENT_USER;
//******定义一个CHttpDownLoad类的指针******
CHttpDownLoad *the=(CHttpDownLoad*)lParam;
//******向列表框写入数据******
//如果"nBreakRev"为"0",说明是新的下载任务.
if(the->m_stLeoDownInfo.nBreakRev!=1)
{
WRITEREGLISTCTRL_DATA pWriteRegListCtrlData;
pWriteRegListCtrlData.nPID=the->m_stLeoDownInfo.nPID;
pWriteRegListCtrlData.nItem=the->m_stLeoDownInfo.nPID-1; //列表框的行标(从0开始)
pWriteRegListCtrlData.nImage=the->m_stLeoDownInfo.nImage; //图示
pWriteRegListCtrlData.strFilePath=the->m_stLeoDownInfo.strFilePath; //文件下载后存盘的绝对路径
pWriteRegListCtrlData.strLocationUrl=the->m_stLeoDownInfo.strLocationUrl; //文件下载的网址
pWriteRegListCtrlData.strComment=the->m_stLeoDownInfo.strComment; //下载的提示信息
pWriteRegListCtrlData.strRegPath=the->m_stLeoDownInfo.strRegSubPath; //保存的注册表路径
//******向注册表写入下载数据******
//注册表位置"HKEY_CURRENT_USER\Software\BLeo2004\DownLoad\Down?"
//"Down?"中的"?"号为第几个下载任务如:Down1,Down2等.
if(DownRegistry.Open(nhKeyRoot,pWriteRegListCtrlData.strRegPath))
{
DownRegistry.Write("Image",pWriteRegListCtrlData.nImage);
DownRegistry.Write("FileName",pWriteRegListCtrlData.strFilePath);
DownRegistry.Write("Comment",pWriteRegListCtrlData.strComment);
DownRegistry.Write("URL",pWriteRegListCtrlData.strLocationUrl);
DownRegistry.Write("Percent","0%"); //***写入百分比这对以后的断点下载有用!
}
DownRegistry.Close();
//******向CMainFrame发出WM_WRITEREGLISTCTRL消息,此消息用于向列表框写入数据.******
::SendMessage(the->m_pMainFrame->m_hWnd,WM_WRITEREGLISTCTRL,0,(LPARAM)&pWriteRegListCtrlData);
}
//******打开计时器,开始下载计时,the为CHttpDownLoad类的指针.******
//1为第一个计时器,用于计算下载用的全部时间
the->SetTimerBegin(1);
//--------------------第二步--------------------------------------------
//---此段程序向要下载文件的服务器发出查询,查看返回的结果是否正确, ---
//---如正确再下载,否则停止下载任务 ---
//----------------------------------------------------------------------
//******定义一个线程指针******
CWinThread *pWinThread=NULL;
HANDLE hThreadTest=NULL; //线程句柄
DWORD dwSingleObject=0; //WaitForSingleObject返回值
int nSocketLinkNum=1; //Socket连接次数,初始为1,终止为10
while(nSocketLinkNum<=the->m_stLeoDownInfo.nSocketLinkNum)
{
//******如果m_bIsEndThe为真,就将这个线程结束,那么这次的下载任务也将结果.******
//这主要用于强制下载线程退出!
if(the->m_bIsEndThe)
{
//停止计时
the->KillTimerEnd(1);
AfxEndThread(0,TRUE); //结束线程
return 0;
}
//******WaitForSingleObject超时时间******
DWORD lTimeOut=(DWORD)the->m_stLeoDownInfo.stSocketType.nTimeOut;
pWinThread=AfxBeginThread(TestSocketLink,(void*)the); //开始创建测试线程
hThreadTest=pWinThread->m_hThread; //赋值线程句柄
dwSingleObject=WaitForSingleObject(hThreadTest,lTimeOut+500); //等侍线程结束
int nSvrState=the->m_stLeoDownInfo.nSvrState; //返回服务器的状态值
CString cs;
long Length;
cs.Format("%d",the->m_stLeoDownInfo.nSvrState);
cs=cs.Left(1); //取状态值的第一位
Length=the->m_stLeoDownInfo.lFileLength; //返回要下载文件的长度
if(dwSingleObject==WAIT_TIMEOUT) //如果线程超时
{
//强制线程退出
TerminateThread(hThreadTest,0);
if(cs=="2" && Length>0)
break;
if(nSvrState==500)
{
nSocketLinkNum+=12;
break;
}
}
else if(dwSingleObject==WAIT_OBJECT_0) //如果线程正常退出
{
//如果服务器返回是2XX并且下载文件的长度大于0,就表明测试成功,下一步就可以正式下载文件了
if(cs=="2" && Length>0)
break; //跳出While!!!
if(nSvrState==500) //如果服务器返回是500,证明服务器出错,下载任务结束
{
nSocketLinkNum+=12;
break;
}
}
//******如果m_bIsEndThe为真,就将这个线程结束,那么这次的下载任务也将结果.******
//这主要用于强制下载线程退出!
if(the->m_bIsEndThe)
{
the->KillTimerEnd(1);
//CloseHandle(hThreadTest);
//ReleaseMutex(the->m_hEndMutex);
AfxEndThread(0,TRUE);
return 0;
}
the->m_stLeoDownInfo.stSocketType.nTimeOut+=1000;
nSocketLinkNum++;
}
//nSocketLinkNum超过10次下载结束
if(nSocketLinkNum>the->m_stLeoDownInfo.nSocketLinkNum)
{
the->KillTimerEnd(1);
::SendMessage(the->m_pMainFrame->m_hWnd,WM_DECRSETHNUM,the->m_stLeoDownInfo.nPID-1,0);
::MessageBox(NULL,"URL连接错误!","Leo",MB_OK|MB_ICONSTOP);
AfxEndThread(0,TRUE);
return 0;
}
//--------------------第三步--------------------------------------------
//--- 此段程序是在下载文件前做一些准被工作 ---
//----------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -