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






4.83/5 (20投票s)
连接到 FTP 服务器,并下载、上传、重命名和删除文件或目录。
引言
我知道互联网上有很多 FTP 客户端程序。但我们也需要了解 FTP(文件传输协议)的底层结构。因此,这个开源项目将引导您学习 FTP。这个程序的界面看起来像 FileZilla。FileZilla 非常流行,但它也存在一些 bug。这一切都始于我想打开我的博客。我需要通过 FTP 连接到我的服务器,发送文件、下载文件等。于是我决定编写自己的应用程序来处理所有这些。FileZilla 已经足够好,但它不是我写的。
背景
让我们看看我们知道什么。我们知道 FTP 是一种标准的基于 TCP 的网络协议,用于在不同主机之间传输文件。它是一种客户端-服务器架构。
FTP 程序是基于命令行实现的。我们仍然可以使用“cmd.exe”连接到 FTP 服务器,因为 FTP 使用命令。例如,要发送文件,我们可以从命令行使用“stor”。为了做到这一切,FTP 服务器需要正在运行并等待传入的请求。我们可以从 Wikipedia 上更好地理解 FTP:“客户端计算机能够与服务器在端口 21 上通信,这被称为控制连接。它在整个会话期间保持打开状态,第二连接称为数据连接,根据需要由服务器从其端口 20 打开到协商的客户端端口(主动模式),或由客户端从任意端口打开到协商的服务器端口(被动模式)来传输文件数据。控制连接用于会话管理(即,在客户端和服务器之间使用类似 Telnet 的协议交换命令、身份验证和密码)。例如,“RETR 文件名”会将指定的文件从服务器传输到客户端。由于这种双端口结构,FTP 被认为是带外协议,而 HTTP 等协议是带内协议。”
“服务器在控制连接上以三位数的 ASCII 状态码和可选的文本消息进行响应,例如“200”(或“200 OK.”)表示最后一个命令已成功。数字代表状态码,可选文本代表解释(例如,
在主动连接中,客户端将 IP 和端口发送给服务器,然后服务器会尝试连接到客户端。但这可能会被客户端阻止,因为防火墙。我们都在使用杀毒软件或 Windows 防火墙,对吧?现在让我们看看被动模式。
在被动连接中,服务器通过“PASV”命令将其 IP 和端口发送给客户端,然后客户端可以尝试使用此 IP 连接到服务器。这是发送文件的非常实用的方式。当我们尝试发送文件时,我们应该首先使用“PASV”模式。如您所知,大多数协议,如 FTP、HTTP,在尝试获取内容时都使用 ASCII 字符,因为它是一种全局模式。因为它是一种全局模式,所以我们将使用这种模式。您可以从此 URL 获取 FTP 命令列表:http://en.wikipedia.org/wiki/File_Transfer_Protocol。
使用代码
现在我们已准备好准备我们的应用程序。让我们编写一些有用的代码 :) 首先,我们需要一个“打开文件对话框”,但要集成到我们的窗体中。
文件浏览器组件
我们需要一个文件浏览器组件,以便在应用程序的界面中查看要发送到 FTP 服务器的文件。打开一个类型为“Windows Forms 用户控件库”的新项目。
它会看起来像这样。现在我们需要一个树视图。几个按钮。以及一个搜索功能。
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 年以来一直在使用。