c# ftp服务器


1、 FTP命令

FTP 协议中规定了一些大家都认识的命令和组成。FTP协议中的命令都由3~4个字母组成,命令与参数之间用空格隔开,每个命令用回车换行结束。

(1)访问命令

(1)访问命令有:

USER命令——格式为:USER <username>, 指定登录的用户名,以便服务器进行身份验证。这个命令通常是控制连接后第一个发出的命令

PASS命令——格式为:PASS <password>, 指定用户密码,该命令必须跟在登录用户名命令之后。

REIN命令——格式为:REIN, 表示重新初始化用户信息,该命令终止当前USER的传输,同时终止正在传输的数据,然后重置所有参数,并打开控制连接,以便客户端再次发生USER命令。

QUIT命令——格式为:QUIT,关闭与服务器的连接

(2)模式设置命令:

PASV命令——格式为:PASV,该命令告诉FTP服务器,让FTP服务器在指定的数据端口进行监听,被动接受客户端的请求。如果未指定任何模式,FTP服务器默认使用PASV模式

PORT命令——格式为:PORT <address>,该命令告诉FTP服务器,客户端监听的端口号是address,让FTP服务器采用主动模式连接客户端。

TYPE命令——格式为: TYPE <data type>,该命令指定要传输的数据类型,有ASCII和BINARY两种类型。

MODE命令——格式为:MODE <mode>,该命令指定传输模式,S表示流,B表示块,C表示压缩。

(3)文件管理命令

CWD命令——格式为:CWD <directory>,该命令是用户可以在不同的目录或数据集下工作而不用改变登录信息,directory一般是目录名或与系统相关的文件集合。

PWD命令——格式为:PWD,该命令返回当前工作目录。

MKD命令——格式为:MKD <directory>,该命令表示在指定路径下创建新目录,directory 表示特定目录的字符串。

CDUP命令——格式为:CDUP,该命令表示回到上层目录

RMD命令——格式为:RMD <directory>,删除指定目录,directory表示特定目录的字符串。

LIST命令——格式为:LIST <name>,该命令返回指定路径下的子目录及文件列表,name 为路径。省略路径时,返回当前路径下的文件列表。

NLIST命令——格式为:NLIST <directory>,该命令返回指定路径下的目录列表,省略路径时,返回当前目录。

RNFR命令——格式为:RNFR <old path>,该命令表示重新命名文件,该命令的下一条命令用RNTO指定新的文件名。

RNTO命令——格式为:RNTO <new path>,该命令和RNFR命令共同完成对文件的重命名。

DELE命令——格式为:DELE <filename>,该命令表示删除指定路径下的文件

(4)文件传输命令:

RETR命令——RETR <filename>,表示下载指定路径的文件

STOR命令——STOR <filename>,表示上传一个指定的文件,并将其存储在指定的位置,如果文件已存在,原文件将被覆盖,如果文件不存在,则创建新文件。

(5)其他命令

SYST命令——格式为:SYST,该命令返回服务器使用的操作系统。

2、 FTP响应码

客户端发送FTP命令后,服务器需要返回FTP响应码,响应码即是回答,我们平常聊天中别人问了说了话或者问了问题,另外一方就需要回答,FTP协议中定义以响应码的形式来作为回答,FTP响应码由ASCII编码的3位数字开头,后面接一行文本提示信息,数字和提示信息中有一个空格,如XXX 接收请求。

每个响应码同样以回车换行结束。

FTP响应码的3位数字每位都有特定的意义,具体见下表:

响应码

表示

1

1XX

表示信息已被服务器正确接收,但尚未被处理

2XX

表示信息已被服务器正确处理完毕

3XX

彪西信息已被服务器正在接受,并正在处理中

4XX

表示信息处理错误(暂时)

5XX

表示信息处理错误(永久)

2

X0X

表示语法错误

X1X

表示系统状态与信息

X2X

表示与FTP服务器系统连接状态

X3X

表示与用户认证有关的信息

X4X

表示未定义

X5X

表示与文件系统有关的信息

 下表列出了常用的响应码所代表的意义:

响应码

意义

响应码

意义

110

重新启动标记应答

332

登陆是需要账户信息

120

服务在指定时间内准备好

350

请求的文件操作需要进一步命令

125

数据连接打开——开始传输

421

服务关闭

150

文件状态良好,将要打开数据连接

425

不能打开数据连接

200

命令成功

426

关闭连接,终止传输

202

命令没有执行

450

文件不可用

211

系统状态回复

451

中止请求操作:有本地错误

212

目录状态回复

452

磁盘空间不足

213

文件状态回复

500

无效命令

214

帮助信息回复

501

语法错误

215

系统类型回复

502

命令未执行

220

服务就绪

503

命令顺序错误

221

服务关闭控制连接,可以退出登陆

504

无效命令参数

225

数据连接打开,无传输正在进行

530

未登陆

226

关闭数据连接,请求的文件操作成功

532

存储文件需要账户信息

227

进入被动模式

550

未执行请求操作

230

用户已登陆

551

请求操作终止:页类型未知

