FTP协议解析与实现关键词:FTP、协议解析、visualC++简述:在学习FTP协议偶有感悟,于是想起了写一篇文章来总结一下自己学习的东西。同时也想与读者共同分享自己的劳动成果,也算是抛砖引玉吧。错误和不当之处欢迎指正。联系方式:lihualoveyou1983@163.com。本文主要对程序员关心的一些技术进行了总结。并结合一个实例来进一步说明。本文涉及内容主要是FTP原理、VC编程、Socket等。正文:一、FTP通信原理简述1.1FTP简介FTP是基于TCP/IP协议的一个应用协议。主要实现在不同的计算机之间的数据共享。FTP采用的是C/S模式。客户既可以下载文件也可以上传文件。当然,FTP给用户一定的权限。用户只能在权限下使用。目前,FTP的服务器种类很多,比如常用的SERV-U,客户端程序也很多,比如:CuteFTP。WINDOWS也提供了一个FTP客户程序。它们都根据相同的协议标准来设计的,具体协议内容可参考RFC文档。SERV-U工作界面windows提供的客户端1.2FTP工作原理FTP工作原理与其它的应用协议有些不同。它是用两个端口进行通信的。一个端口用于命令交互。这个端口在用户连接之后一直保持;而另一个端口只是在数据传时打开(比如:上传文件,下载文件,获取服务端文件列表),在数据传输时有两种不同的模式,一是用户开通这个数据端口,这种模式叫做主动模式;二是服务器提供一个接口,这个模式叫被动模式。FTP原理图1.3用户登录FTP服务器提供了用户的访问权限,有的服务器可以匿名登录,有的服务器要求用户使用密码登录。在每一个与登录有关的命令时,服务器都会有一个返回信息。下面显示了一个登录过程:1.4数据传输在FTP中可以定义数据的传输格式,比如:二进制(进行图象和应用程序传输这种格式)。下面是一个传输过程:二、FTP命令在WINDOWS中提供的命令不是FTP的标准命令。有些命令是许多命令的合集。而FTP标准命令,每发送一个,服务器就会做出一个相应的动作,并把认证信息发送给用户。具体的命令可以参照有关的资料三、实例在这里我们用一个FTP客户端来说明以上的知识。这里面主要是一个封装的类。CFTPClient这个类实现的文件的上传与下载并能获得服务端文件的信息。1.1CFTPClient类classCFTPClient{//成员变量private:CSocket*m_pSocket;CArchive*m_pRxarch;CArchive*m_pTxarch;CSocketFile*m_psfSokFile;CStringm_strMsg;//服务器发回的消息CStringm_fc;CftpclientDlg*m_pWnd;//用于对窗口的操作CByteArraym_btBuf;//成员函数public:CFTPClient(void);~CFTPClient(void);//发送命令到服务器BOOLFtpCommand(CStringstrCommand);//登录到FTP服务器,这个函数只支持在没有防火墙的时候BOOLLogOnToserver(CStringstrHostname,intnHostPort,CStringstrUserName,CStringstrPassword);//退出服务器voidLogOffServer();//上传下载文件BOOLMoveFile(CStringstrRemoteFile,CStringstrLocalFile,BOOLbPasv,BOOLbGet);//列出文件列表BOOLList();voidProcessList();//获取一行信息BOOLGetLine(intndx,CString&strLine);//发送数据BOOLWriteStr(CStringstrOutPut);//接收数据BOOLReadStr();//设置窗口voidSetWnd(CftpclientDlg*pWnd);//发送信息voidSetMessage(CStringstrMsg);//获取文件信息BOOLGetFtpFileInfo(intndx,FTP_FILE_INFO&ftpFileInfo);protected://读取服务器发送的信息BOOLReadStr2();//打开通道BOOLOpenControlChannel(CStringstrServerHost,intnServerPort);//关闭通道voidCloseControlChannel();};1.2登录函数I////////////////////////////////////////////////////////////函数:BOOLCFTPClient::LogOnToserver()////描述://这个函数用于登录到FTP服务器,在这个函数没有对系统的防火墙作//进一步分析,//读者可以进一步扩展它的功能//////参数://-strHostname登录的主机名//-nHostPort主机端口//-strUserName用户名//-strPassword用户密码//返回://-BOOL成功返回TRUE否则返回FALSE//////吴庆民2005.4.19///////////////////////////////////////////////////////BOOLCFTPClient::LogOnToserver(CStringstrHostname,intnHostPort,CStringstrUserName,CStringstrPassword){if(!this-OpenControlChannel(strHostname,21))returnFALSE;if(!this-ReadStr())returnFALSE;this-SetMessage(this-m_strMsg);//发送一个空消息CStringtemp;temp=USER+strUserName+\n\r;//发送用户名if(!this-WriteStr(temp)){returnFALSE;}if(!this-ReadStr())returnFALSE;this-SetMessage(this-m_strMsg);//发送密码temp=PASS+strPassword+\n\r;if(!this-WriteStr(temp))returnFALSE;if(!this-ReadStr())returnFALSE;this-SetMessage(this-m_strMsg);returnTRUE;}这个函数主要是联接服务器打开一个通道用于命令传输。这个通道是全双工的。1.3上传下载文件/////////////////////////////////////////////////////////////////////函数:BOOLCFTPClient::MoveFile()////描述://上传或下载文件,不支持多线程,可以在这个函数上进行一下扩展//////参数://-strRemoteFile远程文件名//-strLocalFile本地文件名//-bPasv是否为被动模式传输//-bGet是否为下载文件////返回://-BOOL成功返回TRUE否则返回FALSE//////吴庆民2005.4.19//////////////////////////////////////////////////////////////////////BOOLCFTPClient::MoveFile(CStringstrRemoteFile,CStringstrLocalFile,BOOLbPasv,BOOLbGet){CFileflDataFile;CStringstrCommand;intpos=0;UINTuServSock,uLocalSock;CStringstrHost;CSocketsServ;CAsyncSocketasListen;inti=0,j=0,num,numread,numsent;CStringstrTemp;constintBUFSIZE=4096;charcbuf[BUFSIZE];if(!flDataFile.Open(strLocalFile,(bGet?CFile::modeCreate|CFile::modeWrite:CFile::modeRead))){this-SetMessage(上传或下载的文件在本地不能打开!);returnFALSE;}//准备传输strCommand=TYPEI\n\r;if(!this-WriteStr(strCommand))returnFALSE;if(!this-ReadStr())returnFALSE;this-SetMessage(this-m_strMsg);if(bPasv){strCommand=PASV\n\r;if(!this-WriteStr(strCommand))returnFALSE;if(!this-ReadStr())returnFALSE;this-SetMessage(this-m_strMsg);//if((==-1&&(j=this-m_strMsg.Find(/)))==-1)returnFALSE;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);i=strTemp.ReverseFind(',');uServSock=atol(strTemp.Right(strTemp.GetLength()-(i+1)));strTemp=strTemp.Left(i);//this-SetMessage(strTemp);i=strTemp.ReverseFind(',');uServSock+=256*atol(strTemp.Right(strTemp.GetLength()-(i+1)));strHost=strTemp.Left(i);while(1){if((i=strHost.Find(','))==-1)break;strHost.SetAt(i,'.');}//this-SetMessage(strHost);//CStringtemp;//temp.Format(strHost+\n%d,uServSock);//this-SetMessage(temp);}else{if(!this-m_pSocket-GetSockName(strHost,uLocalSock))returnFALSE;while(1){if((i=strHost.Find(.))==-1)break;strHost.SetAt(i,',');}if(!(sServ.Create())||!(sServ.Listen()))returnFALSE;if(!sServ.GetSockName(strTemp,uLocalSock))returnFALSE;strHost.Format(strHost+,%d,%d,uLocalSock/256,uLocalSock%256);strCommand=PORT+strHost;strCommand+=\n\r;if(!(this-WriteStr(strCommand)))returnFALSE;if(!(this-ReadStr()))returnFALSE;this-SetMessage(this-m_strMsg);}//发送下载或上传命令if(bGet){strCommand=RETR+strRemoteFile;strCommand+=\n\r;}else{strCommand=STOR+s