FTP協(xié)議將使用兩條單獨(dú)的TCP連接,一條專用于發(fā)送FTP命令,另一條則專用于傳遞數(shù)據(jù)。初始建立連接時(shí),服務(wù)器在21號端口上接收來自客戶端的命令連接。當(dāng)需要傳送數(shù)據(jù)時(shí)(文件列表、文件數(shù)據(jù)等),客戶端向服務(wù)器發(fā)出Port命令,并進(jìn)入監(jiān)聽狀態(tài),等待來自服務(wù)器的數(shù)據(jù)連接請求。
首先我們利用VC++ 6.0的AppWizard創(chuàng)建一個(gè)基于對話框的應(yīng)用程序,命名為FtpClientDemo。調(diào)整主對話框的形式為程序生成五個(gè)基于CAsyncSocket的新類,限于篇幅,只列出主要代碼。
■CCommandSocket類的主要代碼
void CCommandSocket::OnReceive(int nErrorCode)
{
//這個(gè)函數(shù)使得服務(wù)器的應(yīng)答消息顯示在編輯框上
char buffer=new char[4096];
memset(buffer,0,4096);
this-〉Receive(buffer,1024,0);
//接收應(yīng)答消息
MessageList+=buffer;
m_ReturnMessage-〉SetWindowText(MessageList);
delete buffer;
}
■CFileSocket類的主要代碼
void CFileSocket::OnReceive(int nErrorCode)
{
//函數(shù)將收到的文件數(shù)據(jù)寫到文件中
if(File= =NULL)
{ File=new CFile();
File-〉Open(FileName,CFile::modeWrite|CFile::modeCreate);
}
charbuffer=new char[4096];
memset(buffer,0,4096);
this-〉Receive(buffer,4096,0);
ReceiveString=buffer;
File-〉Write(ReceiveString,ReceiveString.GetLength( ));
delete buffer;
}
■CReceiveSocket類的主要代碼
void CReceiveSocket::OnReceive(int nErrorCode)
{
//接收服務(wù)器傳來的文件列表消息
CString ReceiveString,Temp;
charbuffer=new char[4096];
memset(buffer,0,4096);
this-〉Receive(buffer,4096,0); //接收消息
ReceiveString+=buffer;
delete buffer;
//將文件列表從收到的消息字符串中分離出來,并顯示在列表框中
while(!ReceiveString.IsEmpty())
{ int p=ReceiveString.Find("\r\n");
if(p!=-1)
{ Temp=ReceiveString.Left(p);
ReceiveString=ReceiveString.Right(ReceiveString.GetLength()-p-2);
DisplayMessage-〉A(chǔ)ddString(Temp);
}
}
}
■CPortSocket類主要代碼
void CPortSocket::OnAccept(int nErrorCode)
{
//根據(jù)不同的標(biāo)志選擇相應(yīng)的數(shù)據(jù)連接類,以接受服務(wù)器端的數(shù)據(jù)連接請求
if(Flag= =LISTFILE)
//若程序要求對目錄進(jìn)行列表,則采用CReceiveSocket類
{DataSocket=new CReceiveSocket(FileList);
this-〉A(chǔ)ccept(DataSocket);
}
else if(Flag= =DOWNLOAD)
//若程序要求下載文件,則生成CFileSocket類的對象
{FileSocket=new CFileSocket(FileName);
this-〉A(chǔ)ccept(FileSocket);
}
}
■主對話框類CFtpClient- DemoDlg的主要代碼
void CFtpClientDemoDlg::OnFileList()
//響應(yīng)“文件列表”按鈕、列表目錄
{ CString Temp;
if(ControlSocket= =NULL)
{
//連接到FTP服務(wù)器
ControlSocket=new CCommandSocket(&&m_ReturnMessage);
ControlSocket-〉Create();
m_Server.GetWindowText(Temp);
ControlSocket-〉Connect(Temp,21);
//FTP服務(wù)器在21號端口接收連接
}
m_User.GetWindowText(Temp);
Temp="USER "+Temp+"\r\n";
ControlSocket-〉Send(Temp,Temp.GetLength(),0);
//發(fā)User命令,驗(yàn)證用戶
m_Pass.GetWindowText(Temp); //m_Pass為“口令”編輯框的對應(yīng)控制
Temp="PASS "+Temp+"\r\n";
ControlSocket-〉Send(Temp,Temp.GetLength(),0);
//發(fā)Pass命令,校驗(yàn)口令
LisentPort(LISTFILE);
//數(shù)據(jù)連接的對象為目錄列表
ControlSocket-〉Send("LIST \r\n",7 ,0);
//發(fā)List命令,要求列表目錄
}
void CFtpClientDemoDlg::OnDownLoad( )
//下載文件
{
CString String;
LisentPort(DOWNLOAD);
//獲得要下戴文件的文件名
m_LocalFile.GetWindowText(String);
// m_LocalFile為“文件名”編輯框的對應(yīng)控制
String="RETR "+String+"\r\n";
ControlSocket-〉Send(String,String.GetLength( ),0);
//發(fā)RETR命令,下載文件
}
void CFtpClientDemoDlg::LisentPort(UINT Flag)
{
//根據(jù)要求選擇不同的數(shù)據(jù)連接對象
if(LisentSocket!=NULL)
//清空LisentSocket
{ LisentSocket-〉Close();
delete LisentSocket;
LisentSocket=NULL;
}
if(Flag= =LISTFILE)
//如果為目錄列表數(shù)據(jù)連接對象
{ LisentSocket=new CPortSocket(LISTFILE);
LisentSocket-〉SetListBox(&&m_FileList);
//傳列表框到CLisentSocket類中
}
else if(Flag= =DOWNLOAD)
//如果為文件傳輸數(shù)據(jù)連接對象
{ CString String;
m_LocalFile.GetWindowText(String);
LisentSocket=new CPortSocket(DOWNLOAD);
LisentSocket-〉SetFileName(String);
//傳文件名到CLisentSocket類中
}
LisentSocket-〉Create();
//建立Socket并進(jìn)行監(jiān)聽,等待FTP服務(wù)器進(jìn)行數(shù)據(jù)連接
LisentSocket-〉Listen();
//取得數(shù)據(jù)連接Socket的IP地址和監(jiān)聽端口,并把它們作為Port命令的參數(shù)
SOCKADDR_IN listing_address, control_address;
int addr_size;
addr_size = sizeof(listing_address);
LisentSocket-〉GetSockName((SOCKADDR )&&listing_address, &&addr_size); //取IP地址
ControlSocket-〉GetSockName((SOCKADDR )&&control_address, &&addr_size); //取端口
unsigned char port = (unsigned char )&&(listing_address.sin_port);
unsigned char host = (unsigned char )&&(control_address.sin_addr);
CString strBuffer;
strBuffer.Format("PORT %i,%i,%i,%i,%i,%i\r\n",(int)host[0], (int)host[1], (int)host[2], (int)host[3],(int)port[0], (int)port[1]);
ControlSocket-〉Send(strBuffer,strBuffer.GetLength(),0);
//發(fā)送Port命令,進(jìn)行數(shù)據(jù)連接
}
以上代碼在VC++ 6.0、Windows 98上運(yùn)行通過
首先我們利用VC++ 6.0的AppWizard創(chuàng)建一個(gè)基于對話框的應(yīng)用程序,命名為FtpClientDemo。調(diào)整主對話框的形式為程序生成五個(gè)基于CAsyncSocket的新類,限于篇幅,只列出主要代碼。
■CCommandSocket類的主要代碼
void CCommandSocket::OnReceive(int nErrorCode)
{
//這個(gè)函數(shù)使得服務(wù)器的應(yīng)答消息顯示在編輯框上
char buffer=new char[4096];
memset(buffer,0,4096);
this-〉Receive(buffer,1024,0);
//接收應(yīng)答消息
MessageList+=buffer;
m_ReturnMessage-〉SetWindowText(MessageList);
delete buffer;
}
■CFileSocket類的主要代碼
void CFileSocket::OnReceive(int nErrorCode)
{
//函數(shù)將收到的文件數(shù)據(jù)寫到文件中
if(File= =NULL)
{ File=new CFile();
File-〉Open(FileName,CFile::modeWrite|CFile::modeCreate);
}
charbuffer=new char[4096];
memset(buffer,0,4096);
this-〉Receive(buffer,4096,0);
ReceiveString=buffer;
File-〉Write(ReceiveString,ReceiveString.GetLength( ));
delete buffer;
}
■CReceiveSocket類的主要代碼
void CReceiveSocket::OnReceive(int nErrorCode)
{
//接收服務(wù)器傳來的文件列表消息
CString ReceiveString,Temp;
charbuffer=new char[4096];
memset(buffer,0,4096);
this-〉Receive(buffer,4096,0); //接收消息
ReceiveString+=buffer;
delete buffer;
//將文件列表從收到的消息字符串中分離出來,并顯示在列表框中
while(!ReceiveString.IsEmpty())
{ int p=ReceiveString.Find("\r\n");
if(p!=-1)
{ Temp=ReceiveString.Left(p);
ReceiveString=ReceiveString.Right(ReceiveString.GetLength()-p-2);
DisplayMessage-〉A(chǔ)ddString(Temp);
}
}
}
■CPortSocket類主要代碼
void CPortSocket::OnAccept(int nErrorCode)
{
//根據(jù)不同的標(biāo)志選擇相應(yīng)的數(shù)據(jù)連接類,以接受服務(wù)器端的數(shù)據(jù)連接請求
if(Flag= =LISTFILE)
//若程序要求對目錄進(jìn)行列表,則采用CReceiveSocket類
{DataSocket=new CReceiveSocket(FileList);
this-〉A(chǔ)ccept(DataSocket);
}
else if(Flag= =DOWNLOAD)
//若程序要求下載文件,則生成CFileSocket類的對象
{FileSocket=new CFileSocket(FileName);
this-〉A(chǔ)ccept(FileSocket);
}
}
■主對話框類CFtpClient- DemoDlg的主要代碼
void CFtpClientDemoDlg::OnFileList()
//響應(yīng)“文件列表”按鈕、列表目錄
{ CString Temp;
if(ControlSocket= =NULL)
{
//連接到FTP服務(wù)器
ControlSocket=new CCommandSocket(&&m_ReturnMessage);
ControlSocket-〉Create();
m_Server.GetWindowText(Temp);
ControlSocket-〉Connect(Temp,21);
//FTP服務(wù)器在21號端口接收連接
}
m_User.GetWindowText(Temp);
Temp="USER "+Temp+"\r\n";
ControlSocket-〉Send(Temp,Temp.GetLength(),0);
//發(fā)User命令,驗(yàn)證用戶
m_Pass.GetWindowText(Temp); //m_Pass為“口令”編輯框的對應(yīng)控制
Temp="PASS "+Temp+"\r\n";
ControlSocket-〉Send(Temp,Temp.GetLength(),0);
//發(fā)Pass命令,校驗(yàn)口令
LisentPort(LISTFILE);
//數(shù)據(jù)連接的對象為目錄列表
ControlSocket-〉Send("LIST \r\n",7 ,0);
//發(fā)List命令,要求列表目錄
}
void CFtpClientDemoDlg::OnDownLoad( )
//下載文件
{
CString String;
LisentPort(DOWNLOAD);
//獲得要下戴文件的文件名
m_LocalFile.GetWindowText(String);
// m_LocalFile為“文件名”編輯框的對應(yīng)控制
String="RETR "+String+"\r\n";
ControlSocket-〉Send(String,String.GetLength( ),0);
//發(fā)RETR命令,下載文件
}
void CFtpClientDemoDlg::LisentPort(UINT Flag)
{
//根據(jù)要求選擇不同的數(shù)據(jù)連接對象
if(LisentSocket!=NULL)
//清空LisentSocket
{ LisentSocket-〉Close();
delete LisentSocket;
LisentSocket=NULL;
}
if(Flag= =LISTFILE)
//如果為目錄列表數(shù)據(jù)連接對象
{ LisentSocket=new CPortSocket(LISTFILE);
LisentSocket-〉SetListBox(&&m_FileList);
//傳列表框到CLisentSocket類中
}
else if(Flag= =DOWNLOAD)
//如果為文件傳輸數(shù)據(jù)連接對象
{ CString String;
m_LocalFile.GetWindowText(String);
LisentSocket=new CPortSocket(DOWNLOAD);
LisentSocket-〉SetFileName(String);
//傳文件名到CLisentSocket類中
}
LisentSocket-〉Create();
//建立Socket并進(jìn)行監(jiān)聽,等待FTP服務(wù)器進(jìn)行數(shù)據(jù)連接
LisentSocket-〉Listen();
//取得數(shù)據(jù)連接Socket的IP地址和監(jiān)聽端口,并把它們作為Port命令的參數(shù)
SOCKADDR_IN listing_address, control_address;
int addr_size;
addr_size = sizeof(listing_address);
LisentSocket-〉GetSockName((SOCKADDR )&&listing_address, &&addr_size); //取IP地址
ControlSocket-〉GetSockName((SOCKADDR )&&control_address, &&addr_size); //取端口
unsigned char port = (unsigned char )&&(listing_address.sin_port);
unsigned char host = (unsigned char )&&(control_address.sin_addr);
CString strBuffer;
strBuffer.Format("PORT %i,%i,%i,%i,%i,%i\r\n",(int)host[0], (int)host[1], (int)host[2], (int)host[3],(int)port[0], (int)port[1]);
ControlSocket-〉Send(strBuffer,strBuffer.GetLength(),0);
//發(fā)送Port命令,進(jìn)行數(shù)據(jù)連接
}
以上代碼在VC++ 6.0、Windows 98上運(yùn)行通過