65.9K
CodeProject 正在变化。 阅读更多。
Home

文件传输协议 (FTP) 客户端

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (20投票s)

2011 年 12 月 1 日

CPOL

7分钟阅读

viewsIcon

285246

downloadIcon

26793

连接到 FTP 服务器,并下载、上传、重命名和删除文件或目录。

引言

我知道互联网上有很多 FTP 客户端程序。但我们也需要了解 FTP(文件传输协议)的底层结构。因此,这个开源项目将引导您学习 FTP。这个程序的界面看起来像 FileZilla。FileZilla 非常流行,但它也存在一些 bug。这一切都始于我想打开我的博客。我需要通过 FTP 连接到我的服务器,发送文件、下载文件等。于是我决定编写自己的应用程序来处理所有这些。FileZilla 已经足够好,但它不是我写的。

背景

让我们看看我们知道什么。我们知道 FTP 是一种标准的基于 TCP 的网络协议,用于在不同主机之间传输文件。它是一种客户端-服务器架构。

ftp-hosting5.gif

FTP 程序是基于命令行实现的。我们仍然可以使用“cmd.exe”连接到 FTP 服务器,因为 FTP 使用命令。例如,要发送文件,我们可以从命令行使用“stor”。为了做到这一切,FTP 服务器需要正在运行并等待传入的请求。我们可以从 Wikipedia 上更好地理解 FTP:“客户端计算机能够与服务器在端口 21 上通信,这被称为控制连接。它在整个会话期间保持打开状态,第二连接称为数据连接,根据需要由服务器从其端口 20 打开到协商的客户端端口(主动模式),或由客户端从任意端口打开到协商的服务器端口(被动模式)来传输文件数据。控制连接用于会话管理(即,在客户端和服务器之间使用类似 Telnet 的协议交换命令、身份验证和密码)。例如,“RETR 文件名”会将指定的文件从服务器传输到客户端。由于这种双端口结构,FTP 被认为是带外协议,而 HTTP 等协议是带内协议。”

embosip_FTP_Active.gif

“服务器在控制连接上以三位数的 ASCII 状态码和可选的文本消息进行响应,例如“200”(或“200 OK.”)表示最后一个命令已成功。数字代表状态码,可选文本代表解释(例如,)或所需的参数(例如,<存储文件需要账户>)。“那么我们需要做什么?这非常清楚。发送命令,接收“OK”命令,发送数据,接收数据,就是这样。但首先我们应该准备服务器。FTP 服务器可以以“主动”或“被动”模式运行。主动模式是基于服务器的连接,而被动模式是基于客户端的连接。我们来看看更多。”

在主动连接中,客户端将 IP 和端口发送给服务器,然后服务器会尝试连接到客户端。但这可能会被客户端阻止,因为防火墙。我们都在使用杀毒软件或 Windows 防火墙,对吧?现在让我们看看被动模式。

在被动连接中,服务器通过“PASV”命令将其 IP 和端口发送给客户端,然后客户端可以尝试使用此 IP 连接到服务器。这是发送文件的非常实用的方式。当我们尝试发送文件时,我们应该首先使用“PASV”模式。如您所知,大多数协议,如 FTP、HTTP,在尝试获取内容时都使用 ASCII 字符,因为它是一种全局模式。因为它是一种全局模式,所以我们将使用这种模式。您可以从此 URL 获取 FTP 命令列表:http://en.wikipedia.org/wiki/File_Transfer_Protocol

使用代码

现在我们已准备好准备我们的应用程序。让我们编写一些有用的代码 :) 首先,我们需要一个“打开文件对话框”,但要集成到我们的窗体中。

文件浏览器组件

我们需要一个文件浏览器组件,以便在应用程序的界面中查看要发送到 FTP 服务器的文件。打开一个类型为“Windows Forms 用户控件库”的新项目。

Component.png

它会看起来像这样。现在我们需要一个树视图。几个按钮。以及一个搜索功能。

TreeView.Nodes.Clear();
TreeNode nodeD = new TreeNode();
nodeD.Tag = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
nodeD.Text = "Desktop";
nodeD.ImageIndex = 10;
nodeD.SelectedImageIndex = 10;
TreeView.Nodes.Add(nodeD);

如代码所示,我们应该首先添加主节点,“我的文档”、“我的电脑”等。然后获取子目录。

string[] dirList;
dirList = Directory.GetDirectories(parentNode.Tag.ToString());
Array.Sort(dirList);
if (dirList.Length == parentNode.Nodes.Count)
    return;
