vc++ 实现多线程断点续传

时间:2022-09-04 18:45:03

结合本人的FTP类,http类,实现多线程断点续传

 

 

 // DownloadMTR.cpp: implementation of the CDownloadMTR class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "downtest.h"
#include "DownloadMTR.h"
#include <io.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

void DownloadNotify ( int nIndex, UINT nNotityType, LPVOID lpNotifyData, LPVOID pDownloadMTR );

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CDownloadMTR::CDownloadMTR()
 : m_nThreadCount ( DEFAULT_THREAD_COUNT )
 , m_pDownloadPub_MTR ( NULL )
 , m_pDownloadPub_Info ( NULL )
 , m_pDownloadCellInfo ( NULL )
 , m_hThread ( NULL )
 , m_bForceDownload ( FALSE )
 , m_nTotalDownloadedSize_ThisTimes ( 0 )
{
 memset ( &m_BaseDownInfo, 0, sizeof(t_BaseDownInfo) );
 m_hEvtEndModule = ::CreateEvent ( NULL, TRUE, FALSE, NULL );
 m_dwDownloadStartTime = GetTickCount();
}

CDownloadMTR::~CDownloadMTR()
{
 StopDownload ();
}

//
// 设置下载的线程数
//
BOOL CDownloadMTR::SetThreadCount(int nThreadCount)
{
 if ( nThreadCount <= 0 || nThreadCount > MAX_DOWNLOAD_THREAD_COUNT )
 {
  Log ( L_WARNING, "Thread count %d is invalid. Rang [%d-%d]", nThreadCount, 1, MAX_DOWNLOAD_THREAD_COUNT );
  return FALSE;
 }
 if ( nThreadCount == m_nThreadCount )
  return TRUE;

 m_nThreadCount = nThreadCount;
 return TRUE;
}

//
// 下载任务的线程函数
//
DWORD WINAPI ThreadProc_DownloadMTR(
  LPVOID lpParameter   // thread data
)
{
 CDownloadMTR *pDownloadMTR = (CDownloadMTR*)lpParameter;
 ASSERT ( pDownloadMTR );
 pDownloadMTR->ThreadProc_DownloadMTR ();
 TRACE ( "下载任务的线程函数 执行完毕/n" );
 return TRUE;
}

BOOL CDownloadMTR::ThreadProc_DownloadMTR()
{
 // 启动多线程下载任务
 int nRet = StartMTRDownload ();
 if ( nRet == 2 ) return HandleDownloadFinished(ENUM_DOWNLOAD_RESULT_SUCCESS);
 if ( nRet == 0 ) return HandleDownloadFinished(ENUM_DOWNLOAD_RESULT_FAILED);

 // 等待所有线程下载完成
 ENUM_DOWNLOAD_RESULT eDownloadResult = WaitForDownloadFinished ();
 if ( eDownloadResult == ENUM_DOWNLOAD_RESULT_SUCCESS && !GetDownloadResult () )
 {
  eDownloadResult = ENUM_DOWNLOAD_RESULT_FAILED;
 }

 return HandleDownloadFinished ( eDownloadResult );
}

//
// 多线程断点续传下载一个文件
//
BOOL CDownloadMTR::Download (
  LPCTSTR lpszDownloadURL,
  LPCTSTR lpszSavePath,
  LPCTSTR lpszSaveOnlyFileName,
  LPCTSTR lpszUsername/*=NULL*/,
  LPCTSTR lpszPassword/*=NULL*/,
  BOOL bForceDownload/*=FALSE*/  // 如果为 TRUE 表示强制性重新下载,以下载的部分将会被删除,FALSE 表示断点续传
 )
{
 if ( !HANDLE_IS_VALID(m_hEvtEndModule) )
  return FALSE;
 if ( !lpszSavePath || strlen(lpszSavePath) < 1 )
  return FALSE;
 m_csSavePath = lpszSavePath;
 m_csSaveOnlyFileName = GET_SAFE_STRING(lpszSaveOnlyFileName);
 m_bForceDownload = bForceDownload;

 CString csServer, csObject;
 USHORT nPort = 0;
 if ( !ParseURL ( lpszDownloadURL, csServer, csObject, nPort, m_csProtocolType ) )
 {
  Log ( L_ERROR, "Download URL [%s] invalid", lpszDownloadURL );
  return FALSE;
 }
 m_csDownloadURL = lpszDownloadURL;

 // 创建取站点信息对象
 if ( !( m_pDownloadPub_Info = CreateDownloadObject () ) )
 {
  Log ( L_ERROR, "Create download object failed" );
  return HandleDownloadFinished(ENUM_DOWNLOAD_RESULT_FAILED);
 }
 // 设置取站点信息对象的参数
 m_pDownloadPub_Info->SetAuthorization ( lpszUsername, lpszPassword );
 m_pDownloadPub_Info->m_pDownloadMTR = this;
 m_pDownloadPub_Info->SetDownloadUrl ( lpszDownloadURL );

 // 创建一个下载线程
 DWORD dwThreadId = 0;
 m_hThread = CreateThread ( NULL, 0, ::ThreadProc_DownloadMTR, LPVOID(this), 0, &dwThreadId );
 if ( !HANDLE_IS_VALID(m_hThread) )
 {
  Log ( L_WARNING, "Create download thread failed" );
  return FALSE;
 }

 return TRUE;
}

