You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用EventHandler与Java.net Socket时JavaFX应用崩溃问题排查

嘿,我帮你排查了下你的JavaFX聊天应用崩溃无响应的问题,主要是几个IO和线程处理的坑导致的,咱们一步步来解决:

问题根源分析

你的应用按下Enter后卡住崩溃,核心问题有这几个:

  • UI线程被阻塞:JavaFX的handle方法运行在UI主线程中,而dis.readLine()是阻塞式IO操作,会直接卡住UI线程,导致应用出现无响应弹窗。
  • 流与Socket的错误管理:每次发送消息都重新创建DataInputStream/DataOutputStream,还在handle里关闭了dos——这会连带关闭底层的Socket,后续再发送消息直接报错。
  • IO操作的换行符不匹配:客户端用writeBytes(message)发送消息时没加换行符,但服务器用readLine()读取,readLine()会一直等待换行符才返回,导致服务器和客户端双双卡住。
  • 服务器端只能处理一次交互:服务器接收一个连接、处理一条消息后就关闭了ServerSocket,根本没法支持持续的聊天。
修复方案

咱们逐个解决这些问题,下面是修改后的完整代码和关键说明:

关键修改点

  1. 后台线程处理网络接收:把接收服务器消息的逻辑放到单独线程里,避免阻塞UI主线程,同时用Platform.runLater()确保UI更新操作在JavaFX的UI线程执行。
  2. 统一管理Socket和流:在客户端初始化阶段就创建Socket和对应的流,不要每次发送消息都重新实例化,也不要随意关闭流(除非要断开连接)。
  3. 统一消息的换行符:发送消息时添加\n,确保readLine()能正确读取完整消息。
  4. 让服务器支持持续通信:服务器添加循环,持续接收客户端的消息,直到连接断开再关闭资源。

修改后的客户端代码

import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.scene.Parent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.*;

public class ChatAppClient extends Application {
    private TextArea messages = new TextArea();
    private TextField input;
    private static String ipAdress = null;
    private DataInputStream dis;
    private DataOutputStream dos;
    private Socket client;

    public Parent mainScene() throws UnknownHostException, IOException {
        // 读取IP地址
        String userDir = System.getProperty("user.dir");
        File file = new File(userDir + "\\..\\IPAdress.txt");
        RandomAccessFile raFile = new RandomAccessFile(file, "rw");
        raFile.seek(0);
        ipAdress = raFile.readLine();
        raFile.close();

        // 初始化Socket和流,仅执行一次
        client = new Socket(ipAdress, 5000);
        System.out.println("Connected to Server.");
        dis = new DataInputStream(client.getInputStream());
        dos = new DataOutputStream(client.getOutputStream());

        // 启动后台线程接收服务器消息
        new Thread(() -> {
            try {
                String serverMessage;
                while ((serverMessage = dis.readLine()) != null) {
                    // 必须在UI线程更新TextArea,否则会报错
                    Platform.runLater(() -> messages.appendText(serverMessage + "\n"));
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();

        // UI布局设置
        messages.setPrefHeight(220);
        input = new TextField();
        VBox root = new VBox(20, messages, input);
        root.setPrefSize(400, 400);

        // TextField的Enter事件处理
        input.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                try {
                    String message = "Client: " + input.getText().trim();
                    if (!message.isEmpty()) {
                        // 发送消息时添加换行符,匹配服务器的readLine()
                        dos.writeBytes(message + "\n");
                        input.clear();
                    }
                } catch (IOException ioE) {
                    ioE.printStackTrace();
                }
            }
        });
        return root;
    }

    public void start(Stage mainStage) {
        try {
            mainStage.setScene(new Scene(mainScene()));
            mainStage.show();
        } catch (UnknownHostException uHE) {
            uHE.printStackTrace();
        } catch (IOException iOE) {
            iOE.printStackTrace();
        }
    }

    @Override
    public void stop() throws Exception {
        // 应用关闭时统一关闭资源
        if (dis != null) dis.close();
        if (dos != null) dos.close();
        if (client != null) client.close();
        super.stop();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

修改后的服务器代码

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class ChatAppServer {
    public static void main(String[] args) {
        try {
            ServerSocket caServerSocket = new ServerSocket(5000);
            System.out.println("Waiting for Client...");
            Socket nSocket = caServerSocket.accept();
            System.out.println("Client connected.");

            DataInputStream clientMessages = new DataInputStream(nSocket.getInputStream());
            DataOutputStream clientMessagesBack = new DataOutputStream(nSocket.getOutputStream());

            // 循环接收客户端消息,直到连接断开
            String clientMessage;
            while ((clientMessage = clientMessages.readLine()) != null) {
                System.out.println("Received: " + clientMessage);
                // 回传消息时同样添加换行符
                clientMessagesBack.writeBytes(clientMessage + "\n");
            }

            // 连接断开后关闭资源
            clientMessages.close();
            clientMessagesBack.close();
            nSocket.close();
            caServerSocket.close();
        } catch (IOException ioE) {
            ioE.printStackTrace();
        }
    }
}

现在你可以运行修改后的代码:先启动服务器,再启动客户端,输入消息按下Enter,就能正常发送并接收服务器回传的消息,应用也不会再卡住崩溃了。

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

火山引擎 最新活动