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[斷行符號]
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 useway/n";
os.write(str.getBytes());
//得到傳回值
length = is.read(buffer);
s = new String(buffer, 0, length);
System.out.println(s);
//發送密碼
str = "PASS !@#$%abcd/n";
os.write(str.getBytes());
//得到傳回值
length = is.read(buffer);
s = new String(buffer, 0, length);
System.out.println(s);
//發送切換檔案夾指令
str = "CWD /home/useway/n";
os.write(str.getBytes());
//得到傳回值
length = is.read(buffer);
s = new String(buffer, 0, length);
System.out.println(s);
//設定模式
str = "EPSV ALL/n";
os.write(str.getBytes());
//得到傳回值
length = is.read(buffer);
s = new String(buffer, 0, length);
System.out.println(s);
//得到被動監聽資訊
str = "EPSV/n";
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 = "LIST/n";
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){
}
}
} 該程式運行後得到的運行結果,基本上和上面的運行效果相同吧,底層又如何,無非是將那些封裝好的方法解開來運行,只要瞭解到了它們啟動並執行規則,我們自己可以開發出一樣的程式來。
本文是《Java程式員,上班那點事兒》清華大學出版社的一個小節。(轉載請保留這句話,謝謝!)
《Java程式員,上班那點事兒》:
前言,目錄
卓越網銷售連結
China-pub銷售連結
噹噹網銷售連結《Java程式員,上班那點事》紀念帖
關於下載《Java程式員,上班那點事兒》的電子版