//
// 创建下载对象
//
CDownloadPub* CDownloadMTR::CreateDownloadObject ( int nCount/*=1*/ )
{
 if ( nCount < 1 ) return NULL;
 CDownloadPub *pDownloadPub = NULL;
 if ( m_csProtocolType.CompareNoCase ( "http" ) == 0 )
 {
  pDownloadPub = (CDownloadPub*)new CDownloadHttp[nCount];
 }
 else if ( m_csProtocolType.CompareNoCase ( "ftp" ) == 0 )
 {
  pDownloadPub = (CDownloadPub*)new CDownloadFtp[nCount];
 }
 else return NULL;

 return pDownloadPub;
}

//
// 删除下载对象
//
void CDownloadMTR::DeleteDownloadObject ( CDownloadPub *pDownloadPub )
{
 if ( m_csProtocolType.CompareNoCase ( "http" ) == 0 )
 {
  delete[] ( (CDownloadHttp*)pDownloadPub );
 }
 else if ( m_csProtocolType.CompareNoCase ( "ftp" ) == 0 )
 {
  delete[] ( (CDownloadFtp*)pDownloadPub );
 }
 else delete[] pDownloadPub;
}

void Callback_SaveDownloadInfo ( int nIndex, int nDownloadedSize, int nSimpleSaveSize, WPARAM wParam )
{
 CDownloadMTR *pDownloadMTR = (CDownloadMTR*)wParam;
 ASSERT ( pDownloadMTR );
 pDownloadMTR->Callback_SaveDownloadInfo ( nIndex, nDownloadedSize, nSimpleSaveSize );
}

void CDownloadMTR::Callback_SaveDownloadInfo ( int nIndex, int nDownloadedSize, int nSimpleSaveSize )
{
 if ( nIndex >= 0 && nIndex < m_nThreadCount )
 {
  m_pDownloadCellInfo[nIndex].nDownloadedSize = nDownloadedSize;
  if ( nDownloadedSize > 0 )
  {
   m_CSFor_DownloadedData.Lock();
   m_nTotalDownloadedSize_ThisTimes += nSimpleSaveSize;
   m_CSFor_DownloadedData.Unlock ();
  }
 }
}

//
// 创建多线程下载使用的对象和数据缓冲
//
BOOL CDownloadMTR::CreateDownloadObjectAndDataMTR ()
{
 DeleteDownloadObjectAndDataMTR ();

 ASSERT ( !m_pDownloadPub_MTR && m_pDownloadPub_Info );
 m_pDownloadPub_MTR = CreateDownloadObject ( m_nThreadCount );
 // 设置多线程下载使用的对象的参数
 if ( m_pDownloadPub_MTR )
 {
  for ( int nIndex=0; nIndex<m_nThreadCount; nIndex++ )
  {
   m_pDownloadPub_MTR[nIndex].m_nIndex = nIndex;
   m_pDownloadPub_MTR[nIndex].m_pDownloadMTR = this;
   m_pDownloadPub_MTR[nIndex].Set_SaveDownloadInfo_Callback ( ::Callback_SaveDownloadInfo, WPARAM(this) );
   m_pDownloadPub_MTR[nIndex].SetAuthorization ( m_pDownloadPub_Info->Get_UserName(), m_pDownloadPub_Info->Get_GetPassword() );
   m_pDownloadPub_MTR[nIndex].SetDownloadUrl ( m_csDownloadURL );
   if ( !m_pDownloadPub_MTR[nIndex].SetSaveFileName ( GetTempFilePath() ) )
    return FALSE;
  }
 }

 // 创建多线程下载使用的数据缓冲
 ASSERT ( !m_pDownloadCellInfo );
 m_pDownloadCellInfo = new t_DownloadCellInfo[m_nThreadCount];
 if ( m_pDownloadCellInfo )
  memset ( m_pDownloadCellInfo, 0, m_nThreadCount*sizeof(t_DownloadCellInfo) );

 if ( m_pDownloadPub_MTR != NULL && m_pDownloadCellInfo != NULL )
  return TRUE;
 Log ( L_WARNING, "Create MTR download object or buffer failed" );
 return FALSE;

}

//
// 删除多线程下载使用的对象和数据缓冲
//
void CDownloadMTR::DeleteDownloadObjectAndDataMTR()
{
 if ( m_pDownloadPub_MTR )
 {
  DeleteDownloadObject ( m_pDownloadPub_MTR );
  m_pDownloadPub_MTR = NULL;
 }
 if ( m_pDownloadCellInfo )
 {
  delete[] m_pDownloadCellInfo;
  m_pDownloadCellInfo = NULL;
 }
}

