FTP 协议解析与实现

 

正文:

一、FTP通信原理简述

1.1 FTP简介

FTP是基于TCP/IP协议的一个应用协议。主要实现在不同的计算机之间的数据共享。FTP 采用的是C/S模式。客户既可以下载文件也可以上传文件。当然,FTP给用户一定的权限。用户只能在权限下使用。目前,FTP的服务器种类很多,比如常用的SERV-U,客户端程序也很多,比如:CuteFTP。WINDOWS也提供了一个FTP客户程序。它们都根据相同的协议标准来设计的,具体协议内容可参考RFC文档。

SERV-U工作界面

windows提供的客户端

1.2 FTP工作原理

FTP工作原理与其它的应用协议有些不同。它是用两个端口进行通信的。一个端口用于命令交互。这个端口在用户连接之后一直保持;而另一个端口只是在数据传时打开(比如:上传文件,下载文件,获取服务端文件列表),在数据传输时有两种不同的模式,一是用户开通这个数据端口,这种模式叫做主动模式;二是服务器提供一个接口,这个模式叫被动模式。

FTP原理图

1.3 用户登录

FTP服务器提供了用户的访问权限,有的服务器可以匿名登录,有的服务器要求用户使用密码登录。在每一个与登录有关的命令时,服务器都会有一个返回信息。

下面显示了一个登录过程:

1.4 数据传输

在FTP中可以定义数据的传输格式,比如:二进制(进行图象和应用程序传输这种格式)。下面是一个传输过程:

二、FTP命令

在WINDOWS中提供的命令不是FTP的标准命令。有些命令是许多命令的合集。而FTP标准命令,每发送一个,服务器就会做出一个相应的动作,并把认证信息发送给用户。

具体的命令可以参照有关的资料

三、实例

在这里我们用一个FTP客户端来说明以上的知识。这里面主要是一个封装的类。

CFTPClient这个类实现的文件的上传与下载并能获得服务端文件的信息。

1.1 CFTPClient类

 
class  CFTPClient 



//成员变量 


private

CSocket 
*
m_pSocket; 

CArchive 
*
m_pRxarch; 

CArchive 
*
m_pTxarch; 

CSocketFile 
*
m_psfSokFile; 

CString m_strMsg;
//服务器发回的消息 


CString m_fc; 

CftpclientDlg 
*m_pWnd; //用于对窗口的操作 

CByteArray m_btBuf; 

//成员函数 

public

CFTPClient(
void
); 

~CFTPClient(void
); 

//发送命令到服务器 


BOOL FtpCommand (CString strCommand); 

//登录到FTP服务器,这个函数只支持在没有防火墙的时候 

BOOL LogOnToserver ( CString strHostname , 
int nHostPort , CString strUserName , CString strPassword ); 

//退出服务器 


void LogOffServer(); 

//上传下载文件 


BOOL MoveFile (CString strRemoteFile,CString strLocalFile , BOOL bPasv , BOOL bGet); 

//列出文件列表 

BOOL List(); 

void ProcessList(); 

//获取一行信息 


BOOL GetLine(
int ndx,CString &strLine); 

//发送数据 


BOOL WriteStr(CString strOutPut); 

//接收数据 

BOOL ReadStr(); 

//设置窗口 

void SetWnd(CftpclientDlg *pWnd); 

//发送信息 


void SetMessage(CString strMsg); 

//获取文件信息 


BOOL GetFtpFileInfo(
int ndx,FTP_FILE_INFO &ftpFileInfo); 

protected


//读取服务器发送的信息 


BOOL ReadStr2(); 

//打开通道 

BOOL OpenControlChannel(CString strServerHost , 
int nServerPort); 

//关闭通道 


void CloseControlChannel(); 

}


1.2
 登录函数 

I
//// 

//  

//
 函数:BOOL CFTPClient::LogOnToserver () 

//
 

//
 描述: 

// 这个函数用于登录到FTP服务器,在这个函数没有对系统的防火墙作//
进一步分析, 

//
读者可以进一步扩展它的功能 

//
 

//
 

//
 参数: 

//
 -strHostname 登录的主机名 

//
 -nHostPort 主机端口 

//
 -strUserName 用户名 

//
 -strPassword 用户密码 

//
 返回: 

//
 -BOOL 成功返回 TRUE 否则返回 FALSE 

//
 

// //吴庆民 2005.4.19 


//

BOOL CFTPClient::LogOnToserver (CString strHostname,
int  nHostPort,CString strUserName,CString strPassword) 



if (!this->OpenControlChannel (strHostname,21)) return
 FALSE; 

if(!this->ReadStr ()) return
 FALSE; 

this->SetMessage (this->
m_strMsg ); 

//发送一个空消息 


CString temp; 

temp 
= "USER " + strUserName + " "

//发送用户名 


if (!this->WriteStr (temp)) 



return
 FALSE; 

}
 

if (!this->ReadStr ()) return
 FALSE; 

this->SetMessage (this->
m_strMsg ); 

//发送密码 


temp 
= "PASS " + strPassword + " "

if (!this->WriteStr (temp)) return
 FALSE; 

if (!this->ReadStr ()) return
 FALSE; 

this->SetMessage (this->
m_strMsg ); 

return
 TRUE; 

}
 

这个函数主要是联接服务器打开一个通道用于命令传输。这个通道是全双工的。 

1.3
 上传下载文件 

///// 

//  

//
 函数:BOOL CFTPClient::MoveFile () 

//
 

//
 描述: 

//
 上传或下载文件,不支持多线程,可以在这个函数上进行一下扩展 

//
 

//
 

//
 参数: 

//
 -strRemoteFile 远程文件名 

