互联网程序设计第四讲
第四讲 网络文件传送程序设计
完成日期:2018.10.10
GitHub:https://github.com/fyinh/network_programming_course/tree/master/ch04
教学与实践目的:学会基本的文件传输FTP程序设计技术。
进一步学习TCP套接字,利用它的字节传输技术,实现网络文件传输。
文件传输协议规定(RFC 959 FTP.txt),网络文件传输中用两个TCP端口来实现:
- 一个端口(21号)用来对话,传递控制信息,总是开启;
- 一个端口(20号)实现文件数据传递服务,有数据传输服务时开启。
本实验实现一个简约的远程文件传输系统,方便教学资料的下载。
用2021端口实现对话服务,如身份验证、文件目录信息浏览等,用2020端口传递数据文件(即文件上传下载)。
基于C/S软件架构的主要程序模块如下:
客户端程序有:
主界面客户端程序FileClientJFrame.java;
文件对话客户端程序(控制进程)FileDialogClient.java;
文件数据客户端程序(数据传输进程)FileDataClient.java。
服务端程序:
文件对话服务器程序FileDialogServer.java,开启2021端口;
主要功能:身份验证、文件目录传送。
文件数据服务器程序FileDataServer.java,开启2020端口。
主要功能:
根据请求的文件名传送文件,接收文件。
一、程序设计第一步
设计任务:
创建远程文件对话程序
客户端程序主要功能:发送用户信息,浏览文件目录,实现和文件服务器的基本对话。
过程:
1. 新建一个程序包:ftpClient,用于存放远程文件操作相关的程序。
2. 将TCPClient.java程序重构、复制、并命名为FileDialogClient.java,原有的方法保持不变。
3. 用户界面程序FileClientJFrame.java如下图所示。
(1)重构、复制TCPClientThreadJFrame、并命名为FileClientJFrame,新增“下载”和“上传”2个按钮;
(2)运行程序FileClientJFrame,效果是否如图2;
(3)申明FileDialogClient对象,并定义2个全局变量:ip, port;
(4)在界面的“连接”按钮中实例化FileDialogClient对象,并定义和启动“读”线程;
(5)在界面的“发送”按钮中调用FileDialogClient.java的发送方法;
(6)在界面的“退出”按钮中调用FileDialogClient.java的关闭方法。
(7)运行窗口程序,输入一个公用的FTP服务器的地址,连接成功后发送一些基本FTP命令,观察返回结果;
(8)新增快捷功能:
在用户界面设计状态,鼠标右击信息显示区,选择“事件”、“MouseMotion”、“mousedragged”,键入如下代码:
jTextField1.setText(jTextArea1.getSelectedText());
//鼠标加亮的字符串会自动出现在信息录入行中。
二、程序设计第二步
创建客户端数据传送进程FileDataClient.java
主要功能:
连接服务器数据端口、发送文件名、保存下载的文件或上传文件,文件传输完成后关闭数据连接。
过程:
(1)构造方法,FileDataClient(String ip,String port),主要功能是向服务器的数据端口请求连接;
(2)文件下载方法fileGet(fileName)[见附录代码]。
主要功能是准备当地磁盘空文件,向服务器发送文件名(基于字符串输出流操作),然后接收网络文件数据并保存当地磁盘(基于字节流操作),关闭数据套接字。
在界面的“下载”按钮中调用该方法,如:
String fName=jTextField1.getText();
jTextField1.setText("");
new FileDataClient(ip,port).fileGet(fName);
注意,可能存在的问题:
若下载文件不成功,可在此行设置断点,查看3个变量ip, port fName的值是否符合预期; 检查你连接的数据端口是否减“1”了;
若文件是空,第一要检查文件关闭操作是否完成。
(3)文件上传方法filePut()[选做],主要功能是向服务器发送文件名,发送文件数据,关闭数据套接字。
在主界面的“上传”按钮中调用该方法。
new FileDataClient(ip,port).filePut();
三、程序设计第三步:
对话服务器程序FileDialogServer.java
对话服务器程序开启2021端口,用来传送双方的对话信息,如文件目录等信息。
四、程序设计第四步:
数据服务器端程序FileDataServer.java
数据传输程序专门提供文件上传和下载服务,开启2020端口,提供文件下载服务。
附录1:客户程序数据进程中的文件下载模块
public String fileGet(String fName) throws IOException{
boolean flag = false;
if(dataSocket != null){
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 "服务器连接失败.";
}
附录2:服务器对话服务程序中的文件目录服务模块(供参考):
pw.println("可供下载的文件如下:");
String fName1="E:\\";//给出下载目录路经
File f=new File(fName1);
String[] s=f.list();//获得目录下所有的子目录和文件名.
int len=s.length;
int n=0;
long filelength=0;
File temp;
while(n<len){
temp=new File(f+"\\"+s[n]);
if(temp.isFile()){
filelength=temp.length()/1024;//字节单位KB
pw.println(s[n]+" ["+filelength+"KB]");
}else
pw.println(s[n]+" [DIR]");
n++;
}
pw.println("************文件下载********************");
pw.println("输入文件全名,点击下载按钮,退出输入bye");