为了让大家更好的理解我发表的<<实现FTP多线程下载>>,我把我自己编写的CMultiFTP类贴上来。这段代码我不是很满意(以前写的,请写的较仓促),
但主要供大家参考,更好得理解多线程下载的实现。
// MultiFTP1.cpp: implementation of the CMultiFTP class.
//
//////////////////////////////////////////////////////////////////////
/*
#include "stdafx.h"
#include "MultiFTP.h"
#include "MultiFTP1.h"
#include <afxmt.h>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#define BUFFERSIZE 5000
CCriticalSection gcs_multiftp;
struct stThreadParam
{
CString szLocalname;
CString szRemotename;
CString szHost;
CString szUsername,szPassword;
int ID;
int nPort;
CMultiFTP *pFtp;
UINT uStart,uLength;
};
UINT DownloadThread(LPVOID pVoid)
{
stThreadParam *pvar=(stThreadParam *)pVoid;
int ID=pvar->ID ,nPort=pvar->nPort;
CString szLocalname=pvar->szLocalname,szRemotename=pvar->szRemotename;
CString szHost=pvar->szHost ;
CString szUsername=pvar->szUsername ,szPassword=pvar->szPassword ;
CMultiFTP *pFtp=pvar->pFtp;
UINT uStart=pvar->uStart ,uLength=pvar->uLength;
delete pvar;
file://init over
CString szMsg;
CFTPGetFile m_getfile(pFtp);
CFile m_file;
char *pBuffer;
if(!m_getfile.Connect(szHost,nPort,szUsername,szPassword))
{
szMsg.Format("connect to data port fail\r\nID:%d\r\nError:%s",ID,m_getfile.szMsg);
AfxMessageBox(szMsg);
return 0;
}
m_file.Open(szLocalname,CFile::modeWrite|CFile::modeCreate);
pBuffer=(char *)VirtualAlloc(NULL,uLength,MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE);
ASSERT(pBuffer);
if(!m_getfile.OpenFile(szRemotename,szLocalname,uStart,uLength))
{
m_file.Close();
DeleteFile(szLocalname);
VirtualFree(pBuffer,0,MEM_RELEASE);
szMsg.Format("open file fail\r\nID:%d\r\nError:%s",ID,m_getfile.szMsg);
AfxMessageBox(szMsg);
return 0;
}
file://get file contents
for(UINT uRead;uLength;uLength-=uRead)
{
uRead=m_getfile.Read(pBuffer,uLength);
m_file.Write(pBuffer,uRead);
}
m_file.Close();
VirtualFree(pBuffer,0,MEM_RELEASE);
gcs_multiftp.Lock();
pFtp->OnThreadOver(ID,0,"ok");
gcs_multiftp.Unlock();
return 0;
}
CMultiFTP::CMultiFTP()
{
skClient.Create();
nThreads=1;
blContinue=FALSE;
}
CMultiFTP::~CMultiFTP()
{
skClient.ShutDown(2);
skClient.Close();
}
int CMultiFTP::GetThreads()
{
return nThreads;
}
int CMultiFTP::SetThreads(int n)
{
if(blContinue)
nThreads=n;
return nThreads;
}
BOOL CMultiFTP::Connect(CString szHost, UINT Port, CString szUser, CString szPass)
{
ASSERT(skClient.m_hSocket);
szHostname=szHost;
nPort=Port;
szUsername=szUser;
szPassword=szPass;
szMsg=="";
if(!skClient.Connect(szHostname,nPort))
{
return FALSE;
}
GetMsg();
CString szCommand;
szCommand.Format("user %s\r\n",szUsername);
DoCommand(szCommand);
szCommand.Format("pass %s",szPassword);
if(DoCommand(szCommand)!=FTP_LOGOK)
return FALSE;
if(szMsg[3]=='-')
GetMsg();
if(DoCommand("rest 100")==FTP_RESTOK)
{
blContinue=TRUE;
}
else
{
nThreads=1;
blContinue=FALSE;
}
DoCommand("type a");
return TRUE;
}
void CMultiFTP::GetMsg()
{
szMsg="";
char chMsg[256];
int nRecv;
nRecv=skClient.Receive(chMsg,255);
if(nRecv==SOCKET_ERROR || nRecv==0) return;
chMsg[nRecv]='\0';
szMsg=chMsg;
}
int CMultiFTP::GetReturnCode()
{
CString szTemp=szMsg;
szTemp.TrimLeft();
return atoi(szTemp.Left(3));
}
int CMultiFTP::DoCommand(CString szCommand)
{
szCommand+="\r\n";
if(skClient.Send((LPCTSTR)szCommand,szCommand.GetLength())==SOCKET_ERROR)
return SOCKET_ERROR;
GetMsg();
return GetReturnCode();
}
void CMultiFTP::Close()
{
skClient.ShutDown(2);
skClient.Close();
}
BOOL CMultiFTP::IsContinue()
{
return blContinue;
}
void CMultiFTP::OnThreadOver(int ID, UINT uRecv,CString szMsg)
{
nThreadOvered++;
if(nThreadOvered==nThreads)
{
if(Merge())
AfxMessageBox("download over");
else
AfxMessageBox("download fail");
}
}
BOOL CMultiFTP::Get(CString szRemotepath, CString szLocalpath)
{
stThreadParam *pvar;
CString szCommand;
szCommand.Format("size %s",szRemotepath);
if(DoCommand(szCommand)!=FTP_SIZEOK)
return FALSE;
szMsg.Delete(0,4);
UINT uSize=atoi(szMsg),uAvgSize=uSize/nThreads,uStart=0;
nThreadOvered=0;
for(int i=1;i<=nThreads;i++)
{
pvar=new stThreadParam;
pvar->ID =i;
pvar->nPort =nPort;
pvar->pFtp =this;
pvar->szHost =szHostname;
pvar->szPassword =szPassword;
pvar->szUsername =szUsername;
pvar->szLocalname.Format("%s_%d.dat",szLocalpath,i);
pvar->szRemotename =szRemotepath;
pvar->uStart =uStart;
pvar->uLength =(i==nThreads)?uSize:uAvgSize;
uStart+=pvar->uLength ;
uSize-=uAvgSize;
AfxBeginThread(DownloadThread,pvar);
}
szLocalname=szLocalpath;
return TRUE;
}
file://CFTPGetFile class
CFTPGetFile::CFTPGetFile(CMultiFTP *pFtp2)
{
pFtp=pFtp2;
blFileopened=FALSE;
}
CFTPGetFile::~CFTPGetFile()
{
Close();
}
BOOL CFTPGetFile::OpenFile(CString szRemotename,CString szLocalname,UINT uStart2,UINT uLength2)
{
ASSERT(skClient.m_hSocket);
if(skData.m_hSocket)
skData.Close();
uStart=uStart2;
uLength=uLength2;
CString szCommand,szHost;
int iPort;
file://open remote file
file://get data transfer port
if(DoCommand("pasv\r\n")!=FTP_PASVOK)
{
szMsg.Format("openfile:pasv command fail,code:%d",GetFtpCode());
AfxMessageBox(szMsg);
return FALSE;
}
int p1,p2,i1,i2;
int iStart=szMsg.ReverseFind('(');
for(int i=iStart,count=0;i<szMsg.GetLength();i++)
{
if(szMsg[i]==',')
{
count++;
if(count==4) p1=i+1;
if(count==5) p2=i-1;
}
}
i1=atoi(szMsg.Mid(p1,p2-p1+1));
i2=atoi(szMsg.Mid(p2+2));
iPort=(i1<<8)+i2;
szHost=szMsg.Mid(iStart+1,p1-iStart-2);
szHost.Replace(",",".");
file://set file mode to i
DoCommand("type i");
file://reset remote file pointer
szCommand.Format("rest %d",uStart);
DoCommand(szCommand);
ASSERT(GetFtpCode()==FTP_RESTOK);
file://get file command
szCommand.Format("retr %s",szRemotename);
DoCommand(szCommand,FALSE);
skData.Create();
if(!skData.Connect(szHost,iPort))
{
skData.Close();
szMsg.Format("openfile:Connnect to data port:%d at server:%s fail",iPort,szHost);
AfxMessageBox(szMsg);
return FALSE;
}
return TRUE;
}
int CFTPGetFile::DoCommand(CString szCommand,BOOL blGetMsg)
{
int nRecv;
szCommand+="\r\n";
skClient.Send((LPCTSTR)szCommand,szCommand.GetLength());
if(blGetMsg)
{
nRecv=GetMsg();
if(nRecv==0 || nRecv==SOCKET_ERROR)
return nRecv;
return GetFtpCode();
}
return 0;
}
int CFTPGetFile::GetFtpCode()
{
szMsg.TrimLeft();
return atoi(szMsg);
}
BOOL CFTPGetFile::Connect(CString szHostname, int iPort, CString szUser, CString szPass)
{
if(skClient.m_hSocket)
skClient.Close();
skClient.Create();
ASSERT(skClient.m_hSocket);
if(!skClient.Connect(szHostname,iPort))
{
szMsg.Format("GetFile:connect to server fail:%d",GetLastError());
AfxMessageBox(szMsg);
Close();
return FALSE;
}
CString szCommand;
szCommand.Format("user %s\r\n",szUser);
DoCommand(szCommand);
szCommand.Format("pass %s\r\n",szPass);
DoCommand(szCommand);
if(szMsg[0]=='-')
GetMsg();
if(GetFtpCode()!=FTP_LOGOK)
{
szMsg.Format("GetFile:password is not correct");
AfxMessageBox(szMsg);
return FALSE;
}
DoCommand("type a\r\n");
return TRUE;
}
void CFTPGetFile::Close()
{
if(skClient.m_hSocket)
{
skClient.ShutDown(2);
skClient.Close();
}
if(skData.m_hSocket)
{
skData.ShutDown(2);
skData.Close();
}
}
int CFTPGetFile::GetMsg()
{
int nRecv;
szMsg="";
nRecv=skClient.Receive(chRecv,255);
if(nRecv==SOCKET_ERROR)
return nRecv;
chRecv[nRecv]='\0';
szMsg=chRecv;
return nRecv;
}
int CFTPGetFile::Read(char *pBuffer, int nBufferSize)
{
return skData.Receive(pBuffer,nBufferSize);
}
BOOL CMultiFTP::Merge()
{
CFileFind m_find;
CString szFilename;
DWORD dwSize;
for(int i=1;i<=nThreads;i++)
{
szFilename.Format("%s_%d.dat",szLocalname,i);
if(!m_find.FindFile(szFilename))
return FALSE;
if(i==nThreads)
{
m_find.FindNextFile();
dwSize=m_find.GetLength();
}
m_find.Close();
}
CFile m_file,m_file2;
char *pBuffer;
pBuffer=(char *)VirtualAlloc(NULL,dwSize,MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE);
m_file.Open(szLocalname,CFile::modeCreate|CFile::modeWrite);
for(i=1;i<=nThreads;i++)
{
szFilename.Format("%s_%d.dat",szLocalname,i);
m_file2.Open(szFilename,CFile::modeRead);
m_file2.Read(pBuffer,m_file2.GetLength());
m_file.Write(pBuffer,m_file2.GetLength());
m_file2.Close();
// DeleteFile(szFilename);
}
m_file.Close();
VirtualFree(pBuffer,0,MEM_RELEASE);
return TRUE;
}