//
 -strLocalFile 本地文件名 

//
 -bPasv 是否为被动模式传输 

//
 -bGet 是否为下载文件 

//
 

//
 返回: 

//
 -BOOL 成功返回 TRUE 否则返回 FALSE 

//
 

// //吴庆民 2005.4.19 


/

BOOL CFTPClient::MoveFile (CString strRemoteFile,CString strLocalFile,BOOL bPasv,BOOL bGet) 



CFile flDataFile; 

CString strCommand ; 

int pos = 0


UINT uServSock,uLocalSock; 

CString strHost; 

CSocket sServ; 

CAsyncSocket asListen; 

int i=0,j=0
,num,numread,numsent; 

CString strTemp; 

const int BUFSIZE = 4096


char
 cbuf[BUFSIZE]; 

if (!flDataFile.Open (strLocalFile,(bGet?CFile::modeCreate|
CFile::modeWrite:CFile::modeRead))) 



this->SetMessage ("上传或下载的文件在本地不能打开!"
); 

return
 FALSE; 

}
 

//准备传输 


strCommand 
= "TYPE I  "

if (!this->WriteStr (strCommand)) return
 FALSE; 

if (!this->ReadStr ()) return
 FALSE; 

this->SetMessage (this->
m_strMsg ); 

if
 (bPasv) 



strCommand 
= "PASV  "


if (!this->WriteStr (strCommand)) return
 FALSE; 

if (!this->ReadStr ()) return
 FALSE; 

this->SetMessage (this->
m_strMsg ); 

//if ((==-1&&(j=this->m_strMsg.Find ("/)"))==-1) return FALSE; 


i
=this->m_strMsg.Find ("("); 

j
=this->m_strMsg.Find (")"
); 

if (i==-1||j==-1




this->SetMessage ("响应错误!"
); 

}
 

strTemp 
= this->m_strMsg.Mid (i+1,(j-i)-1
); 

= strTemp.ReverseFind (','
); 

uServSock 
= atol(strTemp.Right (strTemp.GetLength () - (i+1
))); 

strTemp 
=
 strTemp.Left (i); 

//this->SetMessage (strTemp); 


= strTemp.ReverseFind (','); 

uServSock 
+= 256*atol(strTemp.Right (strTemp.GetLength () - (i+1
))); 

strHost 
=
 strTemp.Left (i); 

while(1




if ((i=strHost.Find (','))==-1break


strHost.SetAt (i,
'.'
); 

}
 

//
this->SetMessage (strHost); 

//
CString temp; 

//
temp.Format (strHost+" %d",uServSock ); 

//this->SetMessage (temp); 


}
 

else
 



if (!this->m_pSocket->GetSockName (strHost,uLocalSock)) return
 FALSE; 

while(1




if ((i=strHost.Find ("."))==-1break


strHost.SetAt (i,
','
); 

}
 

if (!(sServ.Create ())||!(sServ.Listen ())) return
 FALSE; 

if(!sServ.GetSockName (strTemp,uLocalSock )) return
 FALSE; 

strHost.Format (strHost
+",%d,%d",uLocalSock/256,uLocalSock%256
); 

strCommand 
= "PORT " +
 strHost ; 

strCommand 
+= " "


if (!(this->WriteStr (strCommand))) return
 FALSE; 

if (!(this->ReadStr ())) return
 FALSE; 

this->SetMessage (this->
m_strMsg ); 

}
 

//发送下载或上传命令 


if (bGet) 



strCommand 
= "RETR" +
 strRemoteFile; 

strCommand 
+= " "


}
 

else
 



strCommand 
= "STOR" +
 strRemoteFile; 

strCommand 
+= " "


}
 

if (!this->WriteStr (strCommand)) return
 FALSE; 

if
 (bPasv) 



if (!
(asListen.Create ()) ) 

return
 FALSE; 

asListen.Connect (strHost, uServSock); 

}
 

if (!this->ReadStr ()) return
 FALSE; 

this->SetMessage (this->
m_strMsg ); 

if (this->m_fc != "1"




this->SetMessage ("文件传输不成功!"
); 

return
 FALSE; 

}
 

if(!bPasv&&!sServ.Accept (asListen)) return
 FALSE; 

//数据传输 


DWORD lpArgument; 

if (!asListen.AsyncSelect ()||!asListen.IOCtl (FIONBIO,&lpArgument)) return FALSE; 

while(1




TRY 



if
 (bGet) 



if (!(num=asListen.Receive (cbuf,BUFSIZE,0
))) 

break


else
 

flDataFile.Write (cbuf,num); 

}
 

else
 



if (!(numread =
 flDataFile.Read (cbuf,BUFSIZE))) 

break


else
 

if (!(numsent = asListen.Send (cbuf,numread,0))) break


if (numread !=
 numsent) 

flDataFile.Seek (numsent 
-
 numread,CFile::current); 

pos 
+=
 numsent; 

m_pWnd 
->
SetPos(pos); 

}
 

}
 

CATCH(CException ,e) 



this->SetMessage ("数据传输过程中被中断!"
); 

return
 FALSE; 

}
 

END_CATCH 

}
 

asListen.Close (); 

flDataFile.Close (); 

if (!this->WriteStr (" ")) return
 FALSE; 

this->
ReadStr (); 

this->SetMessage (this->
m_strMsg ); 

return
 TRUE; 

}
 

 

 

参考资料:

1.Visual C ++ 网络通信协议分析与应用实现 汪晓平 钟军 人民邮电出版社

2. FTP 协议的分析和扩展 elly http://elly.blogdriver.com/index.jsp

3. RFC中 FTP 相关文档 http://www.ietf.org/rfc/

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值