学会基本的文件传输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 "服务器连接失败.";
}
}