250

请求的文件操作完成

552

请求文件操作终止:超过存储分配

257

创建路径名

553

为执行请求的操作:文件名不合法

331

用户名正确,需要口令

 

 

 

3、参考代码

        // 启动服务器
        private void btnFtpServerStartStop_Click(object sender, EventArgs e)
        {
            if (myTcpListener == null)
            {
                listenThread = new Thread(ListenClientConnect);
                listenThread.IsBackground = true;
                listenThread.Start();

                lstboxStatus.Enabled = true;
                lstboxStatus.Items.Clear();
                lstboxStatus.Items.Add("启动Ftp服务...");
                btnFtpServerStartStop.Text = "停止";
            }
            else
            {
                myTcpListener.Stop();
                myTcpListener = null;
                listenThread.Abort();
                lstboxStatus.Items.Add("Ftp服务已停止!");
                lstboxStatus.TopIndex = lstboxStatus.Items.Count - 1;

                btnFtpServerStartStop.Text = "启动";
            }
        }

        // 监听端口,处理客户端连接
        private void ListenClientConnect()
        {
            myTcpListener = new TcpListener(IPAddress.Parse(tbxFtpServerIp.Text), int.Parse(tbxFtpServerPort.Text));
            // 开始监听传入的请求
            myTcpListener.Start();
            AddInfo("启动成功!");
            AddInfo("Ftp服务运行中...[单机”停止“退出]");
            while (true)
            {
                try
                {
                    // 接收连接请求
                    TcpClient tcpClient = myTcpListener.AcceptTcpClient();
                    AddInfo(string.Format("客户端({0})与本机({1})建立Ftp连接", tcpClient.Client.RemoteEndPoint, myTcpListener.LocalEndpoint));
                    User user = new User();
                    user.commandSession = new UserSeesion(tcpClient);
                    user.workDir = tbxFtpRoot.Text;
                    Thread t = new Thread(UserProcessing);
                    t.IsBackground = true;
                    t.Start(user);
                }
                catch
                {
                    break;
                }
            }
        }

        // 处理客户端用户请求
        private void UserProcessing(object obj)
        {
            User user = (User)obj;
            string sendString = "220 FTP Server v1.0";
            RepleyCommandToUser(user, sendString);
            while (true)
            {
                string receiveString = null;
                try
                {
                    // 读取客户端发来的请求信息
                    receiveString = user.commandSession.streamReader.ReadLine();
                }
                catch(Exception ex)
                {
                    if (user.commandSession.tcpClient.Connected == false)
                    {
                        AddInfo(string.Format("客户端({0}断开连接!)", user.commandSession.tcpClient.Client.RemoteEndPoint));
                    }
                    else
                    {
                        AddInfo("接收命令失败!" + ex.Message);
                    }

                    break;
                }

                if (receiveString == null)
                {
                    AddInfo("接收字符串为null,结束线程!");
                    break;
                }

                AddInfo(string.Format("来自{0}:[{1}]", user.commandSession.tcpClient.Client.RemoteEndPoint, receiveString));
                
                // 分解客户端发来的控制信息中的命令和参数
                string command = receiveString;
                string param = string.Empty;
                int index = receiveString.IndexOf(' ');
                if (index != -1)
                {
                    command = receiveString.Substring(0, index).ToUpper();
                    param = receiveString.Substring(command.Length).Trim();
                }

                // 处理不需登录即可响应的命令(这里只处理QUIT)
                if (command == "QUIT")
                {
                    // 关闭TCP连接并释放与其关联的所有资源
                    user.commandSession.Close();
                    return;
                }
                else
                {
                    switch (user.loginOK)
                    {
                        // 等待用户输入用户名:
                        case 0:
                            CommandUser(user, command, param);
                            break;

                        // 等待用户输入密码
                        case 1:
                            CommandPassword(user, command, param);
                            break;

                        // 用户名和密码验证正确后登陆
                        case 2:
                            switch (command)
                            {
                                case "CWD":
                                    CommandCWD(user, param);
                                    break;
                                case "PWD":
                                    CommandPWD(user);
                                    break;
                                case "PASV":
                                    CommandPASV(user);
                                    break;
                                case "PORT":
                                    CommandPORT(user, param);
                                    break;
                                case "LIST":
                                    CommandLIST(user, param);
                                    break;
                                case "NLIST":
                                    CommandLIST(user, param);
                                    break;
                                // 处理下载文件命令
                                case "RETR":
                                    CommandRETR(user, param);
                                    break;
                                // 处理上传文件命令
                                case "STOR":
                                    CommandSTOR(user, param);
                                    break;
                                // 处理删除命令
                                case "DELE":
                                    CommandDELE(user, param);
                                    break;
                                // 使用Type命令在ASCII和二进制模式进行变换
                                case "TYPE":
                                    CommandTYPE(user, param);
                                    break;
                                default:
                                    sendString = "502 command is not implemented.";
                                    RepleyCommandToUser(user, sendString);
                                    break;
                            }

                            break;
                    }          
                }
            }       
        }

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM