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