收藏 分享(赏)

FTP服务器 详解+源代码.doc

上传人:11xg27ws 文档编号:7805799 上传时间:2019-05-26 格式:DOC 页数:44 大小:383.50KB
下载 相关 举报
FTP服务器 详解+源代码.doc_第1页
第1页 / 共44页
FTP服务器 详解+源代码.doc_第2页
第2页 / 共44页
FTP服务器 详解+源代码.doc_第3页
第3页 / 共44页
FTP服务器 详解+源代码.doc_第4页
第4页 / 共44页
FTP服务器 详解+源代码.doc_第5页
第5页 / 共44页
点击查看更多>>
资源描述

1、一个简单的 FTP 服务器实例目标FTP 是网 络上共享资源的常用方法,在本章中我 们将实现 一个简单的 FTP 服务器。本章知识点: FTP 协议 Socket 类和 TcpListener 类 System.Threading 名称空间5.1 实例功能本实例实现一个简单的 FTP 服务器,该服务器是一个控制台程序,编译后的可执行文件为 ftpd.exe,在控制台中键入 ftpd 后就可启动服务器,若要改变 ftp 服务器的工作目录,可以键入 ftpd r 后接绝对路径。服务器的 ftp 服务端口采用默认的 21。服务器启动后,用户就可从其他任何一台联网计算机进行访问。下面是应用的一个例子:

2、(服务器所在机器的 ip 为 166.111.147.25)用户在自己计算机的控制台中输入 ftp 166.111.147.25 回车后可以看到服务器传过来的欢迎信息,并要求输入登陆账号(图 5-1) 。图 5-1 登陆 ftp输入用户名和密码后(为简化起见我们在程序中省去了验证过程,任何人都可以登陆) ,用户的控制台如图 5-2 所示,在服务器上,也出现了该用户的登陆情况(图 5-3) 。第五章 FTP 服务器 131图 5-2 成功登陆 图 5-3 服务端接下来用户可以使用各种命令进行各种 ftp 操作,比如列出目录下所有文件和文件夹(ls ) ,下载指定的文件(get ) ,上载文件(p

3、ut )等等。下面是客户端(图 5-4)和服务端(图 5-5)某时刻的运行状态。图 5-4 客户端运行情况图 5-5 服务端运行情况5.2 编程思路要实现 FTP 服务器,我们必须对 FTP 协议有一定的了解,使用符合协议的指令集和网络传输方式,我们将在下一节详细介绍关于 FTP 协议的基础知识。另外,我们还采用了TcpListener 和 Socket 编程技术实现数据传输,所以这也是我们需要掌握的内容。最后,为了同时给多个用户提供服务,FTP 服务器还必须支持多线程。FTP 服务器程序的大框架是这样的:程序运行后,在服务器的某个端口有一个TcpListener 一直在监听用户的请求,当有用

4、户请求服务时,服务器立刻创建一个新的线程处理这个请求,我们称开始了一个新的会话。在会话中,服务器通过 Socket 接收用户命令,对命令进行分析后采取相应的操作,并将结果返回。一直到用户退出这个会话,服务器才销毁这个线程。服务器和客户端的会话方式有两种,一是被动方式(passive) ,即服务器在某个特定端口有一个 TcpListener 在不断监听用户命令;二是主动方式,这种情况下,服务器在该客户端有服务请求时,创建一个套接字和它进行数据传输。C#网络应用案例导航1325.3 关键技术FTP 协议1) 概述FTP 的目标是提高文件的共享性,提供非直接使用远程计算机,使存储介质对用户透明和可靠

5、高效地传送数据。图 5-6 是 FTP 服务示意图,为了让大家更好的理解,我们先解释一下相关的概念。(1)字节大小,在 FTP 中字节大小有两个:逻辑字节大小和用于传输的字节大小。后者通常是 8 位,而前者可不一定是多少了。传输字节不必等于逻辑字节大小,也不必对数据结构进行解释。 (2)控制连接是建立在 USER-PIT 和 SERVER-PI 之间用于交换命令与应答的通信链路。 ( 3)数据连接是传输数据的全双工连接。传输数据可以发生在服务器 DTP 和用户 DTP 之间也可以发生在两个服务器 DTP 之间。 (4)DTP:数据传输过程,DTP 建立和管理数据连接,可以是主动的也可以是被动的