//
// 删除取站点信息的下载对象
//
void CDownloadMTR::DeleteDownloadObject_Info()
{
 if ( m_pDownloadPub_Info )
 {
  DeleteDownloadObject ( m_pDownloadPub_Info );
  m_pDownloadPub_Info = NULL;
 }
}

//
// 启动多线程下载,返回 0 表示失败,1表示成功,2表示不用下载了,因为该文件已经下载过了
//
int CDownloadMTR::StartMTRDownload ()
{
 m_dwDownloadStartTime = GetTickCount();
 DownloadNotify ( -1, NOTIFY_TYPE_START_DOWNLOAD, (LPVOID)NULL, this );
 // 先获取站点信息
 ASSERT ( m_pDownloadPub_Info );
 if ( !m_pDownloadPub_Info->GetRemoteSiteInfo () )
  return 0;
 DbgLog ( "要下载的文件大小是: %d 字节/n", m_pDownloadPub_Info->Get_FileTotalSize () );
 StandardSaveFileName ();
 CFileStatus fileStatus;

 if ( m_bForceDownload )
 {
  // 需要重新下载
  ::DeleteFile ( m_csSavePathFileName );
  ::DeleteFile ( GetTempFilePath() );
 }
 else
 {
  // 要保存的文件是否已经存在,且大小和创建时间一致,如果不是强制性下载,则不需要再下载了。
  if ( CFile::GetStatus(m_csSavePathFileName,fileStatus) )
  {
   if (
    (
    fileStatus.m_mtime.GetTime() - m_pDownloadPub_Info->Get_TimeLastModified() <=2 &&
    m_pDownloadPub_Info->Get_TimeLastModified()-fileStatus.m_mtime.GetTime() <=2
    )
    &&
    fileStatus.m_size == m_pDownloadPub_Info->Get_FileTotalSize ()
    &&
    !m_bForceDownload
    )
   {
    return 2;
   }
  }
 }

 BOOL bMustCreateNullFile = TRUE;
 // 读取下载信息,如果能读到说明上次下载尚未完成
 if ( !m_bForceDownload && m_pDownloadPub_Info->Is_SupportResume() )
 {
  if ( CFile::GetStatus(GetTempFilePath(),fileStatus) &&
   fileStatus.m_size == m_pDownloadPub_Info->Get_FileTotalSize()+GetDownloadInfoWholeSize() )
  {
   if ( ReadDownloadInfo () )
    bMustCreateNullFile = FALSE;
  }
 }
 
 if ( bMustCreateNullFile )
 {
  int nFileSize = m_pDownloadPub_Info->Get_FileTotalSize();
  int nTempFileSize = nFileSize+GetDownloadInfoWholeSize();
  if ( nFileSize < 0 || !m_pDownloadPub_Info->Is_SupportResume() )
   nTempFileSize = 0;
  // 创建一个用来保存下载数据的空文件
  if ( !CreateNullFile ( GetTempFilePath(), nTempFileSize ) )
   return FALSE;
 }

 // 分配下载任务
 if ( !AssignDownloadTask () )
 {
  Log ( L_WARNING, "Assign task failed" );
  return 0;
 }

 m_dwDownloadStartTime = GetTickCount();
 return 1;
}

//
// 得到临时数据保存的路径文件名
//
CString CDownloadMTR::GetTempFilePath ()
{
 ASSERT ( !m_csSavePathFileName.IsEmpty () );
 CString csTempFileName;
 csTempFileName.Format ( "%s.~xhw~", m_csSavePathFileName );
 ::SetFileAttributes ( csTempFileName, FILE_ATTRIBUTE_HIDDEN );
 return csTempFileName;
}