for (int i = 0; i < dirList.Length; i++)
{
    node = new TreeNode();
    node.Tag = dirList[i]; 
    node.Text = dirList[i].Substring(dirList[i].LastIndexOf(@"\") + 1);
    node.ImageIndex = 1;
    parentNode.Nodes.Add(node);
}

您可以从上面的链接获取完整的代码。我们应该跟踪鼠标实现的单击和按下事件。

现在我们有了一个文件浏览器,以及所有关于 FTP 和 Visual Studio 的必要信息。

首先,我们需要连接到服务器。我们应该怎么做?

FTPSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
AppendText(rchLog,"Status : Resolving IP Address\n",Color.Red);
remoteAddress = Dns.GetHostEntry(Server).AddressList[0];
AppendText(rchLog, "Status : IP Address Found ->" + remoteAddress.ToString() + "\n", Color.Red);
addrEndPoint = new IPEndPoint(remoteAddress, Port);
AppendText(rchLog,"Status : EndPoint Found ->" + addrEndPoint.ToString() + "\n", Color.Red);
FTPSocket.Connect(addrEndPoint);

是的,我们需要一个打开的套接字,然后连接到我们的服务器。现在我们可以发送我们的命令了。

AppendText(rchLog, "Command : " + msg + "\n", Color.Blue);
Byte[] CommandBytes = Encoding.ASCII.GetBytes((msg + "\r\n").ToCharArray());
FTPSocket.Send(CommandBytes, CommandBytes.Length, 0);
//read Response
ReadResponse();

我们向服务器发送命令,它会响应,但使用它自己的语言。我们需要理解它。响应包含一个三位数字的代码和一个解释。

private string SplitResponse()
{
    try
    {
        while (true)
        {
            Bytes = FTPSocket.Receive(Buffer, Buffer.Length, 0); //Number Of Bytes (Count)
            StatusMessage += Encoding.ASCII.GetString(Buffer, 0, Bytes); //Convert to String
            if (Bytes < Buffer.Length)  //End Of Response
                break;
        }
        string[] msg = StatusMessage.Split('\n');
        if (StatusMessage.Length > 2)
            StatusMessage = msg[msg.Length - 2];  //Remove Last \n
        else
            StatusMessage = msg[0];
        if (!StatusMessage.Substring(3, 1).Equals(" "))
            return SplitResponse();
        for (int i = 0; i < msg.Length - 1; i++)
            AppendText(rchLog, "Response : " + msg[i] + "\n", Color.Green);
        return StatusMessage;
    }
    catch(Exception ex)
    {
        AppendText(rchLog, "Status : ERROR. " +ex.Message+ "\n", Color.Red);
        FTPSocket.Close();
        return "";
    }
}

是的,就是这样。现在我们可以下载、上传、重命名或删除所有内容了 :)

FTP 命令列表

为了方便参考,我在此附上 Wikipedia 的命令列表。

命令 RFC 描述
ABOR 中止一个活动的文件传输
ACCT 账户信息
ADAT RFC 2228 身份验证/安全数据
ALLO 分配足够的磁盘空间来接收文件
APPE Append
AUTH RFC 2228 身份验证/安全机制
CCC RFC 2228 清除命令通道
CDUP RFC 959 切换到父目录
CONF RFC 2228 保密保护命令
CWD RFC 697 更改工作目录
DELE 删除文件
ENC RFC 2228 隐私保护通道
EPRT RFC 2428 指定服务器应连接的扩展地址和端口
EPSV RFC 2428 进入扩展被动模式
FEAT RFC 2389 获取服务器实现的特性列表
HELP 帮助
LANG RFC 2640 语言协商
LIST 返回指定文件或目录的信息,如果没有指定,则返回当前工作目录的信息。
LPRT RFC 1639 指定服务器应连接的长地址和端口
LPSV RFC 1639 进入长被动模式
MDTM RFC 3659 返回指定文件的最后修改时间
MIC RFC 2228 完整性保护命令
MKD RFC 959 创建目录
MLSD RFC 3659 列出指定目录的内容,如果指定了目录。
MLST RFC 3659 提供关于命令行的确切对象的数据,而没有其他对象。
MODE 设置传输模式(流、块或压缩)
NLST 返回指定目录中文件名的列表。
NOOP 无操作(哑包;主要用于保持连接)
OPTS RFC 2389 为特性选择选项
PASS 身份验证密码
PASV 进入被动模式
PBSZ RFC 2228 保护缓冲区大小
PORT 指定服务器应连接的地址和端口
PROT RFC 2228 数据通道保护级别
PWD RFC 959 打印工作目录。返回主机的当前目录。
QUIT 断开连接
REIN 重新初始化连接
REST RFC 3659 从指定点重新启动传输
RETR 传输文件副本
RMD RFC 959 删除目录
RNFR 重命名自
RNTO 重命名为
SITE 向远程服务器发送特定站点命令
大小 RFC 3659 返回文件大小
SMNT RFC 959 挂载文件结构
STAT 返回当前状态
STOR 接受数据并将数据作为文件存储在服务器端
STOU RFC 959 唯一存储文件
STRU 设置文件传输结构
SYST RFC 959 返回系统类型
TYPE 设置传输模式(ASCII/Binary
USER 身份验证用户名
XCUP RFC 775 切换到当前工作目录的父目录
XMKD RFC 775 创建目录
XPWD RFC 775 打印当前工作目录
XRCP RFC 743
XRMD RFC 775 删除目录
XRSQ RFC 743
XSEM RFC 737 发送,如果无法发送则通过邮件发送
XSEN RFC 737 发送到终端

简短的响应代码列表

  • 2xx - 成功响应
  • 4xx 或 5xx - 失败响应
  • 1xx 或 3xx - 错误或不完整的响应

第二个数字定义了错误的类型

  • x0z - 语法 - 这些响应与语法错误有关。
  • x1z - 信息 - 对信息请求的响应。
  • x2z - 连接 - 响应与控制和数据连接有关。
  • x3z - 身份验证和记账 - 登录过程和记账程序的响应。
  • x4z - 未定义。
  • x5z - 文件系统 - 这些响应传递来自服务器文件系统的状态码。

顺便说一下,最新的 FTP 技术是 RCF 2428,始于 1998 年。这只是一个不重要的信息,因为它与 FTP 的历史有关。但我们看了 RCF 959 技术,它自 1985 年以来一直在使用。

© . All rights reserved.