6、。 (5)EOR 代表记录尾。 (6)NTV 代表网络虚拟终端。 (7 )NVFS 代表网络虚拟文件系统。 (8)FTP 可以传输非连续的文件,这些文件的一部分称为页。 (9)PI 代表协议解释器。 (10)服务器 DTP 代表一种传输过程,它通常处于 “主动 ”状态,它和侦听端口建立数据连接,它还可以为传输和存储设置参数,并根据PI 的指令传输数据。当然,DTP 也可以转入“被动”状态。 (11)服务器 FTP 进程,它是和用户 FTP 进程一起工作的,它由 PI 和 DTP 组成。至于用户 FTP 进程则是由 PI,DTP 和用户接口组成的。图 5-6 FTP 服务示意图注意:数据连接是双

7、向的,它不用整个时间都存在。上图中用户 PI 开始控制连接,控制连接与 Telnet 协议很象。在开始阶段,标准 FTP 命令由用户 PI 产生并通过控制连接传送到服务器进程。服务器 PI 向用户 PI 返回标准应答。FTP 命令指定数据连接参数和文件系统操作。用户 DTP 在特定数据端口侦听,服务器开始数据连接并以指定的参数开始数据传输。数据端口不必在开始 FTP 命令的机器上,但用户或用户 FTP 进程必须确定它在指定的数据端口上侦听。这个数据连接是全双工的。在另外一种情况下,用户或许希望在两个主机间传送文件,不是两个本地主机。用户第五章 FTP 服务器 133在两台主机间建立控制连接,然

8、后规划数据连接。用这种方式,控制信息由用户 PI 获得,但是数据在服务器 DTP 之间传送。图 5-7 就是一个例子:图 5-7协议要求数据传输在处理时打开控制连接。在完成 FTP 服务后由用户中止控制连接,而服务器具体操作。如果在未接收命令时关闭了控制连接,服务器也会关闭数据传输。数据传输功能数据连接只传输数据,控制连接传送命令和响应。几个命令是关于在主机间传输数据的,数据传输基本上独立于物理结构的,但是如果在压缩传输模式下流式传输与文件结构有关,文件的属性与表示类型有关。 数据表示与保存数据是在主机间的存储设置间传送的。因为两个系统的数据存储方式不同,因此需要对它进行转换,在传送文本时会有

9、对 ASCII 表示的问题,在进行二进制传送的时候,会有不同系统对字节长度规定不同的问题,有的系统是 7 位,有的系统可能是 32 位,这也需要进行转换。需要提供数据表示与传输模型函数,但是 FTP 提供这方面的功能不多,超过FTP 提供功能的那一部分要用户自己实现。数据表示是由用户指定的表示类型,它可以是隐含的,也可以是用户指定的。请一定注意:逻辑字节长度与物理字节长度是不同的。ASCII 类型:这是所有 FTP 必须实现的默认类型,用于传送文本文件,当在主机间使用 EBCDIC 传送时更方便,则不使用 ASCII 类型。发送方将内部表示转换为 NVT-ASCII 格式,接收方则进行相反的过

10、程接收数据。根据 NVT 标准,要在行结束处使用 序列。NVT-ASCII是 8 位的。ASCII 和 EBCDIC 的格式参数在下面讨论。EBCDIC 类型:它是作为 ASCII 的另一种方法在主机间传送数据的数据类型。EBCDIC 和 ASCII 很象,仅在类型的功能描述上有一些差别。行结束符使用很少。图象类型:在此类型下传送的数据被看作连续的位,发送方将数据打包到 8 位传输字节中传送。因为结构的需要要对传送数据进行填充,填充字节全部为 0,填充必须在文件结构时使用,而且要标记出以便接收方过滤掉。它用于传送二进制数据和有效地传送和存储文件,因此所有 FTP 也必须实现。C#网络应用案例导

