FTP協議將使用兩條單獨的TCP連接,一條專用于發送FTP命令,另一條則專用于傳遞數據。初始建立連接時,服務器在21號端口上接收來自客戶端的命令連接。當需要傳送數據時(文件列表、文件數據等),客戶端向服務器發出Port命令,并進入監聽狀態,等待來自服務器的數據連接請求。
首先我們利用VC++6.0的AppWizard創建一個基于對話框的應用程序,命名為FtpClientDemo。調整主對話框的形式如圖1,為程序生成五個基于CAsyncSocket的新類,限于篇幅,只列出主要代碼。
■CCommandSocket類的主要代碼
voidCCommandSocket::OnReceive(intnErrorCode)
{
//這個函數使得服務器的應答消息顯示在編輯框上
char?buffer=newchar[4096];
memset(buffer,0,4096);
this-〉Receive(buffer,1024,0);
//接收應答消息
MessageList+=buffer;
m_ReturnMessage-〉SetWindowText(MessageList);
deletebuffer;
}
■CFileSocket類的主要代碼
voidCFileSocket::OnReceive(intnErrorCode)
{
//函數將收到的文件數據寫到文件中
if(File==NULL)
{File=newCFile();
File-〉Open(FileName,CFile::modeWrite|CFile::modeCreate);
}
char?buffer=newchar[4096];
memset(buffer,0,4096);
this-〉Receive(buffer,4096,0);
ReceiveString=buffer;
File-〉Write(ReceiveString,ReceiveString.GetLength());
deletebuffer;
}
■CReceiveSocket類的主要代碼
voidCReceiveSocket::OnReceive(intnErrorCode)
{
//接收服務器傳來的文件列表消息
CStringReceiveString,Temp;
char?buffer=newchar[4096];
memset(buffer,0,4096);
this-〉Receive(buffer,4096,0);//接收消息
ReceiveString+=buffer;
deletebuffer;
//將文件列表從收到的消息字符串中分離出來,并顯示在列表框中
while(!ReceiveString.IsEmpty())
{intp=ReceiveString.Find("\r\n");
if(p!=-1)
{Temp=ReceiveString.Left(p);
ReceiveString=ReceiveString.Right(ReceiveString.GetLength()-p-2);
DisplayMessage-〉AddString(Temp);
}
}
}
■CPortSocket類主要代碼
voidCPortSocket::OnAclearcase/" target="_blank" >ccept(intnErrorCode)
{
//根據不同的標志選擇相應的數據連接類,以接受服務器端的數據連接請求
if(Flag==LISTFILE)
//若程序要求對目錄進行列表,則采用CReceiveSocket類
{DataSocket=newCReceiveSocket(FileList);
this-〉Accept(?DataSocket);
}
elseif(Flag==DOWNLOAD)
//若程序要求下載文件,則生成CFileSocket類的對象
{FileSocket=newCFileSocket(FileName);
this-〉Accept(?FileSocket);
}
}
■主對話框類CFtpClient-DemoDlg的主要代碼
voidCFtpClientDemoDlg::OnFileList()
//響應“文件列表”按鈕、列表目錄
{CStringTemp;
if(ControlSocket==NULL)
{
//連接到FTP服務器
ControlSocket=newCCommandSocket(&&m_ReturnMessage);
ControlSocket-〉Create();
m_Server.GetWindowText(Temp);
ControlSocket-〉Connect(Temp,21);
//FTP服務器在21號端口接收連接
}
m_User.GetWindowText(Temp);
Temp="USER"+Temp+"\r\n";
ControlSocket-〉Send(Temp,Temp.GetLength(),0);
//發User命令,驗證用戶
m_Pass.GetWindowText(Temp);//m_Pass為“口令”編輯框的對應控制
Temp="PASS"+Temp+"\r\n";
ControlSocket-〉Send(Temp,Temp.GetLength(),0);
//發Pass命令,校驗口令
LisentPort(LISTFILE);
//數據連接的對象為目錄列表
ControlSocket-〉Send("LIST\r\n",7,0);
//發List命令,要求列表目錄
}
voidCFtpClientDemoDlg::OnDownLoad()
//下載文件
{
CStringString;
LisentPort(DOWNLOAD);
//獲得要下戴文件的文件名
m_LocalFile.GetWindowText(String);
//m_LocalFile為“文件名”編輯框的對應控制
String="RETR"+String+"\r\n";
ControlSocket-〉Send(String,String.GetLength(),0);
//發RETR命令,下載文件
}
voidCFtpClientDemoDlg::LisentPort(UINTFlag)
{
//根據要求選擇不同的數據連接對象
if(LisentSocket!=NULL)
//清空LisentSocket
{LisentSocket-〉Close();
deleteLisentSocket;
LisentSocket=NULL;
}
if(Flag==LISTFILE)
//如果為目錄列表數據連接對象
{LisentSocket=newCPortSocket(LISTFILE);
LisentSocket-〉SetListBox(&&m_FileList);
//傳列表框到CLisentSocket類中
}
elseif(Flag==DOWNLOAD)
//如果為文件傳輸數據連接對象
{CStringString;
m_LocalFile.GetWindowText(String);
LisentSocket=newCPortSocket(DOWNLOAD);
LisentSocket-〉SetFileName(String);
//傳文件名到CLisentSocket類中
}
LisentSocket-〉Create();
//建立Socket并進行監聽,等待FTP服務器進行數據連接
LisentSocket-〉Listen();
//取得數據連接Socket的IP地址和監聽端口,并把它們作為Port命令的參數
SOCKADDR_INlisting_address,control_address;
intaddr_size;
addr_size=sizeof(listing_address);
LisentSocket-〉GetSockName((SOCKADDR?)&&listing_address,&&addr_size);//取IP地址
ControlSocket-〉GetSockName((SOCKADDR?)&&control_address,&&addr_size);//取端口
unsignedchar?port=(unsignedchar?)&&(listing_address.sin_port);
unsignedchar?host=(unsignedchar?)&&(control_address.sin_addr);
CStringstrBuffer;
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);
//發送Port命令,進行數據連接
}
以上代碼在VC++6.0、Windows98上運行通過