290 likes | 457 Views
C# 网络编程技术教程. 第七章 FTP 编程. 学习目标. 了解 FTP 的相关基础知识。 掌握 .NET 中 FTP 的服务器端编程方法。 掌握 .NET 中 FTP 的客户端编程方法。. 本章内容. 7.1 FTP 概述 7.2 .NET 中的 FTP 编程. 7.1 FTP 概述.
E N D
C#网络编程技术教程 第七章 FTP编程
学习目标 • 了解FTP的相关基础知识。 • 掌握.NET中FTP的服务器端编程方法。 • 掌握.NET中FTP的客户端编程方法。
本章内容 7.1 FTP概述 7.2 .NET中的FTP编程
7.1 FTP概述 FTP(File Transfer Protocol,文件传输协议)是Internet中相当重要的应用之一。通过FTP,用户可从FTP服务器下载(Download)或上传(Upload)文件。FTP被整合在浏览器中,用户可利用浏览器通过超链接(Hyperlink),下载服务器开放的任意文件,达到资源共享的目的。 FTP是一种Client/Server体系结构,但与一般的Client/Server体系结构不同。与Socket网络程序应用的最大差别,在于其定义了服务器端与客户端的行为模式,即相关的指令、响应及传输的数据类型。因此在开发FTP应用程序时,应先了解FTP的详细内容。 FTP是由RFC959 File Transfer Protocol(FTP)所定义的。在此RFC中,强调了FTP的目的为: 文件共享。 通过应用程序直接或间接使用远程主机。 提供一致性的协议,避免用户在不同主机上产生不同的操作方式。 提供可靠及有效率的数据传输。
7.1 FTP概述 7.1.1 FTP结构 一般的Client/Server应用程序,只会在服务器端与客户端建立一个Socket连接,此连接同时处理服务器端与客户端间的连接以及数据的传输。而FTP则不同,服务器与客户端之间共建立两个Socket连接,一个以FTP默认端口21作为两者间的通信连接,另一个则用于客户端传输指令与服务器响应结果。FTP服务器端分为Protocol Interpreter(PI)及Data Transfer Process(DTP)两个模块。服务器端PI用来建立服务器Socket以监听并接收处理FTP客户端的连接请求,此连接会一直持续至客户端结束FTP应用程序为止。 FTP服务器端DTP专门处理FTP协议所定义的特殊指令,并将执行结果或信息返回至客户端。因此,DTP主要是负责传输数据(包括文件)。 FTP客户端分为Protocol Interpreter(PI)、Data Transfer Process(DTP)及User Interface(UI)三个模块。FTP客户端PI用来与服务器建立连接。FTP客户端DTP用来将用户输入的FTP指令传输至服务器端处理,并且接收服务器端的响应(包括文件的处理)。FTP客户端UI用来提供用户与PI间的交互界面。
7.1 FTP概述 7.1.2 FTP命令 除上述连接方式的特殊之外,FTP在命令与响应信息上也有其特殊的定义方式。FTP命令主要包括:访问命令、文件管理命令、数据格式命令、端口定义命令、文件传输命令和杂项命令。这些命令格式及描述分别见表7.1至表7.6所示。
7.1 FTP概述 7.1.2 FTP命令
7.1 FTP概述 7.1.2 FTP命令
7.1 FTP概述 7.1.2 FTP命令
7.1 FTP概述 7.1.3 FTP服务器响应码 RFC959对于FTP指令均明确定义其对应的FTP服务器响应信息,也称FTP服务器响应码,常用的FTP服务器响应码如表7.7所示。响应码由3位数字组成。其中第一位与第二位数字有其特殊的意义,分别如表7.8和表7.9所示。
7.1 FTP概述 7.1.3 FTP服务器响应码
7.1 FTP概述 7.1.3 FTP服务器响应码
7.1 FTP概述 7.1.3 FTP流程 FTP流程是指在FTP协议的基础上,客户端与服务器端完成一次完整的数据信息传输(文件下载或上传)的过程。FTP流程通常由以下三个部分构成。 (1)用户身份确认。 (2)数据传输。 (3)结束FTP。 下面以下载文件操作为例,详细说明上述三个部分的处理流程。 1.用户身份确认 如图7.2所示。首先,FTP客户端尝试与FTP服务器建立连接。命令格式如下。 C:\>ftp localhost 若成功,服务器发回响应成功信息。信息格式如下。 Connected to localhost. 220 FTP Server - <version 1.0> - ready. 同时服务器要求用户输入登录账号。格式如下。 User (localhost: (none)): 用户输入账号并按下Enter键后,客户端则传输“USER<username>”的信息至FTP服务器,此时服务器将再次发回响应信息,同时提示用户输入密码。格式如下: 331 Password required for<username> Password:
7.1 FTP概述 7.1.4 FTP流程 其中<username>为用户刚刚输入的账号。如:User (localhost: (none)):jh 331 Password required for jh Password: 用户输入密码并按下Enter键,客户端则传输“PASS<password>”信息至FTP服务器,并进行用户确认,若正确,服务器发回响应信息。格式如下。 230 User leo logged in. ftp> 至此,完成用户确认的流程。
7.1 FTP概述 7.1.4 FTP流程 2.数据传输 如图7.3所示,登录成功后,用户便可以输入相关的FTP指令至服务器。 例如要从FTP服务器下载文件download.txt。用户应在FTP提示符下输入下载命令,格式如下: ftp> download.txt 客户端首先会传输PORT h1,h2,h3,h4,p1,p2至服务器,以便以此IP地址及端口与服务器端建立DTP数据传输的连接;如果连接成功,服务器发回响应信息,格式如下: PORT 127,0,0,1,4,69 200 PORT command successful. 同时客户端会由DTP自动传输RETR download.txt指令至服务器,以便下载文件。当服务器端开始传输文件时,服务器会发回响应信息,格式如下: 150 Opening BINARY mode data connection for download.txt. 当下载成功后,服务器则发回如下信息: 226 Closing data connection. Transfer Complete. ftp: 15bytes received in 0.00Seconds 15000.00Kbytes/sec. 至此,数据传输结束。
7.1 FTP概述 7.1.4 FTP流程 3.结束FTP 流程如图7.4所示。当数据传输结束后,如不需要再进行其他文件传输,则FTP客户端发送QUIT命令,要求结束FTP连接。FTP服务器端则发回响应信息,表示结束连接。示例如下: ftp> quit 221 Server closing control connection. Goodbye. 图7.3数据传输流程 图7.4结束FTP流程
7.2 .NET中的FTP编程 文件传输系统服务器端和客户端的流程是:服务器端首先进行TCP监听,当有客户端连接请求时,将文件目录发送过去,客户端选择文件后,服务器构造一个文件流读取指定文件,然后将文件流赋给网络流发送到客户端,客户端接到网络流后,转化为文件流存放到文件。 7.2.1 数据发送和接收编程方法 1.数据发送编程方法 (1)当客户端输入了put命令后,会执行以下的代码向FTP服务器发送STOR命令,然后再通过sendData( )方法发送文件数据。如果接收成功,会收到226响应代码。要发送的文件数据通过readFileData( )方法读取。 (2)要发送的文件的数据通过readFileData( )方法获得,参数filename是要读取数据的文件名,返回值是从文件中读取的字节数据。 (3)获得文件数据后,通过sendData( )方法将其发送到FTP服务器。如果是PASV模式,就直接连接FTP服务器,然后发送数据;如果是PORT模式,就等待dataSocket为连接状态时发送。 2.数据接收编程方法 (1)当客户端输入了get命令后,会执行以下的代码从FTP服务器接收文件。如果接收成功,会收到了226响应代码,否则可能会收到550响应代码。接收文件数据通过receiveData( )函数完成,如果接收数据正确,返回值为文件内容,否则返回空字符串。如果接收到文件数据,就调用saveFileData( )方法进行保存。 (2)接收到文件数据后,通过saveFileData( )方法把数据写入文件中:参数filename是要保存的文件名,参数data是要写入的数据。代码首先检查文件是否存在,如果不存在就创建一个新文件。
7.2 .NET中的FTP编程 7.2.2 服务器端开发 1.界面设计 如图7.5所示,在窗体上拖放3个Label控件、1个TextBox控件、3个Button控件、2个RichTextBox控件、1个StatusBar控件。设置各个控件的属性如表7.10所示。
7.2 .NET中的FTP编程 7.2.2 服务器端开发 2.在代码窗口中添加引用 using System.Net; using System.Net.Sockets; using System.Threading; using System.IO; 3.在代码窗口中添加私有成员 private bool control=false; private int port; private System.Net.Sockets.Socket sock; private int number; private int i; private int j; private System.Net.Sockets.TcpListener listener; private System.Windows.Forms.StatusBarPanel statusBarPanel1; private System.IO.FileStream filestream; 4.修改Form1构造方法 在Form1窗口中,单击鼠标右键选择“查看代码”,进入代码窗口。 public Form1( ) { // Windows 窗体设计器支持所必需的 InitializeComponent( ); // // TODO: 在 InitializeComponent 调用后添加任何构造函数代码 string[ ] str=new string[1024]; for(int i=0;i<Directory.GetFiles("d:\\FTP虚拟目录").Length;i++) { str[i]=Directory.GetFiles("d:\\FTP虚拟目录")[i]; richTextBox1.AppendText(str[i]+"\r\n"); } } 图7.5服务器端窗口
7.2 .NET中的FTP编程 7.2.2 服务器端开发 5.“开始服务”按钮的Click事件代码 private void button1_Click(object sender, System.EventArgs e) { try { port=Int32.Parse(textBox1.Text); } catch{MessageBox.Show("您输入的格式不对!请输入正整数。");} try { listener=new TcpListener(port); listener.Start( ); statusBarPanel1.Text="开始监听………"; Thread thread =new Thread(new ThreadStart(receive)); thread.Start( ); } catch(Exception ex){MessageBox.Show(ex.Message);} } 6.Receive( )方法代码 private void receive( ) { sock=listener.AcceptSocket( ); if(sock.Connected) { statusBarPanel1.Text="与客户建立连接"; string str=richTextBox1.Text; byte[] bytee=System.Text.Encoding.BigEndianUnicode.GetBytes(str.ToChar Array( )); sock.Send(bytee,bytee.Length,0);
7.2 .NET中的FTP编程 7.2.2 服务器端开发 //接收信息 while(!control) { NetworkStream stream=new NetworkStream(sock); byte[ ] by=new Byte[1024]; int i=sock.Receive(by,by.Length,0); string ss=System.Text.Encoding.BigEndianUnicode.GetString(by); richTextBox2.AppendText(ss); j=richTextBox2.Lines.Length; if(j>=2) { if(richTextBox2.Lines[j-2].ToString( )!="@@@@@@") {//构造文件流 filestream=new FileStream(richTextBox2.Lines[j-2].ToString( ),FileMode.Open,FileAccess.Read); //定义缓冲区 byte[] b1=new byte[1024]; //循环读文件 while((number=filestream.Read(b1,0,1024))!=0) {//向客户端发送流 stream.Write(b1,0,number); //流刷新 stream.Flush( ); } //文件发送结束,发送"<EOF>",提示客户文件传输完毕。 string st="<EOF>"; byte[] b2=new byte[1024]; byt=System.Text.Encoding.ASCII.GetBytes(st.ToCharArray( )); sock.Send(b2,b2.Length,0); filestream.Close( ); }
7.2 .NET中的FTP编程 7.2.2 服务器端开发 else if(richTextBox2.Lines[j-2].ToString( )=="@@@@@@") { control=true; } } } } } 7.“关闭服务”按钮的Click事件代码 private void button2_Click(object sender, System.EventArgs e) { try { control=true; listener.Stop( ); statusBarPanel1.Text="停止监听"; } catch{MessageBox.Show("监听还未开始,关闭无效。");} } 8.“退出服务”按钮的Click事件代码 private void button3_Click(object sender, System.EventArgs e) { Application.Exit( ); }
7.2 .NET中的FTP编程 7.2.3 客户端开发 1.界面设计 如图7.6所示,在窗体上拖放5个Label控件、3个TextBox控件、3个Button控件、1个GroupBox控件、1个RichTextBox控件、1个StatusBar控件、1个ComboBox控件、1个saveFileDialog控件。设置各个控件的属性如表7.11所示。
7.2 .NET中的FTP编程 7.2.3 客户端开发 2.在代码窗口中添加引用 图7.6 客户端窗口 using System.Net; using System.Net.Sockets; using System.IO; using System.Threadingt; 3.在代码窗口中添加私有成员 private bool control=false; private TcpClient client; private int i; private NetworkStream netStream; private FileStream fileStream=null; private Stream stream=null; 图7.6客户端窗口
7.2 .NET中的FTP编程 7.2.3 客户端开发 4.“连接”按钮的Click事件代码 private void button1_Click(object sender, System.EventArgs e) { int port=0; IPAddress myIP=IPAddress.Parse("127.0.0.1"); try { myIP=IPAddress.Parse(textBox3.Text); } catch{MessageBox.Show("您输入的IP地址格式错误!");} client=new TcpClient( ); try { port=Int32.Parse(textBox2.Text); } catch{MessageBox.Show("请输入整数。");} try { if(textBox1.Text!=" "&&textBox3.Text==" ") { client.Connect(textBox1.Text,port); statusBarPanel1.Text="与服务器建立连接"; //获取网络流 netStream=client.GetStream( ); //读数据 byte[] b=new byte[6400]; i=netStream.Read(b,0,6400); string str=System.Text.Encoding.BigEndianUnicode.GetString(b);
7.2 .NET中的FTP编程 7.2.3 客户端开发 richTextBox1.AppendText(str); int j=richTextBox1.Lines.Length; for(int k=0;k<j-1;k++) { comboBox1.Items.Add(richTextBox1.Lines[k]); } comboBox1.Text=comboBox1.Items[0].ToString( ); } if(textBox3.Text!=""&&textBox1.Text=="") { client.Connect(myIP,port); statusBarPanel1.Text="与服务器建立连接"; netStream=client.GetStream( ); byte[] b=new byte[6400]; int i=netStream.Read(b,0,6400); string str=System.Text.Encoding.BigEndianUnicode.GetString(b); richTextBox1.AppendText(str); int j=richTextBox1.Lines.Length; for(int k=0;k<j-1;k++) { comboBox1.Items.Add(richTextBox1.Lines[k]); } comboBox1.Text=comboBox1.Items[0].ToString( ); }
7.2 .NET中的FTP编程 7.2.3 客户端开发 if(textBox3.Text!=""&&textBox1.Text!="") { client.Connect(myIP,port); statusBarPanel1.Text="与服务器建立连接"; netStream=client.GetStream( ); byte[] b=new byte[6400]; int i=netStream.Read(b,0,6400); string str=System.Text.Encoding.BigEndianUnicode.GetString(b); richTextBox1.AppendText(str); int j=richTextBox1.Lines.Length; for(int k=0;k<j-1;k++) { comboBox1.Items.Add(richTextBox1.Lines[k]); } comboBox1.Text=comboBox1.Items[0].ToString( ); } } catch(Exception ex){MessageBox.Show(ex.Message);} } 5.“下载”按钮的Click事件代码 private void button2_Click(object sender, System.EventArgs e) { control=false; if(saveFileDialog1.ShowDialog( )==DialogResult.OK) { //创建新的文件流 filestream=new FileStream(saveFileDialog1.FileName,FileMode.OpenOrCreate,FileAccess.Write); //获取服务器网络流 netStream=client.GetStream( ); string down=comboBox1.Text+"\r\n"; byte[] b=System.Text.Encoding.BigEndianUnicode.GetBytes(down.ToCharArray( ));
7.2 .NET中的FTP编程 7.2.3 客户端开发 //发送要下载的文件名给服务器 netStream.Write(b,0,b.Length); //流刷新 netStream.Flush( ); //启动接收文件的线程 Thread thread=new Thread(new ThreadStart(download)); thread.Start( ); } } 6.Download( )方法的定义 //此方法用于接收服务器传来的流(要下载的文件) private void download( ) { Stream stream=null; //获取网络流 stream=client.GetStream( ); int length=1024; byte[] b=new byte[1024]; int num=stream.Read(b,0,length); //读取网络流并写入文件 while(num>0) { //读取服务器流 string str=System.Text.Encoding.ASCII.GetString(b); int m=str.IndexOf("<EOF>"); if(m==-1) { filestream.Write(b,0,num); filestream.Flush( ); statusBarPanel1.Text="正在下载文件"; }
7.2 .NET中的FTP编程 7.2.3 客户端开发 else { filestream.Write(b,0,m); filestream.Flush( ); break; } } filestream.Close( ); MessageBox.Show("下载完毕!"); statusBarPanel1.Text="文件下载完毕"; } 7.“关闭”按钮的Click事件代码 private void button3_Click(object sender, System.EventArgs e) { try { netStream=client.GetStream( ); string end="@@@@@@"+"\r\n"; byte[] byt=System.Text.Encoding.BigEndianUnicode.GetBytes(end.ToCharArray( )); netStream.Write(byt,0,byt.Length); netStream.Flush( ); client.Close( ); statusBarPanel1.Text="与服务器断开连接"; } catch{} }