你还不会自己创建一个简单的网络文件传输程序吗?

79 阅读4分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第3篇文章,点击查看活动详情

这是一个网络文件传输程序设计(客户端)

之前已经学过了对话程序,也就是利用TCP套接字实现的字符流,现在这里叙述字节流实现的文件传输程序。首先建立一个文件夹,里面创建类似client和server两个类,然后再创建一个FileDialogClient.java(一个客户端程序文件)

FileDialogClient.java代码如下:(这是与服务器通话的程序)

import java.io.*;
import java.net.Socket;

public class FileDialogClient {
    private Socket socket;
    private PrintWriter pw;
    private BufferedReader br;

    public FileDialogClient(String ip, String port) throws IOException {
        socket = new Socket(ip, Integer.parseInt(port));
//        网络输出流字节流地址
        OutputStream socketOut = socket.getOutputStream();
        pw = new PrintWriter(
                new OutputStreamWriter(
                        socketOut, "utf-8"
                ), true
        );
        InputStream socketIn = socket.getInputStream();
        br = new BufferedReader(
                new InputStreamReader(socketIn, "utf-8")
        );
    }
        public void send (String msg){
            pw.println(msg);
        }

        public String receive () {
            String msg = null;
            try {
                msg = br.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return msg;
        }
        public void close () {
            try {
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

窗口文件FileClientFX.java的代码则如下:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

import java.io.File;
import java.io.IOException;

public class FileClientFX extends Application {
//    FileDialogClient fileDialogClient = new FileDialogClient();
    private Button btnExit = new Button("退出");
    private Button btnSend = new Button("发送");
    private Button btnDown = new Button("下载");
    private Button btnSave = new Button("保存");
    private Button btnConnect = new Button("连接");
    //    待发送信息的文本框
    private TextField tfSend = new TextField();
    //    显示信息的文本区域
    private TextArea taDisplay = new TextArea();
    //     输入ip地址的地方
    private TextField tfIP = new TextField();
    //    输入端口的地方
    private TextField tfPort = new TextField();
    //fileDialogClient不是局部变量,是本程序定义的一个FileDialogClient类型的成员变量
//    这个地方是一开始卡住的地方
    private FileDialogClient fileDialogClient;
    private String ip;   //定义ip和port这两个成员变量
    private String port;
    private Thread receiveThread; //定义成员变量,读取服务器信息的线程
    private void exit() {
        if (fileDialogClient != null) {
            fileDialogClient.send("bye");
            fileDialogClient.close();
        }
        System.exit(0);
    }
    public static void main(String[] args) {
        launch(args);
    }

    public void start(Stage primaryStage) {
        BorderPane mainPain = new BorderPane();
        //        负责ip地址和端口号,顶部区域
        HBox hBox1 = new HBox();
        hBox1.setSpacing(10);
        hBox1.setPadding(new Insets(10,20,10,20));
        hBox1.setAlignment(Pos.CENTER);
        hBox1.getChildren().addAll(new Label("IP地址:"),tfIP,new Label("端口号:"),tfPort,btnConnect);
//        内容显示区域
        VBox vbox = new VBox();
        vbox.setSpacing(10);  //各控件之间的间隔
        taDisplay.setMaxSize(Double.MAX_VALUE,Double.MAX_VALUE);
        vbox.setPadding(new Insets(10,20,10,20));
        vbox.getChildren().addAll(hBox1,new Label("信息显示区域:"),taDisplay,new Label("信息输入区域:"),tfSend);
//        可以自动扩展输入区域范围
        VBox.setVgrow(taDisplay, Priority.ALWAYS);
        mainPain.setCenter(vbox);
        HBox hBox = new HBox();
        hBox.setSpacing(10);
        hBox.setPadding(new Insets(10,20,10,20));
        hBox.setAlignment(Pos.CENTER_RIGHT);
        hBox.getChildren().addAll(btnExit,btnDown,btnSave,btnSend);
        mainPain.setBottom(hBox);
        Scene scene = new Scene(mainPain,700,400);
        primaryStage.setScene(scene);
        primaryStage.show();

//        解决显示框只读和自动换行
        taDisplay.setWrapText(true);
        taDisplay.setEditable(false);
        btnConnect.setOnAction(event -> {
            String ip = tfIP.getText().trim();
            String port = tfPort.getText().trim();

            try {
                fileDialogClient = new FileDialogClient(ip,port);
                String firstMsg = fileDialogClient.receive();
                taDisplay.appendText(firstMsg + "\n");
//                receiveThread的函数撰写即多线程函数写法
//                这里这个人想到了启用发送按钮,停用连接按钮
                btnSend.setDisable(false);
                btnConnect.setDisable(true);
                receiveThread = new Thread(()->{
                    String msg;
                    while ((msg = fileDialogClient.receive()) != null) {
                        String msgTemp = msg;
                        Platform.runLater(()->{
                            taDisplay.appendText(msgTemp + "\n");
                        });
                    }
                    Platform.runLater(() -> {
                        taDisplay.appendText("对话已关闭! \n");
                    });
                }, "my-readServerThread");
                receiveThread.start();
            } catch (Exception e) {
                taDisplay.appendText("服务器失败!" + e.getMessage() + "\n");
            }
        });
        primaryStage.setOnCloseRequest(event -> {
            exit();
        });
        btnExit.setOnAction(event -> {
            exit();
        });
        btnSend.setOnAction(event -> {
            String sendMsg = tfSend.getText().trim();
            fileDialogClient.send(sendMsg);//向服务器发送一串字符
            taDisplay.appendText("客户端发送:" + sendMsg + "\n");
            tfSend.clear();
//    感觉应该是这里导致窗口崩掉
//            String receiveMsg = fileDialogClient.receive();//从服务器接收一行字符
//            taDisplay.appendText(receiveMsg + "\n");
            if (sendMsg.equals("bye")) {
                btnConnect.setDisable(false);
                btnSend.setDisable(true);
            }
        });


//        btnSave.setOnAction(event -> {
//            TextFileIO textFileIO = new TextFileIO();
//            textFileIO.append(
//                    LocalDateTime.now().withNano(0) + "\n" + taDisplay.getText()
//            );
//        });
//
       btnDown.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 {
               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();
           }
        });

    }
}

到这一步,要运行下看看窗口是否弹出来了,如果报错请看

这是数据传送文件,主要运用了字节流,注释都写在上面了,如果有哪块不是很明白。可以在下面评论

import java.io.*;
import java.net.Socket;

public class FileDataClient {
    private Socket dataSocket;
    public FileDataClient(String ip,String port) {  //这个地方在一开始卡了,构造方法的时候要分配成员变量,赋予其ip地址还有端口号
        try {
        dataSocket = new Socket(ip, Integer.parseInt(port));
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    public void getFile(File saveFile) throws IOException {
        if (dataSocket != null) {          //在这里是判断如果没有数据传过来则不返回,在本地创建文件
            FileOutputStream fileOut = new FileOutputStream(saveFile);    //这个就是在本地新建一个文件
            byte[] buf = new byte[1024];   //这个则是用来缓存接收到的字节数据
//          这里分别是网络字节输入流和输出流
            InputStream socketIn = dataSocket.getInputStream();
            OutputStream socketOut = dataSocket.getOutputStream();
//          然后是字符串读写功能,要向服务器发送请求的名字,字符串写入网络输出流传来的文件信息
            PrintWriter pw = new PrintWriter(new OutputStreamWriter(socketOut,"utf-8"),true);
            pw.println(saveFile.getName());
            int size = 0;   //数据文件大小判断,只要读到数据就会改变
            while ((size = socketIn.read(buf)) != -1) {  //读一块到缓存,如果读取结束就返回-1
                fileOut.write(buf,0,size);  //写块到文件
            }
            fileOut.close();
            if (dataSocket != null) {
                dataSocket.close();
            }
            else {
                System.out.println("连接ftp数据服务器失败");
            }
        }
    }
}