《互联网程序设计》 -- 第4周网络文件的传输

这周是学习,设计一个网络文件传输程序

总述

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

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

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

网络对话和网络文件传输,使用TCPsocket编程,本质还是一样,对话过程,使用字符流来包装;而网络文件传输过程,则应该使用字节流来进行处理。

再说一遍,两者本质一样!

都是客户端向服务器发送请求,服务器判断,当服务器接收到的是下载文件的请求的时候就会调用方法吧文件(可封装成其他类型)写入网络流中,然后客户端就接收这个文件。本质还是一样的。

设计第一步:创建窗体界面

 如图所示,与前几周的窗体大致相似。增加了下载按钮

下载按钮的功能:

btnDownload.setOnAction(event -> {
  if(tfSend.getText().equals("")) //没有输入文件名则返回
    return;


  String fName = tfSend.getText().trim();
  tfSend.clear();

  FileChooser fileChooser = new FileChooser();
  fileChooser.setInitialFileName(fName);
  File saveFile = fileChooser.showSaveDialog(null);
  if (saveFile == null) {
    return;//用户放弃操作则返回
  }  
  try {
    //数据端口是2020
    new FileDataClient(ip, "2020").getFile(saveFile);
    Alert alert = new Alert(Alert.AlertType.INFORMATION);
    alert.setContentText(saveFile.getName() + " 下载完毕!");
    alert.showAndWait();
    //通知服务器已经完成了下载动作,不发送的话,服务器不能提供有效反馈信息
    fileDialogClient.send("客户端开启下载");
  } catch (IOException e) {
    e.printStackTrace();
  }
});
Download按钮

还有一种锦上添花的功能,实现鼠标拖拽就能复制内容到信息输入框:

//信息显示区鼠标拖动高亮文字直接复制到信息输入框,方便选择文件名
//taDispaly为信息选择区的TextArea,tfSend为信息输入区的TextField
//为taDisplay的选择范围属性添加监听器,当该属性值变化(选择文字时),会触发监听器中的代码
taDisplay.selectionProperty().addListener((observable, oldValue, newValue) -> {
  //只有当鼠标拖动选中了文字才复制内容
  if(!taDisplay.getSelectedText().equals(""))
    tfSend.setText(taDisplay.getSelectedText());
});

设计第二步,创建客户端数据传输程序

我们应该想到这个程序应该有的功能:连接服务器数据端口、发送文件名、保存下载的文件,文件传输完成后关闭数据连接。

我们可以在构造方法中向服务器发起连接

public FileDataClient(String ip,String port) throws IOException{
        dataSocket = new Socket(ip, Integer.parseInt(port));//服务器发起连接

    }

剩下的功能我们可以封装成一个getFile方法:

public void getFile(File saveFile) throws IOException {

        if (dataSocket != null) { // dataSocket是Socket类型的成员变量


            FileOutputStream fileOut = new FileOutputStream(saveFile);//新建本地空文件
            byte[] buf = new byte[1024]; // 用来缓存接收的字节数据
            //网络字节输入流
            InputStream socketIn = dataSocket.getInputStream();
            //网络字节输出流
            OutputStream socketOut = dataSocket.getOutputStream();

            //(2)向服务器发送请求的文件名,字符串读写功能
            PrintWriter pw = new PrintWriter(new OutputStreamWriter(socketOut, "utf-8"), true);
            pw.println(saveFile.getName());

            //(3)接收服务器的数据文件,字节读写功能
            int size = 0;
            while ((size = socketIn.read(buf)) != -1) {//读一块到缓存,读取结束返回-1
                fileOut.write(buf, 0, size); //写一块到文件
            }
            fileOut.flush();//关闭前将缓存的数据全部推出
            //文件传输完毕,关闭流
            fileOut.close();
            if (dataSocket != null) {
                dataSocket.close();
            }
        } else {
            System.err.println("连接ftp数据服务器失败");
        }
    }
getFile方法

众所周知,下载文件是一个交互的过程。

所以我们需要在一些关键的时间点提醒用户,或者说提示。如:文件开始下载时,完成下载时:

这里就是创建了一个对话框用来提示用户:

new FileDataClient(ip, "2020").getFile(saveFile);//数据端口为2020
    Alert alert = new Alert(Alert.AlertType.INFORMATION);
    alert.setContentText(saveFile.getName() + " 下载完毕!");
    alert.showAndWait();
    //通知服务器已经完成了下载动作,不发送的话,服务器不能提供有效反馈信息
    fileDialogClient.send("客户端开启下载");

这样我们的一个简易的网络文件传输程序就设计完成了。

实验中注意的点是在 把ip,port这两个成员变量使用的时候,不能将他们重新在连接按钮那里重新定义,不然ip和port的值就是null了。

错误示范:

 正确如下:

或者直接把获得ip和port的方法放在类里面而非方法里。

本次的所有代码都放在:https://wws.lanzous.com/ivMzch0al7a

认清现实,放弃幻想。 细节决定成败,心态放好,认真学习与工作。
原文地址:https://www.cnblogs.com/jyf2018/p/13746840.html