You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

服务器关闭时向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();
    }
}

关键说明

  1. 服务器端:

    • 用writeUTF替代writeBytes,通过字符串长度标记确保客户端准确读取完整内容
    • 添加flush()保证关闭前信号能发送到客户端
    • 调整资源关闭顺序,避免出现资源泄漏
  2. 客户端:

    • 新增独立监听线程,避免JavaFX主线程被阻塞导致UI卡顿
    • 同时处理主动接收的"Done"信号和服务器异常断开的IOException
    • 用Platform.runLater()执行UI操作,确保符合JavaFX线程安全规则
    • 保留原有的ConnectException处理,保证服务器未启动时直接显示错误界面

内容的提问来源于stack exchange,提问作者Celo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 23:45:39