⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ftpdownload.cpp

📁 多线程下载工具可支持http,ftp,https多协议....vc编写
💻 CPP
字号:
// FTPDownload.cpp: implementation of the CFTPDownload class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "download.h"
#include "FTPDownload.h"

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

 DWORD WINAPI FTPDownloadThread(LPVOID);
 DWORD WINAPI FTPNotify(LPVOID); 

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


 /*这个类主要包含关于FTP下载的各种函数*/
 CFTPDownload::CFTPDownload()
{
    for(int  i=0; i<10; i++){
		m_hThread[i] = NULL;
		m_bTerminate[i] = FALSE;
		m_sum[i]=0;
		hh[i]=0;
	  }
    
	m_bResume = FALSE;

}

CFTPDownload::~CFTPDownload()
{

}

//开始任务函数,主要进行地址的分析,并调用创建线程函数开始下载任务。
void CFTPDownload::Begin(CString Allurl, CString Saveas, int nParts,CString User,CString Pass)
{
    for( int i=0; i<10; i++){
		  m_bTerminate[i] = FALSE;
		  m_state.wcsize[i] = 0;//用来记录暂停时下载文件的大小
		  m_sum[i] = 0;
		  hh[i]=0;
     }
	
	m_strSavePath = Saveas;
    m_nParts = nParts;
	m_strUser = User;
	m_strPass = Pass;
	
	if(!(AnalyseURL(Allurl))){AfxMessageBox("URL错误,请确认地址正确"); return;}//调用分析地址函数
	if(!(SendRequest())) {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 * (filesize /m_nParts);
			m_state.range[i * 2 + 1] = (i + 1) * (filesize / m_nParts) ;
            m_state.threadsize[i] = m_state.range[i * 2 + 1] - m_state.range[i * 2];//每个线程的任务总大小
		}
		m_state.range[2*m_nParts-1] = filesize;
		m_state.threadsize[m_nParts-1] = m_state.range[2*m_nParts-1] - m_state.range[2*m_nParts-2];

	}
    
    CreateThread(); //调用创建线程的任务
    return;

}

//分析地址的函数,将地址分为服务器与文件绝对路径,并获得文件名,用户名密码
BOOL CFTPDownload::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.ReverseFind(':');
	int m = m_strServer.Find("@");
	if(n > m)
	{
        CString temp = m_strServer.Mid(n+1);
        m_Port = (USHORT)_ttoi((LPCTSTR)temp);
		m_strServer = m_strServer.Left(n);
	}
	else
	{
		m_Port = 21;
	}
    
	if(m != -1)
	{
		strTemp.Empty();
		strTemp = m_strServer.Left(m);
        m_strServer =m_strServer.Mid(m+1);
		n = strTemp.Find(':');
		if(n==-1) return FALSE;
		m_strUser = strTemp.Left(n);
		m_strPass = strTemp.Mid(n+1);
	}

    return TRUE;
}

//向服务器发送第一次请求,登陆服务器
BOOL CFTPDownload::SendRequest()
{
   if(FSocket.m_hSocket!= NULL) FSocket.Close();
   FSocket.Create();
   FSocket.Connect(m_strServer, m_Port);

   ZeroMemory(ReadBuf,1025);
   FSocket.Receive(ReadBuf, 1025);
   strRead.Empty();
   strRead+= ReadBuf;
   if(!(AnalyseReceive(strRead))) {AfxMessageBox("连接错误,请确认地址正确"); return FALSE; }

   strSend.Empty(); 
   strSend ="USER "+m_strUser+"\r\n";
   SendInf(strSend);

   strSend.Empty(); 
   strSend ="PASS "+m_strPass+"\r\n";
   SendInf(strSend);

   strSend.Empty(); 
   strSend ="SIZE "+m_strObject+"\r\n";
   SendInf(strSend);

   FSocket.Close();

   return TRUE;
}

//发送请求时接受服务器返回信息,并分析
BOOL CFTPDownload::AnalyseReceive(CString strRead)
{
	DWORD Code;
	
	int n = strRead.Find(" ");
	CString temp = strRead.Left(n);
    Code = (DWORD)_ttoi((LPCTSTR)temp);
	if(Code<400)  return TRUE;
	else return FALSE;

}


