[C# 网络编程系列]专题十二:实现一个简单的FTP服务器
引言:
休息一個(gè)國慶節(jié)后好久沒有更新文章了,主要是剛開始休息完心態(tài)還沒有調(diào)整過來的, 現(xiàn)在差不多進(jìn)入狀態(tài)了, 所以繼續(xù)和大家分享下網(wǎng)絡(luò)編程的知識(shí),在本專題中將和大家分享如何自己實(shí)現(xiàn)一個(gè)簡(jiǎn)單的FTP服務(wù)器。在我們平時(shí)的上網(wǎng)過程中,一般都是使用FTP的客戶端來對(duì)商家提供的服務(wù)器進(jìn)行訪問(上傳、下載文件),例如我們經(jīng)常用到微軟的SkyDrive網(wǎng)盤,115網(wǎng)盤等,然而我們經(jīng)常用到的都是網(wǎng)頁版本的,網(wǎng)頁版本和客戶端版本的不同,網(wǎng)頁版本的FTP客戶端,它與服務(wù)器的交流是使用HTTP協(xié)議發(fā)出對(duì)服務(wù)器的請(qǐng)求的,而客戶端版本采用的是FTP協(xié)議發(fā)出命令對(duì)服務(wù)器進(jìn)行請(qǐng)求。然后我們接觸到FTP服務(wù)器卻很少的, 所以本專題中將和大家介紹下如何實(shí)現(xiàn)一個(gè)FTP服務(wù)器(不要覺得服務(wù)器很深?yuàn)W一樣的,大家可以簡(jiǎn)單的認(rèn)為服務(wù)器也是一個(gè)程序,該程序是對(duì)客戶端發(fā)來的請(qǐng)求做處理的,請(qǐng)求大家可以簡(jiǎn)單理解為字符串,從這個(gè)角度看, 服務(wù)器程序就是一個(gè)對(duì)字符串解析的過程。),也是為后面的一個(gè)專題做一個(gè)鋪墊,因?yàn)楹竺鎸n}將和大家介紹下FTP客戶端——文件上傳下載器,有了自己自定義的FTP服務(wù)器后, 自定義的FTP客戶端就可以對(duì)自定義的FTP服務(wù)器進(jìn)行訪問,使兩者形成一個(gè)完整的軟件,從而也讓大家對(duì)基于FTP協(xié)議的工具有一個(gè)初步的了解。
?
一、基于FTP協(xié)議的客戶端和服務(wù)器是如何"溝通的"?
FTP客戶端和FTP服務(wù)器之間的“溝通”分為四個(gè)階段的:
1. 啟動(dòng)FTP
客戶通過FTP客戶端軟件,發(fā)起FTP交互式的命令,就是告訴服務(wù)器(也就是一臺(tái)電腦,服務(wù)器上與一個(gè)程序(FTP服務(wù))會(huì)接收命令,并解析發(fā)來的命令,然后發(fā)出回復(fù)信息)說:“我想和你聊聊天,可以嗎?”
2. 建立控制連接
客戶端TCP層根據(jù)客戶給出的服務(wù)器IP地址,向服務(wù)器提供FTP服務(wù)的21號(hào)端口發(fā)出主動(dòng)建立連接的請(qǐng)求,服務(wù)器接收到請(qǐng)求后,通過3次握手之后,客戶端和服務(wù)器之間就建立一個(gè)TCP連接(就是一條通道,就好比生活中馬路,有了馬路之后車才能夠在兩地之間運(yùn)送東西),之后,所有用戶發(fā)出的FTP命令和服務(wù)器的回應(yīng)都是通過該連接來傳送的, 所以也把這個(gè)TCP連接叫做控制連接,控制連接在用戶退出之前一直存在。
3. 建立數(shù)據(jù)連接和進(jìn)行文件傳輸
現(xiàn)在客戶端和服務(wù)器端已經(jīng)建立聊天的通道了(控制連接),但是兩者聊天過程中如果互相想贈(zèng)送禮物要怎么辦呢?(這里形象的把客戶端和服務(wù)器端文件的傳輸比喻兩個(gè)人通過聊天后互相贈(zèng)送禮物的過程),此時(shí)我們就需要另外一條馬路(數(shù)據(jù)連接)來進(jìn)行“禮物的贈(zèng)送”了,具體贈(zèng)送禮物的過程如下:
4 關(guān)閉FTP
當(dāng)用戶退出FTP時(shí),通關(guān)客戶端發(fā)送退出命令,之后控制連接被關(guān)閉,FTP服務(wù)結(jié)束。
二、從上面的溝通過程中你明白了什么?
從上面客戶端與服務(wù)器端的溝通過程中,這里可以概括幾點(diǎn):
(1)客戶端與服務(wù)器端進(jìn)行交互過程中,傳輸層使用的是TCP協(xié)議而不是其他傳輸層協(xié)議
(2)溝通過程有兩條TCP連接——一條是控制連接,即傳輸命令和響應(yīng)信息的通道,另一條是數(shù)據(jù)連接,即傳輸文件的馬路,并且必須先有控制連接才能建立數(shù)據(jù)連接,因?yàn)橐M(jìn)行文件傳輸首先必須知道客戶的IP地址和端口號(hào),這個(gè)過程就是通過控制連接傳送的命令來告知服務(wù)器客戶端的IP地址和端口號(hào),之后再在兩者之間建立數(shù)據(jù)連接來傳輸文件
(3)在服務(wù)器端,控制連接(端口號(hào)為21)和數(shù)據(jù)連接(端口號(hào) 為20)使用了不同的端口號(hào)
三、贈(zèng)送禮物的方式?——文件傳輸模式
客戶端與FTP服務(wù)器建立數(shù)據(jù)連接之后,首先需要告訴服務(wù)器采用哪種文件傳輸模式,FTP提供了兩種文件傳輸模式,一種是主動(dòng)(Port)模式,另一種是被動(dòng)(Passive)模式。
主動(dòng)模式——服務(wù)器向客戶端發(fā)起數(shù)據(jù)連接請(qǐng)求,被動(dòng)模式——客戶端向服務(wù)器發(fā)起數(shù)據(jù)請(qǐng)求。
然而兩種模式有什么相同點(diǎn)和不同點(diǎn)呢?
兩種模式的相同點(diǎn): 服務(wù)器都使用21號(hào)端口進(jìn)行用戶驗(yàn)證和管理
不同點(diǎn): 傳送文件數(shù)據(jù)的方式不一樣,主動(dòng)模式的FTP服務(wù)器數(shù)據(jù)端口固定在20,而被動(dòng)模式的FTP服務(wù)器數(shù)據(jù)端口則在1025~65535之間的隨機(jī)數(shù)。
3.1 主動(dòng)模式
主動(dòng)模式——服務(wù)器主動(dòng)連接客戶端,然后傳輸文件,在這種模式下,FTP客戶端先用一個(gè)端口N(N>1024)向服務(wù)器的21號(hào)端口發(fā)起控制連接,連接成功后,在發(fā)出PORT N+1命令告訴服務(wù)器自己監(jiān)聽的端口為N+1;服務(wù)器接受到該命令后,用一個(gè)新的數(shù)據(jù)端口(20號(hào)端口)與客戶端的端口N+1建立連接,然后進(jìn)行文件傳輸,而客戶端則通過監(jiān)聽N+1端口接受文件數(shù)據(jù)。
注意: 采用主動(dòng)模式存在一個(gè)問題,如果客戶端安裝了防火墻或在內(nèi)網(wǎng)時(shí),由于防火墻一般不允許接受外部發(fā)起的標(biāo)準(zhǔn)端口以外的連接請(qǐng)求,因此外部FTP服務(wù)器就無法使用主動(dòng)模式穿過防火墻主動(dòng)連接客戶端(這里與客戶端連接的端口為N+1(N>1024),非標(biāo)準(zhǔn)端口),從而造成無法傳送文件數(shù)據(jù),此時(shí)就需要采用被動(dòng)模式傳送文件了。
3.2 被動(dòng)模式
被動(dòng)模式——服務(wù)器被動(dòng)接受客戶端連接請(qǐng)求,即控制連接請(qǐng)求和數(shù)據(jù)連接請(qǐng)求都是由客戶端發(fā)起,在這種模式下,FTP客戶端先隨機(jī)開始一個(gè)端口N向服務(wù)器的21號(hào)端口發(fā)起控制連接,然后向服務(wù)器發(fā)送PASV命令。服務(wù)器收到該命令后,會(huì)用一個(gè)新的端口P(P>1024)進(jìn)行監(jiān)聽,同時(shí)將該端口號(hào)告訴客戶端,客戶端接受到響應(yīng)命令后,再通過新的端口N+1連接服務(wù)器的端口P,然后進(jìn)行文件數(shù)據(jù)傳輸。
注意:采用被動(dòng)模式與主動(dòng)模式也存在相同的問題,如果服務(wù)器安裝了防火墻,客戶端同樣可能無法與服務(wù)器端的端口P建立數(shù)據(jù)請(qǐng)求,因?yàn)樵撜?qǐng)求可能會(huì)被防火墻過濾掉。在實(shí)際應(yīng)用中,服務(wù)器一般指定一個(gè)端口范圍,允許客戶端與該范圍內(nèi)的端口建立數(shù)據(jù)連接,而不再這個(gè)范圍內(nèi)的端口會(huì)被服務(wù)器的防火墻過濾掉,從而在一定程度上消除了針對(duì)服務(wù)器的惡意攻擊。
四、 FTP協(xié)議中有哪些命令的?
協(xié)議簡(jiǎn)單說就是一個(gè)規(guī)范,就好比打牌一樣,制定一個(gè)大家都能明白的規(guī)則,斗地主的規(guī)則被大家都認(rèn)可的,但是私下我們也可以自定義規(guī)則來玩的(例如說三個(gè)只能帶一個(gè)等這樣的規(guī)則),同樣FTP規(guī)則也是大家都認(rèn)可的一個(gè)協(xié)議,我們當(dāng)然也可以自定義協(xié)議。
由于.Net平臺(tái)下目前還沒有提供對(duì)FTP服務(wù)器端開發(fā)的類庫,因此要實(shí)現(xiàn)一個(gè)FTP服務(wù)器端的應(yīng)用程序,就必須了解FTP協(xié)議的詳細(xì)內(nèi)容。
4.1 FTP命令有哪些?
FTP 協(xié)議中規(guī)定了一些大家都認(rèn)識(shí)的命令和組成。FTP協(xié)議中的命令都由3~4個(gè)字母組成,命令與參數(shù)之間用空格隔開,每個(gè)命令用回車換行結(jié)束。
(1)訪問命令
(1)訪問命令有:
USER命令——格式為:USER <username>, 指定登錄的用戶名,以便服務(wù)器進(jìn)行身份驗(yàn)證。這個(gè)命令通常是控制連接后第一個(gè)發(fā)出的命令
PASS命令——格式為:PASS <password>, 指定用戶密碼,該命令必須跟在登錄用戶名命令之后。
REIN命令——格式為:REIN, 表示重新初始化用戶信息,該命令終止當(dāng)前USER的傳輸,同時(shí)終止正在傳輸?shù)臄?shù)據(jù),然后重置所有參數(shù),并打開控制連接,以便客戶端再次發(fā)生USER命令。
QUIT命令——格式為:QUIT,關(guān)閉與服務(wù)器的連接
(2)模式設(shè)置命令:
PASV命令——格式為:PASV,該命令告訴FTP服務(wù)器,讓FTP服務(wù)器在指定的數(shù)據(jù)端口進(jìn)行監(jiān)聽,被動(dòng)接受客戶端的請(qǐng)求。如果未指定任何模式,FTP服務(wù)器默認(rèn)使用PASV模式
PORT命令——格式為:PORT <address>,該命令告訴FTP服務(wù)器,客戶端監(jiān)聽的端口號(hào)是address,讓FTP服務(wù)器采用主動(dòng)模式連接客戶端。
TYPE命令——格式為: TYPE <data type>,該命令指定要傳輸?shù)臄?shù)據(jù)類型,有ASCII和BINARY兩種類型。
MODE命令——格式為:MODE <mode>,該命令指定傳輸模式,S表示流,B表示塊,C表示壓縮。
(3)文件管理命令
CWD命令——格式為:CWD <directory>,該命令是用戶可以在不同的目錄或數(shù)據(jù)集下工作而不用改變登錄信息,directory一般是目錄名或與系統(tǒng)相關(guān)的文件集合。
PWD命令——格式為:PWD,該命令返回當(dāng)前工作目錄。
MKD命令——格式為:MKD <directory>,該命令表示在指定路徑下創(chuàng)建新目錄,directory 表示特定目錄的字符串。
CDUP命令——格式為:CDUP,該命令表示回到上層目錄
RMD命令——格式為:RMD <directory>,刪除指定目錄,directory表示特定目錄的字符串。
LIST命令——格式為:LIST <name>,該命令返回指定路徑下的子目錄及文件列表,name 為路徑。省略路徑時(shí),返回當(dāng)前路徑下的文件列表。
NLIST命令——格式為:NLIST <directory>,該命令返回指定路徑下的目錄列表,省略路徑時(shí),返回當(dāng)前目錄。
RNFR命令——格式為:RNFR <old path>,該命令表示重新命名文件,該命令的下一條命令用RNTO指定新的文件名。
RNTO命令——格式為:RNTO <new path>,該命令和RNFR命令共同完成對(duì)文件的重命名。
DELE命令——格式為:DELE <filename>,該命令表示刪除指定路徑下的文件
(4)文件傳輸命令:
RETR命令——RETR <filename>,表示下載指定路徑的文件
STOR命令——STOR <filename>,表示上傳一個(gè)指定的文件,并將其存儲(chǔ)在指定的位置,如果文件已存在,原文件將被覆蓋,如果文件不存在,則創(chuàng)建新文件。
(5)其他命令
SYST命令——格式為:SYST,該命令返回服務(wù)器使用的操作系統(tǒng)。
4.2 FTP響應(yīng)碼
客戶端發(fā)送FTP命令后,服務(wù)器需要返回FTP響應(yīng)碼,響應(yīng)碼即是回答,我們平常聊天中別人問了說了話或者問了問題,另外一方就需要回答,FTP協(xié)議中定義以響應(yīng)碼的形式來作為回答,FTP響應(yīng)碼由ASCII編碼的3位數(shù)字開頭,后面接一行文本提示信息,數(shù)字和提示信息中有一個(gè)空格,如XXX 接收請(qǐng)求。
每個(gè)響應(yīng)碼同樣以回車換行結(jié)束。
FTP響應(yīng)碼的3位數(shù)字每位都有特定的意義,具體見下表:
| 響應(yīng)碼 | 表示 | |
| 第 1 位 數(shù) 字 | 1XX | 表示信息已被服務(wù)器正確接收,但尚未被處理 |
| 2XX | 表示信息已被服務(wù)器正確處理完畢 | |
| 3XX | 彪西信息已被服務(wù)器正在接受,并正在處理中 | |
| 4XX | 表示信息處理錯(cuò)誤(暫時(shí)) | |
| 5XX | 表示信息處理錯(cuò)誤(永久) | |
| 第 2 位 數(shù) 字 | X0X | 表示語法錯(cuò)誤 |
| X1X | 表示系統(tǒng)狀態(tài)與信息 | |
| X2X | 表示與FTP服務(wù)器系統(tǒng)連接狀態(tài) | |
| X3X | 表示與用戶認(rèn)證有關(guān)的信息 | |
| X4X | 表示未定義 | |
| X5X | 表示與文件系統(tǒng)有關(guān)的信息 | |
?下表列出了常用的響應(yīng)碼所代表的意義:
| 響應(yīng)碼 | 意義 | 響應(yīng)碼 | 意義 |
| 110 | 重新啟動(dòng)標(biāo)記應(yīng)答 | 332 | 登陸是需要賬戶信息 |
| 120 | 服務(wù)在指定時(shí)間內(nèi)準(zhǔn)備好 | 350 | 請(qǐng)求的文件操作需要進(jìn)一步命令 |
| 125 | 數(shù)據(jù)連接打開——開始傳輸 | 421 | 服務(wù)關(guān)閉 |
| 150 | 文件狀態(tài)良好,將要打開數(shù)據(jù)連接 | 425 | 不能打開數(shù)據(jù)連接 |
| 200 | 命令成功 | 426 | 關(guān)閉連接,終止傳輸 |
| 202 | 命令沒有執(zhí)行 | 450 | 文件不可用 |
| 211 | 系統(tǒng)狀態(tài)回復(fù) | 451 | 中止請(qǐng)求操作:有本地錯(cuò)誤 |
| 212 | 目錄狀態(tài)回復(fù) | 452 | 磁盤空間不足 |
| 213 | 文件狀態(tài)回復(fù) | 500 | 無效命令 |
| 214 | 幫助信息回復(fù) | 501 | 語法錯(cuò)誤 |
| 215 | 系統(tǒng)類型回復(fù) | 502 | 命令未執(zhí)行 |
| 220 | 服務(wù)就緒 | 503 | 命令順序錯(cuò)誤 |
| 221 | 服務(wù)關(guān)閉控制連接,可以退出登陸 | 504 | 無效命令參數(shù) |
| 225 | 數(shù)據(jù)連接打開,無傳輸正在進(jìn)行 | 530 | 未登陸 |
| 226 | 關(guān)閉數(shù)據(jù)連接,請(qǐng)求的文件操作成功 | 532 | 存儲(chǔ)文件需要賬戶信息 |
| 227 | 進(jìn)入被動(dòng)模式 | 550 | 未執(zhí)行請(qǐng)求操作 |
| 230 | 用戶已登陸 | 551 | 請(qǐng)求操作終止:頁類型未知 |
| 250 | 請(qǐng)求的文件操作完成 | 552 | 請(qǐng)求文件操作終止:超過存儲(chǔ)分配 |
| 257 | 創(chuàng)建路徑名 | 553 | 為執(zhí)行請(qǐng)求的操作:文件名不合法 |
| 331 | 用戶名正確,需要口令 | ? | ? |
五、實(shí)現(xiàn)自定義的FTP服務(wù)器
相信大家看完上面的介紹對(duì)FTP協(xié)議以及FTP客戶端和FTP服務(wù)器的交互過程有一定的理解的,這時(shí)候大家知道理論后就一定很想知道知道這些之后可以做什么的?答案就是可以制作一個(gè)簡(jiǎn)單的FTP服務(wù)器,大家可以根據(jù)代碼來進(jìn)一步理解FTP協(xié)議。下面是程序中一些核心代碼片段:
View Code // 啟動(dòng)服務(wù)器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("啟動(dòng)Ftp服務(wù)...");btnFtpServerStartStop.Text = "停止";}else{myTcpListener.Stop();myTcpListener = null;listenThread.Abort();lstboxStatus.Items.Add("Ftp服務(wù)已停止!");lstboxStatus.TopIndex = lstboxStatus.Items.Count - 1;btnFtpServerStartStop.Text = "啟動(dòng)";}}// 監(jiān)聽端口,處理客戶端連接private void ListenClientConnect(){myTcpListener = new TcpListener(IPAddress.Parse(tbxFtpServerIp.Text), int.Parse(tbxFtpServerPort.Text));// 開始監(jiān)聽傳入的請(qǐng)求 myTcpListener.Start();AddInfo("啟動(dòng)成功!");AddInfo("Ftp服務(wù)運(yùn)行中...[單機(jī)”停止“退出]");while (true){try{// 接收連接請(qǐng)求TcpClient tcpClient = myTcpListener.AcceptTcpClient();AddInfo(string.Format("客戶端({0})與本機(jī)({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;}}}// 處理客戶端用戶請(qǐng)求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{// 讀取客戶端發(fā)來的請(qǐng)求信息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,結(jié)束線程!");break;}AddInfo(string.Format("來自{0}:[{1}]", user.commandSession.tcpClient.Client.RemoteEndPoint, receiveString));// 分解客戶端發(fā)來的控制信息中的命令和參數(shù)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();}// 處理不需登錄即可響應(yīng)的命令(這里只處理QUIT)if (command == "QUIT"){// 關(guān)閉TCP連接并釋放與其關(guān)聯(lián)的所有資源 user.commandSession.Close();return;}else{switch (user.loginOK){// 等待用戶輸入用戶名:case 0:CommandUser(user, command, param);break;// 等待用戶輸入密碼case 1:CommandPassword(user, command, param);break;// 用戶名和密碼驗(yàn)證正確后登陸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和二進(jìn)制模式進(jìn)行變換case "TYPE":CommandTYPE(user, param);break;default:sendString = "502 command is not implemented.";RepleyCommandToUser(user, sendString);break;}break;} }} }?
程序演示截圖:
?首先在F:\盤下新建文件夾MyFtpServerRoot,在其中創(chuàng)建目錄結(jié)構(gòu)并放一些文件資源,例如圖片,文檔等,程序中演示的目錄結(jié)構(gòu)如下圖:
?這樣,本地的FTP服務(wù)站點(diǎn)就已經(jīng)建好了,運(yùn)行FTP服務(wù)器程序,然后點(diǎn)擊“啟動(dòng)”按鈕后就啟動(dòng)了FTP服務(wù)器,運(yùn)行結(jié)果如下圖所示:
然后配合上個(gè)專題中實(shí)現(xiàn)的FTP客戶端來完成與FTP服務(wù)器的“聊天”演示,因?yàn)镕TP服務(wù)器程序中已經(jīng)初始化用戶名和密碼(都為admin),所以FTP客戶端中取消選擇“匿名復(fù)選框”,直接輸入用戶名和密碼為admin后點(diǎn)擊“登錄”按鈕后就完成了用戶驗(yàn)證的過程,并與FTP服務(wù)器建立了控制連接和數(shù)據(jù)連接。運(yùn)行結(jié)果如下圖:
當(dāng)然用戶可以通過"上傳"、“下載”和刪除按鈕來對(duì)FTP服務(wù)器上的文件進(jìn)行操作,這里就不貼出運(yùn)行圖片了, 大家可以下載源碼來測(cè)試下的。
?
六、內(nèi)容的結(jié)尾,說說后面的計(jì)劃吧
這個(gè)專題介紹完后,我這個(gè)C#網(wǎng)絡(luò)編程系列也就介紹完了,這個(gè)系列中主要介紹網(wǎng)絡(luò)編程的一些入門知識(shí),對(duì)于朋友在留言中經(jīng)常提到的“打洞”技術(shù)以及一些網(wǎng)絡(luò)編程中一些更難的內(nèi)容還大家一起努力來學(xué)習(xí)的,同時(shí)我也會(huì)在后面和大家分享下一些實(shí)際開發(fā)過程中的網(wǎng)絡(luò)編程的內(nèi)容(在后面的文章打算和大家分享一個(gè)下載器的實(shí)現(xiàn)),最后,希望這個(gè)系列可以讓大家對(duì)網(wǎng)絡(luò)協(xié)議有一個(gè)最初的入門,這樣在實(shí)際的開發(fā)過程中才知道這些實(shí)現(xiàn)背后的原理。之后我總結(jié)下我這個(gè)系列的所有文章的索引,以便讓大家更好的閱讀和查找關(guān)于這個(gè)系列的所有文章。
?
源碼下載:http://files.cnblogs.com/zhili/FtpServer.zip,大家如果覺得不錯(cuò)的話,還請(qǐng)大家推薦下,謝謝大家的支持
?用來演示的服務(wù)器目錄:http://files.cnblogs.com/zhili/MyFtpServerRoot.zip
?上個(gè)專題FTP文件上傳下載器源碼:http://files.cnblogs.com/zhili/FTPUpDownloader.zip
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/zhili/archive/2012/10/18/FTPServer.html
總結(jié)
以上是生活随笔為你收集整理的[C# 网络编程系列]专题十二:实现一个简单的FTP服务器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 取样问题
- 下一篇: C#.NET SQLServer数据库分