《互联网程序设计(Java)》——课程笔记5:网络文件传送程序设计

学会基本的文件传输FTP程序设计技术。

前两讲我们学会了使用TCP套接字(Socket),能实现字符串的发送和接收功能,简单地做到了客户机和服务器的对话。

今天,我们进一步学习TCP套接字,利用它的字节传输技术,实现网络文件传输。

文件传输协议规定(RFC 959 FTP.txt),网络文件传输中用两个TCP端口来实现:

一个端口(21号)用来对话,传递控制信息,总是开启;

一个端口(20号)实现文件数据传递服务,有数据传输服务时开启。

《互联网程序设计(Java)》——课程笔记5:网络文件传送程序设计

                                           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);

 

《互联网程序设计(Java)》——课程笔记5:网络文件传送程序设计

 

部分具体代码:

    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 "服务器连接失败.";
}
}