void  CFTPDownload::SendInf(CString strSend)
{
   FSocket.Send(strSend.GetBuffer(0), strSend.GetLength());
   ZeroMemory(ReadBuf,1025);
   FSocket.Receive(ReadBuf, 1025);
   strRead.Empty();
   strRead+= ReadBuf;
   if(!(AnalyseReceive(strRead))) {AfxMessageBox("连接错误,请确认地址正确"); return; }
   int n = strSend.Find(" ");
   strSend = strSend.Left(n);
   if(strSend=="SIZE") SizeOf(strRead);//如果发送获取大小请求后,获取文件大小
}

//结合上面函数获得下载大小
void CFTPDownload::SizeOf(CString strRead)
{
	CString temp;
	int n = strRead.Find(" ");
	temp = strRead.Mid(n+1);
	filesize = (DWORD)_ttoi((LPCTSTR)temp);
}


//创建线程函数
void CFTPDownload::CreateThread()
{
	DWORD dwThread;
	m_index = 0;
	for(int i = 0; i < m_nParts; i++)
	{
		m_bTerminate[i] = FALSE;
		m_hThread[i] = ::CreateThread(NULL, 0, FTPDownloadThread,  (LPVOID)this, 0, &dwThread);

	}
    DWORD dwNotify;
	m_hNotify = ::CreateThread(NULL, 0, FTPNotify, (LPVOID)this, 0, &dwNotify);
}

DWORD WINAPI FTPDownloadThread(LPVOID lpParam)
{
	CFTPDownload* pThis = (CFTPDownload*)lpParam;
	int index;
	index = pThis->m_index;
	InterlockedIncrement(&pThis->m_index);
    pThis->ThreadFunc(index);
	return 0L;
}

DWORD WINAPI FTPNotify(LPVOID lpParam)
{
    CFTPDownload* pThis = (CFTPDownload*)lpParam;
    pThis->Finish();
	return 0L;
}

