服务器关闭时向JavaFX客户端抛出异常的实现方案
JavaFX客户端监听服务器关闭的解决方案
核心问题修正思路
- 客户端需启动独立线程监听服务器输入,避免阻塞JavaFX主线程
- 服务器改用
writeUTF发送"Done"信号,确保客户端能完整读取字符串 - 客户端需同时处理主动接收的关闭信号和服务器异常断开的情况
修改后的服务器代码(Server.java)
package body.core.ClientServer; import javafx.application.Application; import javafx.application.Platform; import javafx.scene.Scene; import javafx.scene.control.ScrollPane; import javafx.scene.control.TextArea; import javafx.stage.Stage; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.Date; public class Server extends Application { @Override public void start(Stage stage) throws Exception { TextArea ta = new TextArea(); Scene scene = new Scene(new ScrollPane(ta), 450, 200); stage.setTitle("Server"); stage.setScene(scene); stage.show(); new Thread(() -> { ServerSocket serverSocket = null; Socket socket = null; DataOutputStream outputToClient = null; try { serverSocket = new ServerSocket(8000); Platform.runLater(() -> ta.appendText("Server started at " + new Date() + '\n')); socket = serverSocket.accept(); Platform.runLater(() -> ta.appendText("Client connected at " + new Date() + '\n')); outputToClient = new DataOutputStream(socket.getOutputStream()); stage.setOnCloseRequest(event -> { try { if (outputToClient != null) { // 用writeUTF发送带长度标记的字符串,确保客户端准确读取 outputToClient.writeUTF("Done"); outputToClient.flush(); // 强制发送缓存数据 } } catch (IOException e) { e.printStackTrace(); } finally { // 按顺序关闭资源 try { if (socket != null) socket.close(); if (serverSocket != null) serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } Platform.runLater(() -> ta.appendText("Server closed at " + new Date() + '\n')); } }); } catch(IOException ex) { ex.printStackTrace(); } }).start(); } public static void main(String[] args) { launch(); } }
修改后的客户端代码(InitApplication.java)
package com.sailingclub.ui.sailingclub; import body.entities.Admin; import javafx.application.Application; import javafx.application.Platform; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.net.ConnectException; import java.net.Socket; public class InitApplication extends Application { DataOutputStream toServer = null; DataInputStream fromServer = null; Socket socket = null; Stage primaryStage; @Override public void start(Stage primaryStage) throws IOException, InterruptedException { this.primaryStage = primaryStage; try { socket = new Socket("localhost", 8000); fromServer = new DataInputStream(socket.getInputStream()); toServer = new DataOutputStream(socket.getOutputStream()); primaryStage.setOnCloseRequest(event -> { try { if (socket != null) socket.close(); } catch (IOException e) { e.printStackTrace(); } }); // 加载主界面 loadMainScene(); // 启动独立线程监听服务器消息 startServerListener(); } catch (Exception e) { if (e instanceof ConnectException) { loadErrorScene(); } else { e.printStackTrace(); } } } // 加载主界面 private void loadMainScene() throws IOException { FXMLLoader loader = new FXMLLoader(new File("src/main/java/com/sailingclub/ui/sailingclub/first/FirstPage.fxml").toURI().toURL()); Parent root = loader.load(); Scene scene = new Scene(root); primaryStage.setScene(scene); primaryStage.show(); } // 加载错误界面 private void loadErrorScene() throws IOException { FXMLLoader loader = new FXMLLoader(new File("src/main/java/com/sailingclub/ui/sailingclub/error.fxml").toURI().toURL()); Parent root = loader.load(); Scene scene = new Scene(root); primaryStage.setScene(scene); primaryStage.show(); } // 启动服务器监听线程,避免阻塞UI private void startServerListener() { new Thread(() -> { try { while (true) { String message = fromServer.readUTF(); if ("Done".equals(message)) { // 收到服务器关闭信号,切换到错误界面 Platform.runLater(this::loadErrorScene); break; } } } catch (IOException e) { // 服务器异常断开时触发,同样切换到错误界面 Platform.runLater(() -> { try { loadErrorScene(); } catch (IOException ex) { ex.printStackTrace(); } }); } finally { // 关闭所有资源 try { if (fromServer != null) fromServer.close(); if (toServer != null) toServer.close(); if (socket != null) socket.close(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } public static void main(String[] args) { var beginning=new Admin(); beginning.notifyGenerator("Membership"); beginning.notifyGenerator("Storage"); launch(); } }
关键说明
服务器端:
- 用
writeUTF替代writeBytes,通过字符串长度标记确保客户端准确读取完整内容 - 添加
flush()保证关闭前信号能发送到客户端 - 调整资源关闭顺序,避免出现资源泄漏
- 用
客户端:
- 新增独立监听线程,避免JavaFX主线程被阻塞导致UI卡顿
- 同时处理主动接收的"Done"信号和服务器异常断开的
IOException - 用
Platform.runLater()执行UI操作,确保符合JavaFX线程安全规则 - 保留原有的
ConnectException处理,保证服务器未启动时直接显示错误界面
内容的提问来源于stack exchange,提问作者Celo
相关产品推荐
相关产品推荐

