//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 = 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; iPER_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; }