11、航134本地类型:也可以以十进制指定逻辑字节大小。如果物理字节大小和逻辑字节大小不同,直接将物理数据打包为逻辑字节,不用什么填充。接收方根据逻辑字节大小进行和本机的存储特点进行转换。传输必须是可重复的,也就是说,相同的文件相同的参数,那内容必须是一样的。数据结构除了有不同的数据类型外,FTP 还允许有不同的文件结构,下面是三种文件结构:文件式结构:文件中没有内部结构,文件被看作是二进制流;记录结构:文件是由一系列记录组成的;页结构:文件是由不同的索引页组成的。如果未使用 STRU 命令,文件结构是默认值。文件的结构会影响传输模型,存储和数据表示。文件本来的属性和保存它的主机有关,不同的机器会以

12、自己的方式保存文件。在不同主机间传送文件时必须使主机能够识别相互的表示。有些主机上的文件是面向字节的,有些是面向记录的,在传送时就会出现问题。那就要在接收方进行内部转换。在进行转换的时候,需要区别记录的边界,在 ASCII 中使用 ,在 EBCDIC 中使用作为分隔符。采用这种实现方法的必须保证转换是可逆的。文件结构:如果未使用 STRU 命令,文件结构是默认值。文件结构中没有默认值,文件被看作是连续的字节串。记录结构:对于文本文件,记录结构必须是所有 FTP 实现必须有的。记录结构文件是由连续的记录构成的。页结构:文件是非连续时使用页结构。这种文件称为随机访问文件。这些文件中有时会的和文件整

13、体或部分相关的信息出现。在 FTP 中,文件的一个部分称为页。建立数据连接传送数据机制包括建立连接选择数据参数。用户和服务器 DTP 有默认数据端口。用户进程默认数据端口和控制连接端口相同。服务器进程默认数据端口和控制连接端口相邻。传输字节大小是 8 位字节。此字节是实际传输字节,但不代表主机内的数据表示。被动数据传输进程在数据端口接收数据,FTP 请求命令决定数据传输的方向。服务器在接收到请求以后,将初始化端口的数据连接。当连接建立后,传输在 DTP 之间传送,服务器 PI 对用户 PI 返回应答。FTP 实现运行一个默认数据端口,用户 PI 才能改变默认端口。通过 PORT 命令可能改变端

14、口,用户可能希望数据在第三方主机上进行其它操作,用户 PI 需要在两个服务器 PI 上建立连接。一个服务器被告知侦听另一服务器的请求。用户 PI通过 PORT 命令通知另一服务器的数据端口。最后双方发送相应的传送命令。通常,服务器负责支持数据连接,初始化并关闭它,除非用户 DTP 在传输模式下要求关闭连接。服务器在下面情况下关闭数据连接: 服务器结束发送数据,通过 EOF 要求中止传送; 用户发送 ABORT 命令; 用户改变端口; 控制连接关闭;第五章 FTP 服务器 135 发生不可恢复错误。数据连接管理默认数据连接端口:所有 FTP 必须支持默认数据连接,只有用户 PI 能够初始化非默认

15、端口的使用。确定非默认数据端口:用户 PI 可以使用 PORT 命令指定非默认端口,它要求服务器方以 PASV 确定非默认数据端口。连接是由双方地址确定的,因此改变一方地址就改变了连接。数据连接的重用:在使用流式数据传输模型时,文件结束通过关闭连接指示。如果要传送多个文件时就会出麻烦,解决的方法有两个,一个是确定非默认端口,另一个是使用另一种传输模式。就传输模式而言,流传输模式是不安全的,因此无法确定连接是暂时还是永久关闭。其它传输模式不通过关闭连接表示文件结构,它们可以通过 FTP 命令决定传送结构。因此使用这些传输模式可以在保持连接的情况下传送多个文件。传输模式有三种传输模式:一种将数据格

