我报名参加金石计划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数据服务器失败");
}
}
}
}