《網際網路程式設計(Java)》——課程筆記5:網路檔案傳送程式設計
學會基本的檔案傳輸FTP程式設計技術。
前兩講我們學會了使用TCP套接字(Socket),能實現字串的傳送和接收功能,簡單地做到了客戶機和伺服器的對話。
今天,我們進一步學習TCP套接字,利用它的位元組傳輸技術,實現網路檔案傳輸。
檔案傳輸協議規定(RFC 959 FTP.txt),網路檔案傳輸中用兩個TCP埠來實現:
一個埠(21號)用來對話,傳遞控制資訊,總是開啟;
一個埠(20號)實現檔案資料傳遞服務,有資料傳輸服務時開啟。
FTP的兩個埠
用2021埠實現對話服務,如身份驗證、檔案目錄資訊瀏覽等,用2020埠傳遞資料檔案(即檔案上傳下載)。
程式設計知識點:位元組流(網路位元組流和檔案位元組流)的讀寫技術,進一步參考檔案,詳見文章“Java套接字程式技術比較研究.doc”
基於C/S軟體架構的主要程式模組如下:
客戶端程式有:
主介面客戶端程式FileClientJFrame.java;
檔案對話客戶端程式(控制程序)FileDialogClient.java;
檔案資料客戶端程式(資料傳輸程序)FileDataClient.java。
服務端程式:
檔案對話伺服器程式FileDialogServer.java,開啟2021埠;
主要功能:身份驗證、檔案目錄傳送。
檔案資料伺服器程式FileDataServer.java,開啟2020埠。
主要功能:根據請求的檔名傳送檔案,接收檔案。
一、程式設計第一步:建立遠端檔案對話程式
客戶端程式主要功能:傳送使用者資訊,瀏覽檔案目錄,實現和檔案伺服器的基本對話。
1. 新建一個程式包:ftpClient,用於存放遠端檔案操作相關的程式。
2. 將TCPClient.java程式重構、複製、並命名為FileDialogClient.java,原有的方法保持不變。
(1) 重構、複製TCPClientThreadJFrame、並命名為FileClientJFrame,新增“下載”和“上傳”2個按鈕;
(2) 執行程式FileClientJFrame,效果是否如圖2;
(3) 申明FileDialogClient物件,並定義2個全域性變數:ip, port;
(4) 在介面的“連線”按鈕中例項化FileDialogClient物件,並定義和啟動“讀”執行緒;
(5) 在介面的“傳送”按鈕中呼叫FileDialogClient.java的傳送方法;
(6) 在介面的“退出”按鈕中呼叫FileDialogClient.java的關閉方法。
(7) //執行視窗程式,輸入一個公用的FTP伺服器的地址,或教學用的FTP伺服器:192.168.2*1.*4:2021,連線成功後傳送一些基本FTP命令,觀察返回結果;
(8) 新增快捷功能:
在使用者介面設計狀態,滑鼠右擊資訊顯示區,選擇“事件”、“MouseMotion”、“mousedragged”,鍵入如下程式碼:
jTextField1.setText(jTextArea1.getSelectedText());//滑鼠加亮的字串會自動出現在資訊錄入行中。
二、 程式設計第二步:
建立客戶端資料傳送程序FileDataClient.java
主要功能:連線伺服器資料埠、傳送檔名、儲存下載的檔案或上傳檔案,檔案傳輸完成後關閉資料連線。
該程式有3個方法:
(1)構造方法,FileDataClient(String ip,String port),主要功能是向伺服器的資料埠請求連線;
(2)檔案下載方法fileGet(fileName)
主要功能是準備當地磁碟空檔案,向伺服器傳送檔名(基於字串輸出流操作),然後接收網路檔案資料並儲存當地磁碟(基於位元組流操作),關閉資料套接字。
在介面的“下載”按鈕中呼叫該方法,如:
String fName=jTextField1.getText();
jTextField1.setText("");
new FileDataClient(ip,port).fileGet(fName);
部分具體程式碼:
private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {
String fName=jTextField1.getText();
jTextField1.setText("");
try {
String ip="202.*16.1*5.22";
String port="2021";
new FileDataClient(ip,port).fileGet(fName);
// TODO add your handling code here:
} catch (IOException ex) {
Logger.getLogger(FileClientJFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
檔案下載部分:
public String fileGet(String fName) throws IOException{
boolean flag=false;
//Object dataSocket = null;
if(dataSocket!=null){//自定義變數dataSocket
byte[] buff=new byte[1024*2];//用來緩衝接收的位元組資料
//(1)檔案儲存對話方塊.
JFileChooser chooser=new JFileChooser();
File saveFile=new File(fName);
chooser.setSelectedFile(saveFile);
int stat=chooser.showSaveDialog(null);
if(stat==JFileChooser.APPROVE_OPTION)
saveFile=chooser.getSelectedFile();
else
saveFile=null;
if(saveFile!=null){
FileOutputStream fileOut=new FileOutputStream(saveFile); //新建本地空檔案.
InputStream socketIn= dataSocket.getInputStream();//網路位元組輸入流
OutputStream socketOut=dataSocket.getOutputStream();//網路位元組數出流
//(2)傳送請求的檔名,字串讀寫功能
PrintWriter pw=new PrintWriter(new OutputStreamWriter(socketOut,"GB2312"),true);
pw.println(fName);
//(3)接收伺服器的資料檔案,位元組讀寫功能
int len=socketIn.read(buff);//讀一塊到緩衝區.
while(len!=-1){
fileOut.write(buff,0,len);//寫一塊到檔案.
len=socketIn.read(buff);
flag=true;
}
//(4)檔案傳輸完畢,關閉資料套接字。
fileOut.close();
//JOptionPane.showMessageDialog(null, "檔案接收完畢.");
dataSocket.close();
if(flag)
return "檔案下載成功.";
else{
saveFile.delete();
return "檔名錯誤或檔案下載失敗.";
}
}else{
dataSocket.close();
return "本地檔案建立失敗.";
}
}else
return "伺服器連線失敗.";
}
}