16、式化并考虑重新开始过程;一种压缩数据;一种是不经过处理(少量处理)传送。所有数据传输必须以一个 EOF 结束,它可以显式给出,也可以通过关闭连接隐式给出。对于记录文件,所有 EOR 是显式的,包括最后一个记录。对于以页结构传送的文件,使用“最后一页”表示结束。从这里开始,下文中我们提到的字节指的是“传输字节”。为了进行标准化传送,传送主机必须把行结束或记录结束的内部表示转化为传输模式和文件结构指定的形式传送,接收方则进行相反的工作。IBM 大型机的记录计数域可能不能为其它主机识别,所以记录结束标记在流模式下以双字节控制码传送,在块或压缩模式下以标记位传送。而 ASCII 或 EBCDIC 的行

17、结束则则或指示。这样的转换需要时间,所以相同的系统在传送文本文件时采用二进制或流表示比较合适。下面是 FTP 定义的传输模式:流模式:数据以字节流的形式传送。使用的表示类型没有限制,允许记录结构。在记录结构文件 EOR 和 EOF 表示为双字节控制码。第一字节全为 0,后一字节为转义字符。当第二位值为 1 时表示 EOR,为 2 时表示 EOF,如果要同时表示 EOR 和 EOF,值为 3。全 1 字节作为数据发送时必须使用双字节传送,其中数据保存在第二个字节内。如果是文件结构,通过发送方关闭连接表示 EOF,接收到的所有数据就是文件内容。块模式:文件以块形式传送,块带有自己的头部分。头字节包

18、括计数域和描述子代码。关于 FTP 协议的基本知识我们先介绍这些,在以后的代码分析中我们将结合实例,做更深入的讨论。基于 TCP 协议的网络通讯TCP 协议是一个基本的网络协议,基本上所有的网络服务都是基于 TCP 协议的,FTP也是,所以必须了解基于 TCP 协议的编程。然而 TCP 协议是一个庞杂的体系,要彻底的弄清楚它的实现不是这么容易,不过在.NET Framework 环境下,我们不必要去追究 TCP 协议底层的实现,一样可以很方便的编写出基于 TCP 协议进行网络通讯的程序。C#网络应用案例导航136在这章里,我们主要通过两个类编写 FTP 服务程序,这两个类是: TcpListe

19、ner 类和Socket 类,它们都属于 System.Net.Sockets 名称空间。 Socket 类简介Socket(套接字)是通信的基石,是支持 TCP/IP 协议的网络通信的基本操作单元。可以将 Socket 看作不同主机间的进程进行双向通信的端点,它构成了单个主机内及整个网络间的编程界面。Socket 存在于通信域中,通信域是为了处理一般的线程通过 Socket 通信而引进的一种抽象概念。Socket 通常和同一个域中的 Socket 交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序) 。各种进程使用这个相同的域互相之间用Internet 协议簇来进行通信。 S

20、ocket 可以根据通信性质分类,这种性质对于用户是可见的。应用程序一般仅在同一类的 Socket 间进行通信。不过只要底层的通信协议允许,不同类型的 Socket 间也照样可以通信。Socket 有两种不同的类型:流 Socket 和数据报 Socket。Socket 工作原理:要通过互联网进行通信,你至少需要一对 Socket,其中一个运行于客户机端,我们称之为 ClientSocket,另一个运行于服务器端,我们称之为 ServerSocket。根据连接启动的方式以及本地 Socket 要连接的目标,Socket 之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。 所谓服

21、务器监听,是服务器端 Socket 并不定位具体的客户端 Socket,而是处于等待连接的状态,实时监控网络状态。所谓客户端请求,是指由客户端的 Socket 提出连接请求,要连接的目标是服务器端的Socket。为此,客户端的 Socket 必须首先描述它要连接的服务器的 Socket,指出服务器端Socket 的地址和端口号,然后就向服务器端 Socket 提出连接请求。所谓连接确认,是指当服务器端 Socket 监听到或者说接收到客户端 Socket 的连接请求,它就响应客户端 Socket 的请求,建立一个新的线程,把服务器端 Socket 的描述发给客户端,一旦客户端确认了此描述,连接

