-
10.2.4.3 範例3:網路應用層協定的開發
清華大學出版社《Java程式設計師,上班那點事兒》作者:鐘聲-第10章《高手有多高有多菜》部分節錄。
大家也許都用過FTP上傳下載工具,像是「LeapFTP」這個工具是一個很方便的FTP伺服器上傳下載工具,如圖所示。這個工具很方便,輸入使用者名稱密碼以後,就可以看到FTP伺服器端的檔案列表,方便進行上傳與下載操作。
你有試過自己用Java寫一個FTP的檔案上傳與下載應用程式?
Java也可以發展出這樣的程式來,並不複雜,我們先看看,利用Java的FTP類工具包製作FTP應用程式的開發是怎麼做的,請看如下程式:
import sun.net.*;
import sun.net.ftp.*;
public class FTP {
public static void main(String[] args) {
String server="192.168.0.12"; //輸入的FTP伺服器的IP位址
String user="useway"; //登入FTP伺服器的使用者名稱
String password=" !@#$%abce "; //登入FTP伺服器的使用者名稱的口令
String path="/home/useway"; //FTP伺服器上的路徑
try {
FtpClient ftpClient=new FtpClient(); //建立FtpClient物件
ftpClient.openServer(server); //連接FTP伺服器
ftpClient.login(user, password); //登入FTP伺服器
if (path.length()!=0) ftpClient.cd(path);
TelnetInputStream is=ftpClient.list();
int c;
while ((c=is.read())!=-1) {
System.out.print((char)c);
}
is.close();
ftpClient.closeServer();//退出FTP伺服器
}
catch(Exception ex){
}
}
}
如果你有興趣的話,可以自己寫一個這個程序,當本程序運行以後,我們看到如圖所示的情況,列出了伺服器端程序的目錄內容。
這個程序是一個簡單的得到FTP伺服器端檔案清單的程序,但不要誤會,這個程序可稱不上「網頁應用層協定」程序的開發!
這個程式只是利用「sun.net.*;」和「sun.net.ftp.*;」中的相關類別進行的對FTP端的操作的,我們根本沒有利用Java的Socket的在網路層面向FTP伺服器端發送任何請求,而是透過Java提供的工具包,向伺服器端發送的連結請求。
利用Java的FTP包來連結FTP伺服器的好處在於我們不需要關心網路層面發送資料的具體細節,只要呼叫對應的方法就行了。利用Java的FTP套件來連結FTP伺服器的缺點是讓開發者不知道應用層協定收發的來龍去脈,搞不清楚其中原理,對底層資料的掌握程度非常弱。
講到這裡有程式設計師會問:“那麼FTP在網路層面和PC與伺服器間是如何互動的呢?”,好,就給大家列出FTP協定互動過程。
請看下面的一段FTP協定互動的範例:
FTP伺服器: 220 (vsFTPd 2.0.1)
FTP客戶端: USER useway
FTP伺服器: 331 Please specify the password.
FTP客戶端: PASS !@#$%abce
FTP伺服器: 230 Login successful.
FTP客戶端: CWD /home/useway
FTP伺服器: 250 Directory successfully changed.
FTP客戶端: EPSV ALL
FTP伺服器: 200 EPSV ALL ok.
FTP客戶端: EPSV
FTP伺服器: 229 Entering Extended Passive Mode (|||62501|)
FTP客戶端: LIST
FTP伺服器: 150 Here comes the directory listing.
FTP伺服器: 226 Directory send OK.
FTP客戶端: QUIT
FTP伺服器: 221 Goodbye.
以上這段文字其實就是FTP伺服器和FTP客戶端之間相互互動的過程,它們之間傳遞訊息的協議是TCP協議,互相發送的內容就是上面這段文字所寫的內容。
我們下面逐步的去解釋每一句話的意思:
FTP伺服器: 220 (vsFTPd 2.0.1) |說明:連結成功
FTP客戶端: USER useway |說明:輸入使用者名稱
FTP伺服器: 331 Please specify the password. |說明:請輸入密碼
FTP客戶端: PASS !@#$%abce |說明:輸入密碼
FTP伺服器: 230 Login successful. |說明:登入成功
FTP客戶端: CWD /home/useway |說明:切換目錄
FTP伺服器: 250 Directory successfully changed. |說明:目錄切換成功
FTP客戶端: EPSV ALL |說明:為EPSV被動連結方式
FTP伺服器: 200 EPSV ALL ok. |說明:OK
FTP客戶端: EPSV |說明:鏈接
FTP伺服器: 229 Entering Extended Passive Mode (|||62501|) |說明:被動連結埠為62501
FTP客戶端: LIST |說明:執行LIST顯示檔案列表
FTP伺服器: 150 Here comes the directory listing. |說明:清單從62501連接埠被傳送
FTP伺服器: 226 Directory send OK. |說明:傳送完成
FTP客戶端: QUIT |說明:退出FTP
FTP伺服器: 221 Goodbye. |說明:再見
有了以上文字的內容,我們不需要任何工具也可以得到FTP檔案清單了,不信你跟著我一起做一遍。
第一步:首先開啟CMD進入DOS命令列模式,鍵入:
telnet 192.168.0.1 21[回車]
說明:Telnet 到Ftp伺服器的21連接埠。
執行該指令後,得到的結果如圖所示。
大家發現什麼問題了嗎?
提示的內容正好就是,我們上面一段文字的第一句:220 (vsFTPd 2.0.1),這說明FTP伺服器已經接受了我們的鏈接,已經可以進行下一步操作了。
第二步:將後面一系列的發送內容逐一鍵入:
USER useway[Enter]
PASS !@#$%abce [回車]
CWD /home/useway[回車]
EPSV ALL[回車]
EPSV[回車]
得到的結果如圖所示。
好,這回FTP伺服器給了一系列的回應,在最後給了一個新的連接埠號碼"58143"。
第三步:再開啟一個新的CMD窗口,鍵入:
telnet 192.168.0.1 58143[回車]
注意,這次Telnet請求連結伺服器的連接埠號碼是“58143”,是FTP伺服器給我們的連結連接埠。連結後,視窗為空白沒有任何提示,如圖所示。
第四步:回到第一個CMD窗口,鍵入:
LIST[回車]
第五步:這時候第二CMD視窗就接收到了文件列表:
第二個視窗接收到了文件列表如圖所示。
第六步:退出操作
QUIT[回車]
執行完成後,失去與主機的鏈接,如圖所示。
大家看到了吧,FTP協定就是這樣的互動過程,利用系統自帶的Telnet工具也可以完成FTP的這些基本指令的操作。如果,你想用Java的Socket完成以上操作就只需要一步一步的按照上述內容發送字串給FTP伺服器端就行了。
我們下面也給例子代碼:
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class FTPClient{
public static void main(String[] args) throws Exception{
Socket socket = new Socket("192.168.0.1",21);
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
//接收初始連結訊息
byte[] buffer = new byte[100];
int length = is.read(buffer);
String s = new String(buffer, 0, length);
System.out.println(s);
//發送使用者名稱
String str = "USER usewayn";
os.write(str.getBytes());
//得到回傳值
length = is.read(buffer);
s = new String(buffer, 0, length);
System.out.println(s);
//發送密碼
str = "PASS !@#$%abcdn ";
os.write(str.getBytes());
//得到回傳值
length = is.read(buffer);
s = new String(buffer, 0, length);
System.out.println(s);
//發送切換資料夾指令
str = "CWD /home/usewayn";
os.write(str.getBytes());
//得到回傳值
length = is.read(buffer);
s = new String(buffer, 0, length);
System.out.println(s);
//設定模式
str = "EPSV ALLn";
os.write(str.getBytes());
//得到回傳值
length = is.read(buffer);
s = new String(buffer, 0, length);
System.out.println(s);
//得到被動監聽訊息
str = "EPSVn";
os.write(str.getBytes());
//得到回傳值
length = is.read(buffer);
s = new String(buffer, 0, length);
System.out.println(s);
//取得FTP被動監聽的連接埠號
String portlist=s.substring(s.indexOf("(|||")+4,s.indexOf("|)"));
System.out.println(portlist);
//實例化ShowList執行緒類,連結FTP被動監聽埠號
ShowList sl=new ShowList();
sl.port=Integer.parseInt(portlist);
sl.start();
//執行LIST指令
str = "LISTn";
os.write(str.getBytes());
//得到回傳值
length = is.read(buffer);
s = new String(buffer, 0, length);
System.out.println(s);
//關閉連結
is.close();
os.close();
socket.close();
}
}
//得到被動連結資訊類,這個類是多執行緒的
class ShowList extends Thread{
public int port=0;
public void run(){
try{
Socket socket = new Socket("192.168.0.1",this.port);
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
byte[] buffer = new byte[10000];
int length = is.read(buffer);
String s = new String(buffer, 0, length);
System.out.println(s);
//關閉連結
is.close();
os.close();
socket.close();
}
catch(Exception ex){
}
}
}
程式運行後得到的運行結果如圖所示,基本上和上面的運行效果相同吧,底層又如何,無非是將那些封裝好的方法解開來運行,只要了解到了它們運行的規則,我們自己可以開發出一樣的程式來。