//
// 分配下载任务
//
BOOL CDownloadMTR::AssignDownloadTask()
{
 ASSERT ( m_pDownloadPub_Info );
 if ( !m_pDownloadPub_Info->Is_SupportResume() )
 {
  DeleteDownloadObjectAndDataMTR ();
  Log ( L_WARNING, "Site [%s] not support resume download", m_pDownloadPub_Info->Get_ServerName() );
 }
 // 文件大小未知,采用单线程
 if ( m_pDownloadPub_Info->Get_FileTotalSize () <= 0 || !m_pDownloadPub_Info->Is_SupportResume() )
 {
  if ( m_nThreadCount != 1 )
  {
   DeleteDownloadObjectAndDataMTR ();
   SetThreadCount ( 1 );
  }
 }

 if ( !DownloadInfoIsValid() || !m_pDownloadPub_MTR || !m_pDownloadCellInfo )
 {
  if ( !CreateDownloadObjectAndDataMTR () )
   return FALSE;
 }

 ASSERT ( m_pDownloadPub_MTR && m_pDownloadCellInfo );

 // 下载任务尚未分配
 if ( !DownloadInfoIsValid() )
 {
  int nWillDownloadSize = -1, nWillDownloadStartPos = 0, nNoAssignSize = 0;
  if ( m_pDownloadPub_Info->Get_FileTotalSize () > 0 )
  {
   nWillDownloadSize = m_pDownloadPub_Info->Get_FileTotalSize () / m_nThreadCount;
   // 均分后剩下的部分,让第一个线程来承担下载
   nNoAssignSize = m_pDownloadPub_Info->Get_FileTotalSize () % m_nThreadCount;
  }

  DbgLog ( "任务分配如下:--------------------/n" );
  for ( int nIndex=0; nIndex<m_nThreadCount; nIndex++ )
  {
   m_pDownloadCellInfo[nIndex].nWillDownloadStartPos = nWillDownloadStartPos;
   m_pDownloadCellInfo[nIndex].nWillDownloadSize = nWillDownloadSize;
   if ( nIndex == 0 && m_pDownloadPub_Info->Get_FileTotalSize () > 0 )
   {
    m_pDownloadCellInfo[nIndex].nWillDownloadSize += nNoAssignSize;
   }

   DbgLog ( "线程.%d 从 %d(0x%08x) 下载到 %d(0x%08x) 共 %d(0x%08x) 字节/n", nIndex,
    m_pDownloadCellInfo[nIndex].nWillDownloadStartPos, m_pDownloadCellInfo[nIndex].nWillDownloadStartPos,
    m_pDownloadCellInfo[nIndex].nWillDownloadStartPos+m_pDownloadCellInfo[nIndex].nWillDownloadSize,
    m_pDownloadCellInfo[nIndex].nWillDownloadStartPos+m_pDownloadCellInfo[nIndex].nWillDownloadSize,
    m_pDownloadCellInfo[nIndex].nWillDownloadSize, m_pDownloadCellInfo[nIndex].nWillDownloadSize );

   nWillDownloadStartPos += m_pDownloadCellInfo[nIndex].nWillDownloadSize;
  }
 }


 // 启动下载任务
 for ( int nIndex=0; nIndex<m_nThreadCount; nIndex++ )
 {
  if ( !m_pDownloadPub_MTR[nIndex].Download ( m_pDownloadCellInfo[nIndex].nWillDownloadStartPos,
   m_pDownloadCellInfo[nIndex].nWillDownloadSize, m_pDownloadCellInfo[nIndex].nDownloadedSize ) )
   return FALSE;
 }

 m_BaseDownInfo.dwThreadCount = m_nThreadCount;
 return TRUE;
}

//
// 从下载信息文件中读取下载信息
//
BOOL CDownloadMTR::ReadDownloadInfo()
{
 CString csTempFileName = GetTempFilePath ();
 BOOL bRet = FALSE;
 CFile file;
 TRY
 {
  if ( file.Open ( csTempFileName, CFile::modeCreate|CFile::modeNoTruncate|CFile::modeReadWrite|CFile::typeBinary|CFile::shareDenyNone ) )
  {
   if ( file.Seek ( -(int)sizeof(t_BaseDownInfo), CFile::end ) == (int)(file.GetLength() - sizeof(t_BaseDownInfo)) )
   {
    if ( (UINT)file.Read ( &m_BaseDownInfo, sizeof(t_BaseDownInfo) ) == sizeof(t_BaseDownInfo) )
    {
     if ( (m_BaseDownInfo.dwThreadCount > 0 && m_BaseDownInfo.dwThreadCount <= MAX_DOWNLOAD_THREAD_COUNT)&&
      SetThreadCount ( m_BaseDownInfo.dwThreadCount ) )
     {
      if ( CreateDownloadObjectAndDataMTR () )
      {
       if ( file.Seek ( -GetDownloadInfoWholeSize(), CFile::end ) == int(file.GetLength() - GetDownloadInfoWholeSize()) )
       {
        if ( file.Read ( m_pDownloadCellInfo, sizeof(t_DownloadCellInfo)*m_nThreadCount ) == sizeof(t_DownloadCellInfo)*m_nThreadCount )
        {
         bRet = TRUE;
        }
        else
        {
         memset ( m_pDownloadCellInfo, 0, sizeof(t_DownloadCellInfo)*m_nThreadCount );
        }
       }
      }
     }
    }
   }
  }
 }
 CATCH( CFileException, e )
 {
  e->Delete ();
  bRet = FALSE;
 }
 END_CATCH

 if ( HANDLE_IS_VALID(file.m_hFile) )
  file.Close ();

 return bRet;
}