22、就建立好了。而服务器端 Socket 继续处于监听状态,继续接收其他客户端 Socket 的连接请求。 TcpListener 和 TcpClientTcpClient 类是基于 TCP 协议的客户端类,而 TcpListener 是服务器端,监听(Listen)客户端传来的连接请求。TcpClient 类通过 TCP 协议与服务器进行通讯并获取信息,它的内部封装了一个 Socket 类的实例,这个 Socket 对象被用来使用 TCP 协议向服务器请求和获取数据。因为与远程主机的交互是以数据流的形式出现的,所以传输的数据可以使用.net framework 中流处理技术读写。多线程操作系统使

23、用进程将它们正在执行的不同应用程序分开。线程是操作系统分配处理器时间的基本单元,并且该进程中可以有多个线程同时执行代码。每个线程都维护异常处理程序、调度优先级和一组系统用于在调度该线程前保存线程上下文的结构。线程上下文包括为使线程在线程的宿主进程地址空间中无缝地继续执行所需的所有信息,包括线程的 CPU 寄存器组和堆栈。.NET 框架将操作系统进程进一步细分为由 System.AppDomain 表示的称为应用程序域第五章 FTP 服务器 137的轻量托管子进程。一个或多个托管线程(由 System.Threading.Thread 表示)可以在同一个非托管进程中的一个或任意数目的应用程序域中

24、运行。虽然每个应用程序域都是用单个线程启动的,但该应用程序域中的代码可以创建附加应用程序域和附加线程。其结果是托管线程可以在同一个非托管进程中的应用程序域之间自由移动。支持抢先多任务处理的操作系统可以创建多个进程中的多个线程同时执行的效果。它通过以下方式实现这一点:在需要处理器时间的线程之间分割可用处理器时间,并轮流为每个线程分配处理器时间片。当前执行的线程在其时间片结束时被挂起,而另一个线程继续运行。当系统从一个线程切换到另一个线程时,它将保存被抢先的线程的线程上下文,并重新加载线程队列中下一个线程的已保存线程上下文。时间片的长度取决于操作系统和处理器。由于每个时间片都很小,因此即使只有一个

25、处理器,多个线程看起来似乎是在同时执行。要提高对用户的响应速度并且处理所需数据以便几乎同时完成工作,使用多个线程是一种最为强大的技术。在具有一个处理器的计算机上,多个线程可以通过利用用户事件之间很小的时间段在后台处理数据来达到这种效果。例如,在另一个线程正在重新计算同一应用程序中的电子表格的其他部分时,用户可以编辑该电子表格。在 C#中,System.Threading 名称空间提供了对多线程编程的支持,ThreadPool 类可以管理同时存在的一组线程,Mutex 类解决线程同步的问题,还有其它的一些类,可以解决多线程编程中遇到的各种问题,像死锁(两个线程都停止响应,并且都在等待对方完成)和

26、争用条件(由于意外地出现对两个事件的执行时间的临界依赖性而发生反常的结果)等等。5.4 实现步骤5.4.1 步骤 1-建立程序框架本实例是控制台程序,在 File-New-Project 内选择建立 C# Cosole Application, 名为ftpd,如图 5-8 所示。工程建立之后,在 Solution Explorer 中将系统为我们建立的默认类文件 Class1.cs 改名为 ftpd.cs, 并把该文件中原来的所有内容删掉。建立我们自己的名称空间,添加引用:namespace FTPD.NETusing System;using System.IO;using System.N

27、et;using System.Net.Sockets;using System.Threading;在这个例子中,我们要创建的类有:主程序类 MainApp,负责启动服务器,是程序运行的入口;Ftpd 类,创建服务线程,处理用户请求,最主要的工作在这里完成。 SessionInfo类,用于管理会话;另外我们还有一个 PassiveInfo 结构,用于管理被动模式下的连接设置。后面两个数据类型非常简单,只含有若干数据项,而没有任何方法。C#网络应用案例导航138图 5-8 创建工程5.4.2 步骤 2-实现 MainApp步骤描述启动 FTP 服务器,用户可以设置 FTP 服务的目录。功能实现