//实际下载函数,向服务器发送下载请求
void CFTPDownload::ThreadFunc(int index)
{
    if(m_state.range[2*index] ==-99) { hh[index]=1000; m_sum[index]=m_state.threadsize[index]; return;}//用来判断此线程是否已经结束
	
	CSocket tSocket,readsocket;
	tSocket.Create();
    tSocket.Connect(m_strServer, m_Port);

	CString strSend,strRead;
	char	 ReadBuf[1025];

   ZeroMemory(ReadBuf,1025);
   tSocket.Receive(ReadBuf, 1025);
   strRead.Empty();
   strRead+= ReadBuf;
   if(!(AnalyseReceive(strRead))) {AfxMessageBox("线程连接错误,建议减少线程重试"); m_bTerminate[0] = TRUE;return; }

   strSend.Empty(); 
   strSend ="USER "+m_strUser+"\r\n";
   tSocket.Send(strSend.GetBuffer(0), strSend.GetLength());
   ZeroMemory(ReadBuf,1025);
   tSocket.Receive(ReadBuf, 1025);
   strRead.Empty();
   strRead+= ReadBuf;
   if(!(AnalyseReceive(strRead))) {AfxMessageBox("线程连接错误,建议减少线程重试"); m_bTerminate[0] = TRUE;return; }

   strSend.Empty(); 
   strSend ="PASS "+m_strPass+"\r\n";
   tSocket.Send(strSend.GetBuffer(0), strSend.GetLength());
   ZeroMemory(ReadBuf,1025);
   tSocket.Receive(ReadBuf, 1025);
   strRead.Empty();
   strRead+= ReadBuf;
   if(!(AnalyseReceive(strRead))) {AfxMessageBox("线程连接错误,建议减少线程重试"); m_bTerminate[0] = TRUE;return; }

   strSend.Empty(); 
   strSend ="TYPE I\r\n";
   tSocket.Send(strSend.GetBuffer(0), strSend.GetLength());
   ZeroMemory(ReadBuf,1025);
   tSocket.Receive(ReadBuf, 1025);
   strRead.Empty();
   strRead+= ReadBuf;
   if(!(AnalyseReceive(strRead))) {AfxMessageBox("线程连接错误,建议减少线程重试"); m_bTerminate[0] = TRUE;return; }

   strSend.Empty(); 
   strSend.Format("REST %d\r\n",m_state.range[2*index]);
   tSocket.Send(strSend.GetBuffer(0), strSend.GetLength());
   ZeroMemory(ReadBuf,1025);
   tSocket.Receive(ReadBuf, 1025);
   strRead.Empty();
   strRead+= ReadBuf;
   if(!(AnalyseReceive(strRead))) {AfxMessageBox("线程连接错误,建议减少线程重试");m_bTerminate[0] = TRUE; return; }


   //被动传输模式,获得服务器提供的地址端口
   strSend.Empty(); 
   strSend ="PASV\r\n";
   tSocket.Send(strSend.GetBuffer(0), strSend.GetLength());
   ZeroMemory(ReadBuf,1025);
   tSocket.Receive(ReadBuf, 1025);
   strRead.Empty();
   strRead+= ReadBuf;
   

   int i = strRead.Find("("); int j = strRead.Find(")");
   CString temp = strRead.Mid(i+1,(j-i)-1);
   
   //将服务器传回的地址信息进行格式转换
   i = temp.ReverseFind(',');
   UINT  port = atol(temp.Mid(i+1));
   temp = temp.Left(i);
   i = temp.ReverseFind(',');
   port += 256*atol(temp.Mid(i+1));
   CString rhost = temp.Left(i);
   while(1)
   {
	   if((i=rhost.Find(','))==-1) break;
	   rhost.SetAt(i,'.');
   }

   strSend.Empty(); 
   strSend ="RETR "+m_strObject+"\r\n";
   tSocket.Send(strSend.GetBuffer(0), strSend.GetLength());

   readsocket.Create();
   readsocket.Connect(rhost,port);
 
   CFile file;
   CString name;
   name.Format(".down%d", index);
   name =m_strSavePath + name;
   	if(!m_bResume)
		 file.Open(name, CFile::modeCreate | CFile::modeWrite);
	else
		 file.Open(name, CFile::modeWrite);
   file.SeekToEnd();

   DWORD num,sum; sum=0;
   dwsize[index] = m_state.range[2*index+1]-m_state.range[2*index];
   
   //循环接受服务器发送的数据
   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(ReadBuf,1025);
      if( sum >= dwsize[index]  || !(num = readsocket.Receive(ReadBuf, 1025)) || num == SOCKET_ERROR) break;
	  else   file.Write(ReadBuf, num);
      sum+=num; 
	        //进度条显示需要的一些变量
			m_sum[index]=sum+m_state.wcsize[index];
			hh[index]=  long((1.0*m_sum[index])*1000/m_state.threadsize[index]);
   }
   

   file.Close();
   readsocket.Close();
   tSocket.Close();
   
   m_state.range[2*index]= -99;//用来判断此线程是否已经结束的标志
   return;
}

//全部线程结束后调用的完成函数
void CFTPDownload::Finish()
{
	char* lpData = NULL;
    HRESULT ret = WaitForMultipleObjects(m_nParts, m_hThread, TRUE, INFINITE);
	if(m_bTerminate[0]){
		return;
	}
	//如果线程结束正常,就分别打开临时文件将其依次写入目标文件
	if(ret == 0){
	    
		CFile file, t[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;
			t[i].Open(name, CFile::modeRead);
			DWORD len = m_state.threadsize[i];
			if(lpData){
				delete [] lpData;
				lpData = NULL;
			}
			if(lpData == NULL){
				lpData = new char[len];
			}
			t[i].Read(lpData, len);
			file.Write(lpData, len);
			t[i].Close();
			DeleteFile(name);
			}
			if(lpData){
				delete [] lpData;
				lpData = NULL;
			}
			file.Close();
			name.Empty();
			name = m_strSavePath + ".down";
			DeleteFile(name);
			AfxMessageBox("下载完成");
		}
	
    if(lpData){
		delete [] lpData;
		lpData = NULL;
	}
	return ;
}

//暂停函数
void CFTPDownload::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 + -