BOOL CDownloadMTR::SaveDownloadInfo ()
{
 if ( !m_pDownloadPub_Info->Is_SupportResume() )
  return TRUE;
 CString csTempFileName = GetTempFilePath ();
 BOOL bRet = FALSE;
 CFile file;
 TRY
 {
  if ( file.Open ( csTempFileName, CFile::modeCreate|CFile::modeNoTruncate|CFile::modeReadWrite|CFile::typeBinary|CFile::shareDenyNone ) )
  {
   if ( file.Seek ( -(int)sizeof(t_BaseDownInfo), CFile::end ) == (int)(file.GetLength() - sizeof(t_BaseDownInfo)) )
   {
    file.Write ( &m_BaseDownInfo, sizeof(t_BaseDownInfo) );
    if ( file.Seek ( -GetDownloadInfoWholeSize(), CFile::end ) == int(file.GetLength() - GetDownloadInfoWholeSize()) )
    {
     file.Write ( m_pDownloadCellInfo, m_nThreadCount*sizeof(t_DownloadCellInfo) );
     bRet = TRUE;
    }
   }
  }
 }
 CATCH( CFileException, e )
 {
  e->Delete ();
  bRet = FALSE;
 }
 END_CATCH
 if ( HANDLE_IS_VALID ( file.m_hFile ) )
  file.Close ();

 if ( !bRet ) Log ( L_WARNING, "Save download info failed. %s", hwFormatMessage ( GetLastError() ) );

 return bRet;

}

BOOL CDownloadMTR::HandleDownloadFinished(ENUM_DOWNLOAD_RESULT eDownloadResult)
{
 CString csTempFileName;
 CFileStatus fileStatus;
 BOOL bRet = FALSE;
 CFile file;

 if ( eDownloadResult != ENUM_DOWNLOAD_RESULT_SUCCESS )
 {
  SaveDownloadInfo ();
  goto Finished;
 }
 csTempFileName = GetTempFilePath ();

 // 设置文件大小
 if ( m_pDownloadPub_Info->Is_SupportResume() && m_pDownloadPub_Info->Get_FileTotalSize() > 0 )
 {
  TRY
  {
   file.Open ( csTempFileName, CFile::modeCreate|CFile::modeNoTruncate|CFile::modeReadWrite|CFile::typeBinary|CFile::shareDenyNone );
   file.SetLength(m_pDownloadPub_Info->Get_FileTotalSize ());
   bRet = TRUE;
  }
  CATCH( CFileException, e )
  {
   e->Delete ();
   bRet = FALSE;
  }
  END_CATCH
  if ( HANDLE_IS_VALID(file.m_hFile) )
   file.Close ();
  if ( !bRet )
  {
   Log ( L_WARNING, "Set [%s] length failed", csTempFileName );
   eDownloadResult = ENUM_DOWNLOAD_RESULT_FAILED;
   goto Finished;
  }
 }

 if ( _access(csTempFileName,04) == 0 )
 {
  // 将文件改名
  bRet = FALSE;
  DeleteFile ( m_csSavePathFileName );
  TRY
  {
   CFile::Rename ( csTempFileName, m_csSavePathFileName );
   bRet = TRUE;
  }
  CATCH( CFileException, e )
  {
   e->Delete ();
   bRet = FALSE;
  }
  END_CATCH
   
  if ( !bRet )
  {
   Log ( L_WARNING, "Rename [%s] failed. %s", csTempFileName, hwFormatMessage(GetLastError()) );
   eDownloadResult = ENUM_DOWNLOAD_RESULT_FAILED;
   goto Finished;
  }

  // 设置文件属性,时间设置和服务器一致
  bRet = FALSE;
  if ( CFile::GetStatus(m_csSavePathFileName,fileStatus) )
  {
   fileStatus.m_mtime = m_pDownloadPub_Info->Get_TimeLastModified();
   fileStatus.m_attribute = CFile::normal;
   CFile::SetStatus ( m_csSavePathFileName, fileStatus );
   bRet = TRUE;
  }
  if ( !bRet )
  {
   Log ( L_WARNING, "Set file [%s] status failed. %s", csTempFileName, hwFormatMessage(GetLastError()) );
   eDownloadResult = ENUM_DOWNLOAD_RESULT_FAILED;
   goto Finished;
  }
 }

Finished:
 DownloadNotify ( -1, NOTIFY_TYPE_END_DOWNLOAD, (LPVOID)eDownloadResult, this );
 return bRet;
}

BOOL CDownloadMTR::GetDownloadResult()
{
 for ( int nIndex=0; nIndex<m_nThreadCount; nIndex++ )
 {
  if ( !m_pDownloadPub_MTR[nIndex].Is_DownloadSuccess() )
   return FALSE;
 }

 return TRUE;
}

//
// 下载信息是否有效
//
BOOL CDownloadMTR::DownloadInfoIsValid()
{
 BOOL bValid = FALSE;
 int nIndex = 0;
 if ( !m_pDownloadCellInfo ) goto Invalid;
 if ( m_BaseDownInfo.dwThreadCount < 1 || m_BaseDownInfo.dwThreadCount > MAX_DOWNLOAD_THREAD_COUNT )
  goto Invalid;
 
 for ( nIndex=0; nIndex<m_nThreadCount; nIndex++ )
 {
  if ( m_pDownloadCellInfo[nIndex].nWillDownloadSize > 0 )
  {
   bValid = TRUE;
   break;
  }
 }
 if ( !bValid ) goto Invalid;

 return TRUE;

Invalid:
 if ( m_pDownloadCellInfo )
  memset ( m_pDownloadCellInfo, 0, m_nThreadCount*sizeof(t_DownloadCellInfo) );
 memset ( &m_BaseDownInfo, 0, sizeof(t_BaseDownInfo) );
 return FALSE;
}

