📄 ftpdownload.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 + -