28、在 ftpd.cs 文件中加入下面的代码:public class MainApppublic const String Version = “0.0.1“;public static String szFtpRoot = “E:incoming“;public static bool fDebug = true;/服务器管理界面:public static void Usage( )Console.WriteLine(“ftpd usage:“);Console.WriteLine();/改变ftp目录Console.WriteLine(“-r patht:tSpecifies FTP Ro

29、ot“);/帮助Console.WriteLine(“-? t:tPrints this help“);return;public static int Main( String Arguments )for( int i = 0; i 0)if(si.fBinary)bw.Write(ReceivedBytes);elsefor(int i = 0; i -1)sBinarySocket.Send(br.ReadBytes(1024);br.Close();pStream.Close();sBinarySocket.Close();elseReply(sSocket, 150, “Openi

30、ng ASCII mode data connection for “ + Parameter + “);StreamReader sr = new StreamReader(pStream);if(si.fPassive)SendOverPassiveDataConnection(GetBytes(sr.ReadToEnd(), si);elseSendOverDataConnection(GetBytes(sr.ReadToEnd();sr.Close();第五章 FTP 服务器 157catch(Exception Ex)DBG_TRACE(Ex.Message);Reply(sSock

31、et, 200, “Transfer Complete“);return;/用户指定端口,用于主动传输模式public void ProcessPortCommand(Socket sSocket, String Parameter)/ PORT command -/ PORT is used to denote the Data Port the client wishes to recieve/ data on. We have to send all data requests back to the client via this/ port.String tmp = Paramete

32、r.Split( , );String szIP = “ + tmp0 + “.“ + tmp1 + “.“ + tmp2 + “.“ + tmp3;int iPortNum = (int.Parse(tmp4) 8) | int.Parse(tmp5);Random iRand = new Random();Int32 iPort = iRand.Next(0,30000);IPEndPoint LocalEndPoint = new IPEndPoint( Dns.GetHostByAddress( “127.0.0.1“ ).AddressList0, iPort );IPEndPoin

33、t RemoteEndPoint = new IPEndPoint( Dns.GetHostByAddress(szIP).AddressList0, iPortNum );localEP = LocalEndPoint;remoteEP = RemoteEndPoint;Reply(sSocket, 200, “PORT Command successful.“);return;/身份验证public void AuthenticateUser(String Username, String Password, Socket sSocket)/这里可以添加验证用户密码的代码,现在我们让所有人

34、都可以进入:Reply(sSocket, 230, “Login Successful.“);return;代码分析 用户的命令为“PASV”时,表示用户希望用被动模式进行数据连接,我们调用ProcessPassiveCommand( Socket sSocket, String Parameter, SessionInfo si )来实现,第一个参数为控制连接的 Socket 对象,第二个参数一般为空,第三个参数是会话对象。在这个函数里,我们在服务端创建了一个 TcpListener 对象,对用户的数据连接请求进行监听,这个 TcpListener 对象的端口是由两个随机数通过计算得到的,一

35、般不会有重复。在下面代码里我们通过 DNS 类获得主机地址:IPHostEntry LocalHostEntry = Dns.GetHostByName(Dns.GetHostName();DNS 类属于 System.Net 名称空间,提供了一系列通过域名系统获得主机信息的方法。解析出来的主机信息被放在一个 IPHostEntry 类对象返回,如果一个域名对应了多个 IP 地址,C#网络应用案例导航158则 IPHostEntry 对象包含所有的 IP 地址的列表。实际上,IPHostEntry 类有一个称为AddressList 的属性,是一个 IPAddress 类型的数组,用以存放所有

36、的 IP 信息。C#用 IPAddress 类来存储 IP 地址信息,IPAddress 类的构造函数原型是:Serializablepublic IPAddress(long newAddress);newAddress 为长整型表示的 IP 地址。IPAddress 类的主要方法有: ToString() 方法返回表示 IP 地址的以点号分隔的字符串,如“127.0.0.1” 。 IPAddress Parse(string ipString) 方法通过一个字符串表示的 IP 地址创建一个 IPAddress对象。DNS 类的主要方法有: GetHostByAddress:根据 IP 地址

37、返回 IPHostEntry 对象,它的参数可以是一个IPAddress 对象,也可以是一个表示 IP 地址的字符串。 GetHostByName:根据 DNS 名称返回 IPHostEntry 对象,唯一的参数是表示 DNS 的字符串。 GetHostName:返回本机的 DNS 名称。 Resolve:根据字符串表示的 DNS 名称或者 IP 地址返回 IPHostEntry 对象。服务器将 TcpListener 的 IP 地址和得到端口的两个随机数通过控制连接传给了用户,应答码为 227,表示进入动态模式。当用户希望进行数据连接时,他就可以创建 Socket 对象,对这个 TcpLis

38、tener 提出连接请求,而服务器立即使用用户创建的 Socket 对象进行数据连接。我们将被动模式的信息放在了会话对象 si 内:si.pi.iphostentry = LocalHostEntry;si.pi.iPort = iPort;si.pi.tcpListener = tcpListener;si.fPassive = true; 目录下所有文件和子目录列表用户命令为“LIST”时,服务器将给用户传输当前目录下所有文件和子目录的列表,我们通过函数 ProcessListCommand(Socket sSocket, String Parameter, SessionInfo si)

39、来实现,第一个参数为控制连接 Socket,第二个参数一般为空,第三个参数为会话对象。在函数体内,我们通过:String DirList = Directory.GetDirectories(si.szFtpRoot);for(int i = 0; i DirList.Length; i+ )DirectoryInfo tmp = new DirectoryInfo(DirListi);szDirList += tmp.Name;szDirList += “rn“;获得当前目录下所有子目录的名字。接着,我们通过:DirectoryInfo curDir = new DirectoryInfo(

40、si.szFtpRoot);FileInfo szFileListArray = curDir.GetFiles();for(int i = 0; i szFileListArray.Length; i+)szFileList += szFileListArrayi.Name;szFileList += “rn“;第五章 FTP 服务器 159获得目录下所有文件的名字。实际上,我们只用 Directory 类就可以实现同样的功能:String FileName=Directiory.GetFiles(si.szFtpRoot);For(int I=0;IFileName.Length;I+)s

41、zFileList+=FileName;szFileList+=”rn”;在函数的后半部分,我们根据数据连接是主动还是被动模式,选择传输字符串的函数,主动模式下的服务器给用户传输数据的函数是:public bool SendOverDataConnection(Byte pBytes)所谓“主动” ,是指由服务端初始化数据连接,在进行主动模式连接之前,用户一般会先发出“PORT ”命令,设置主动连接的本地和远程地址,设置过程是在函数ProcessPortCommand 内完成的。public void ProcessPortCommand(Socket sSocket, String Para

42、meter)第一个参数是控制连接 Socket,第二个参数是远程 IP 地址和端口,需要注意的是端口并不是直接给出,而是给出两个数,实际端口号需要经过某个运算得到。而本地端口是随机选择的:Random iRand = new Random();Int32 iPort = iRand.Next(0,30000);最后,我们把本地地址端口和远程地址端口分别放到 Ftpd 类的成员变量 localEP 和remoteEP 中。回到 SendOverDataConnection 函数,服务端创建一个 Socket 对象后,将它和 localEP 绑定在一起,连接 remoteEP 所指的用户端口,成功

43、后开始发送数据。如果连接是被动模式,则采用 SendOverPassiveDataConnection 函数方法:public bool SendOverPassiveDataConnection(Byte pBytes, SessionInfo si)该方法通过原先设定的 TcpListener 对象,获得本次数据连接的 Socket 对象,将数据发送出去:Socket s1 = si.pi.tcpListener.AcceptSocket();s1.Send(pBytes);s1.Close(); 处理用户上载当用户需要将文件上载到服务器时,用户使用命令“STOR” ,后接上载后在服务器上的文件名。我们调用函数 ProcessStoreCommand 来处理。数据连接依然分主动和被动两种模式:Socket s1 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);if(si.fPassive)sBinarySocket = si.pi.tcpListener.AcceptSocket();elsesBinarySocket = s1;sBinarySocket.Bind(localEP);sBinarySocket.Connect(remoteEP);

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报