//
// 找到剩余未下载的数量最大的那个对象编号
//
int CDownloadMTR::GetUndownloadMaxBytes( int &nUndownloadBytes )
{
 nUndownloadBytes = 0;
 int nMaxIndex = -1;
 for ( int nIndex=0; nIndex<m_nThreadCount; nIndex++ )
 {
  int nTempBytes = m_pDownloadPub_MTR[nIndex].GetUndownloadBytes ();
  if ( nUndownloadBytes < nTempBytes )
  {
   nUndownloadBytes = nTempBytes;
   nMaxIndex = nIndex;
  }
 }
 return nMaxIndex;
}

//
// 编号为 nIndex 的对象调度任务,为下载任务最繁重的对象减轻负担
//
BOOL CDownloadMTR::AttemperDownloadTask(int nIndex)
{
 ASSERT ( m_pDownloadPub_MTR && m_pDownloadCellInfo );
 if ( m_nThreadCount <= 1 || m_pDownloadCellInfo[nIndex].nWillDownloadSize == -1 )
  return FALSE;
 int nUndownloadBytes = 0;
 int nIndex_Heavy = GetUndownloadMaxBytes ( nUndownloadBytes );
 if ( nIndex_Heavy == -1 || nIndex_Heavy == nIndex )
  return FALSE;
 if ( m_pDownloadPub_MTR[nIndex_Heavy].ThreadIsRunning() && nUndownloadBytes < 100*1024 )
  return FALSE;
 ASSERT ( nIndex_Heavy >= 0 && nIndex_Heavy < m_nThreadCount );
 ASSERT ( m_pDownloadPub_MTR[nIndex_Heavy].Get_WillDownloadStartPos() == m_pDownloadCellInfo[nIndex_Heavy].nWillDownloadStartPos );

 DbgLog ( "下载对象.%d 帮 %d (%s) 减轻负担/n", nIndex, nIndex_Heavy, m_pDownloadPub_MTR[nIndex_Heavy].ThreadIsRunning()?"运行":"停止" );
 // 给空闲下载对象分配新任务
 m_pDownloadCellInfo[nIndex].nWillDownloadSize = ( m_pDownloadPub_MTR[nIndex_Heavy].ThreadIsRunning()?(nUndownloadBytes/2) : nUndownloadBytes );
 m_pDownloadCellInfo[nIndex].nWillDownloadStartPos = m_pDownloadPub_MTR[nIndex_Heavy].Get_WillDownloadStartPos() +
  m_pDownloadPub_MTR[nIndex_Heavy].Get_WillDownloadSize() - m_pDownloadCellInfo[nIndex].nWillDownloadSize;
 m_pDownloadCellInfo[nIndex].nDownloadedSize = 0;
 DbgLog ( "空闲下载对象.%d 分配新任务: %d(0x%08x) - %d(0x%08x) 共 %d(0x%08x)/n", nIndex, m_pDownloadCellInfo[nIndex].nWillDownloadStartPos, m_pDownloadCellInfo[nIndex].nWillDownloadStartPos,
  m_pDownloadCellInfo[nIndex].nWillDownloadStartPos + m_pDownloadCellInfo[nIndex].nWillDownloadSize, m_pDownloadCellInfo[nIndex].nWillDownloadStartPos + m_pDownloadCellInfo[nIndex].nWillDownloadSize,
  m_pDownloadCellInfo[nIndex].nWillDownloadSize, m_pDownloadCellInfo[nIndex].nWillDownloadSize );

 // 启动空闲下载对象的下载任务
 if ( m_pDownloadCellInfo[nIndex].nWillDownloadSize == 0 )
  return FALSE;
 m_pDownloadPub_MTR[nIndex].ResetVar ();
 if ( !m_pDownloadPub_MTR[nIndex].Download ( m_pDownloadCellInfo[nIndex].nWillDownloadStartPos,
  m_pDownloadCellInfo[nIndex].nWillDownloadSize, m_pDownloadCellInfo[nIndex].nDownloadedSize ) )
  return FALSE;

 // 减轻繁忙下载对象的任务
 m_pDownloadCellInfo[nIndex_Heavy].nWillDownloadSize -= m_pDownloadCellInfo[nIndex].nWillDownloadSize;
 m_pDownloadPub_MTR[nIndex_Heavy].Set_WillDownloadSize ( m_pDownloadCellInfo[nIndex_Heavy].nWillDownloadSize );
 DbgLog ( "繁忙下载对象.%d 下载了 %d(0x%08x) 未完 %d(0x%08x) 字节,调整任务为: %d(0x%08x) - %d(0x%08x) 共 %d(0x%08x)/n",
  nIndex_Heavy, m_pDownloadPub_MTR[nIndex_Heavy].Get_DownloadedSize(), m_pDownloadPub_MTR[nIndex_Heavy].Get_DownloadedSize(),
  nUndownloadBytes, nUndownloadBytes,
  m_pDownloadCellInfo[nIndex_Heavy].nWillDownloadStartPos, m_pDownloadCellInfo[nIndex_Heavy].nWillDownloadStartPos,
  m_pDownloadCellInfo[nIndex_Heavy].nWillDownloadStartPos + m_pDownloadCellInfo[nIndex_Heavy].nWillDownloadSize, m_pDownloadCellInfo[nIndex_Heavy].nWillDownloadStartPos + m_pDownloadCellInfo[nIndex_Heavy].nWillDownloadSize,
  m_pDownloadCellInfo[nIndex_Heavy].nWillDownloadSize, m_pDownloadCellInfo[nIndex_Heavy].nWillDownloadSize );

 return TRUE;
}

//
// 等待下载结束
//
ENUM_DOWNLOAD_RESULT CDownloadMTR::WaitForDownloadFinished()
{
 ASSERT ( HANDLE_IS_VALID(m_hEvtEndModule) );
 int nCount = m_nThreadCount + 1;
 ENUM_DOWNLOAD_RESULT eDownloadResult = ENUM_DOWNLOAD_RESULT_FAILED;
 HANDLE *lpHandles = new HANDLE[nCount];
 if ( !lpHandles ) goto End;
 while ( TRUE )
 {
  nCount = 0;
  for ( int nIndex=0; nIndex<m_nThreadCount; nIndex++ )
  {
   HANDLE hThread = m_pDownloadPub_MTR[nIndex].Get_Thread_Handle ();
   if ( HANDLE_IS_VALID(hThread) )
    lpHandles[nCount++] = hThread;
  }
  lpHandles[nCount++] = m_hEvtEndModule;

  if ( nCount == 1 )
  {
   if ( Get_TotalDownloadedSize() >= m_pDownloadPub_Info->Get_FileTotalSize() )
   {
    ASSERT ( Get_TotalDownloadedSize() == m_pDownloadPub_Info->Get_FileTotalSize() );
    eDownloadResult = ENUM_DOWNLOAD_RESULT_SUCCESS;
   }
   else
    eDownloadResult = ENUM_DOWNLOAD_RESULT_FAILED;
   goto End;
  }
  int nRet = (int)WaitForMultipleObjects ( nCount, lpHandles, FALSE, INFINITE ) - WAIT_OBJECT_0;
  // 某下载对象完成任务了
  if ( nRet >= 0 && nRet < nCount-1 )
  {
   nIndex = FindIndexByThreadHandle ( lpHandles[nRet] );
   if ( ( nIndex >= 0 && nIndex < m_nThreadCount ) )
   {
    if ( !m_pDownloadPub_MTR[nIndex].Is_DownloadSuccess() ||
     !AttemperDownloadTask ( nIndex ) )
    {
     m_pDownloadPub_MTR[nIndex].Clear_Thread_Handle ();
    }
   }
   else
   {
    eDownloadResult = ENUM_DOWNLOAD_RESULT_CANCEL;
    goto End;
   }
  }
  // 模块结束  
  else
  {
   eDownloadResult = ENUM_DOWNLOAD_RESULT_CANCEL;
   goto End;
  }
 }

End:
 // 等待所有下载线程结束
 if ( eDownloadResult != ENUM_DOWNLOAD_RESULT_SUCCESS )
 {
  nCount = 0;
  for ( int nIndex=0; nIndex<m_nThreadCount; nIndex++ )
  {
   HANDLE hThread = m_pDownloadPub_MTR[nIndex].Get_Thread_Handle ();
   if ( HANDLE_IS_VALID(hThread) )
    lpHandles[nCount++] = hThread;
  }
  WaitForMultipleObjects ( nCount, lpHandles, TRUE, 500*1000 );
 }
 if ( lpHandles ) delete[] lpHandles;
 return eDownloadResult;
}

int CDownloadMTR::FindIndexByThreadHandle(HANDLE hThread)
{
 for ( int nIndex=0; nIndex<m_nThreadCount; nIndex++ )
 {
  HANDLE hThread_Temp = m_pDownloadPub_MTR[nIndex].Get_Thread_Handle ();
  if ( HANDLE_IS_VALID(hThread_Temp) && hThread_Temp == hThread )
   return nIndex;
 }
 return -1;
}

int CDownloadMTR::GetDownloadInfoWholeSize()
{
 return ( sizeof(t_DownloadCellInfo)*m_nThreadCount + sizeof(t_BaseDownInfo) );
}

//
// 获取下载所消耗的时间(毫秒),可用来计算下载速度和推算剩余时间
//
DWORD CDownloadMTR::GetDownloadElapsedTime()
{
 return (GetTickCount() - m_dwDownloadStartTime);
}

//
// 停止下载。将所有下载线程关闭,将下载对象删除,文件关闭
//
void CDownloadMTR::StopDownload()
{
 if ( HANDLE_IS_VALID(m_hEvtEndModule) )
 {
  ::SetEvent ( m_hEvtEndModule );
 }

 // 设置多线程下载使用的对象的参数
 if ( m_pDownloadPub_MTR )
 {
  for ( int nIndex=0; nIndex<m_nThreadCount; nIndex++ )
  {
   m_pDownloadPub_MTR[nIndex].StopDownload ();
  }
 }
 if ( m_pDownloadPub_Info )
 {
  m_pDownloadPub_Info->StopDownload ();
 }

 if ( HANDLE_IS_VALID(m_hThread) )
 {
  WaitForThreadEnd ( m_hThread,100*1000 );
  CLOSE_HANDLE ( m_hThread )
 }

 DeleteDownloadObjectAndDataMTR ();
 DeleteDownloadObject_Info ();

 CLOSE_HANDLE ( m_hEvtEndModule );
}

void CDownloadMTR::StandardSaveFileName ()
{
 ASSERT ( m_csSavePath.GetLength() > 0 );
 StandardizationPathBuffer ( m_csSavePath.GetBuffer(MAX_PATH), MAX_PATH );
 m_csSavePath.ReleaseBuffer ();
 MakeSureDirectory ( m_csSavePath );

 char szOnlyFileName_NoExt_User[MAX_PATH] = {0};
 char szExtensionName_User[MAX_PATH] = {0};
 // 如果用户指定了新的保存文件名,就用新的。
 if ( m_csSaveOnlyFileName.GetLength() > 0 )
 {
  CString csFileNameByURL = GetLocalFileNameByURL ( m_csDownloadURL );
  if ( csFileNameByURL.CompareNoCase(m_csSaveOnlyFileName) != 0 )
  {
   PartFileAndExtensionName ( m_csSaveOnlyFileName, szOnlyFileName_NoExt_User, MAX_PATH, szExtensionName_User, MAX_PATH );
  }
 }

 CString csExtensionName_Remote;
 CString csFileName_Remote = m_pDownloadPub_Info->GetDownloadObjectFileName ( &csExtensionName_Remote );

 if ( strlen(szOnlyFileName_NoExt_User) > 0 )
 {
  if ( strlen(szExtensionName_User) < 1 )
   STRNCPY_CS ( szExtensionName_User, csExtensionName_Remote );
  m_csSavePathFileName.Format ( "%s%s.%s", StandardizationFileForPathName(m_csSavePath,FALSE),
   StandardizationFileForPathName(szOnlyFileName_NoExt_User,TRUE), StandardizationFileForPathName(szExtensionName_User,TRUE) );
 }
 else
 {
  m_csSavePathFileName.Format ( "%s%s", StandardizationFileForPathName(m_csSavePath,FALSE), StandardizationFileForPathName(csFileName_Remote,TRUE) );
 }
}

//
// 根据 URL 来获取本地保存的文件名
//
CString CDownloadMTR::GetLocalFileNameByURL ( LPCTSTR lpszDownloadURL )
{
 if ( !lpszDownloadURL || strlen(lpszDownloadURL) < 1 )
  return "";
 char szOnlyPath[MAX_PATH] = {0};
 char szOnlyFileName[MAX_PATH] = {0};
 if ( !PartFileAndPathByFullPath ( lpszDownloadURL, szOnlyFileName, MAX_PATH, szOnlyPath, MAX_PATH ) )
  return "";
 return szOnlyFileName;
}

//
// 获取文件大小
//
int CDownloadMTR::Get_FileTotaleSize()
{
 if ( !m_pDownloadPub_Info ) return -1;
 return m_pDownloadPub_Info->Get_FileTotalSize ();
}

//
// 获取已下载的字节数,包括以前下载的和本次下载的
//
int CDownloadMTR::Get_TotalDownloadedSize()
{
 if ( !m_pDownloadPub_Info ) return -1;
 int nTotalUndownloadBytes = 0;
 for ( int nIndex=0; nIndex<m_nThreadCount; nIndex++ )
 {
  nTotalUndownloadBytes += m_pDownloadPub_MTR[nIndex].GetUndownloadBytes();
 }
 int nFileSize = m_pDownloadPub_Info->Get_FileTotalSize();
 if ( nFileSize < 1 ) return -1;
 // 文件大小减去未完成的,就是已下载的
 return ( nFileSize - nTotalUndownloadBytes );
}

int CDownloadMTR::Get_TotalDownloadedSize_ThisTimes()
{
 m_CSFor_DownloadedData.Lock ();
 int nTotalDownloadedSize_ThisTimes = m_nTotalDownloadedSize_ThisTimes;
 m_CSFor_DownloadedData.Unlock ();
 return nTotalDownloadedSize_ThisTimes;
}