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

JavaFx服务端接收序列化图形对象绘图报错求助

问题排查与解决:JavaFX服务端空指针与端口占用异常

错误信息

[!] Server waiting.
Cannot invoke "task2.SerializableRectangle.getX()" because "this.m_rectangle" is null
Rectangle (Object) (10.0,10.0,200.0,200.0).
Exception in thread "Server Thread" java.lang.RuntimeException: java.net.BindException: Address already in use: bind
    at task2.Server.run(Server.java:30)
    at java.base/java.lang.Thread.run(Thread.java:1589)
Caused by: java.net.BindException: Address already in use: bind
    at java.base/sun.nio.ch.Net.bind0(Native Method)
    at java.base/sun.nio.ch.Net.bind(Net.java:555)
    at java.base/sun.nio.ch.Net.bind(Net.java:544)
    at java.base/sun.nio.ch.NioSocketImpl.bind(NioSocketImpl.java:629)
    at java.base/java.net.ServerSocket.bind(ServerSocket.java:393)
    at java.base/java.net.ServerSocket.<init>(ServerSocket.java:275)
    at java.base/java.net.ServerSocket.<init>(ServerSocket.java:168)
    at task2.Server.run(Server.java:17)
    ... 1 more

需求目标

运行一个带JavaFX的服务端,接收客户端发送的序列化图形对象描述,在JavaFX Canvas上绘制该图形。

相关代码

Server.java

public class Server implements Runnable {
    private SerializableRectangle m_rectangle = null;
    @Override
    public void run() {
        ServerSocket server = null;
        try {
            while(true) {

                server = new ServerSocket(2345);
                System.out.println("[!] Server waiting.");

                Socket pipe = null;
                pipe = server.accept();

                ObjectInputStream serverInputStream = new ObjectInputStream(pipe.getInputStream());
                m_rectangle = (SerializableRectangle) serverInputStream.readObject();

                System.out.println(m_rectangle.toString());
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    public Rectangle getRectangle(){
        return new Rectangle(
                (int)m_rectangle.getX(),
                (int)m_rectangle.getY(),
                (int)m_rectangle.getWidth(),
                (int)m_rectangle.getHeight()
        );
    }
}

ServerApplication.java

public class ServerApplication extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        stage.setTitle("Server Application");
        Group root = new Group();
        Canvas canvas = new Canvas(300, 250);
        GraphicsContext context = canvas.getGraphicsContext2D();
        drawShape(context);
        root.getChildren().add(canvas);
        stage.setScene(new Scene(root));
        stage.show();
    }

    private void drawShape(GraphicsContext context)
    {
        try{
            Server server = new Server();
            Thread serverThread = (new Thread(server));
            serverThread.setName("Server Thread");
            serverThread.setDaemon(true);
            serverThread.start();

            Rectangle rect = server.getRectangle();
            context.fillRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
            context.setStroke(Color.BLUE);

        }catch (Exception ex){
            System.out.println(ex.getMessage());
        }
    }

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

Client.java

public class Client {
    public static void main(String[] args) {
        Socket socket = null;
        try {
            socket = new Socket(InetAddress.getLocalHost(), 2345);

            ObjectOutputStream clientOutputStream = new ObjectOutputStream(socket.getOutputStream());
            clientOutputStream.writeObject(new SerializableRectangle(10, 10, 200, 200));
            clientOutputStream.close();

        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

问题分析与解决方案

1. 空指针异常(m_rectangle为null)

原因:启动服务端线程后立即调用server.getRectangle(),此时线程还未完成客户端连接和对象读取,m_rectangle仍为null,直接调用getX()触发空指针。同时,JavaFX UI操作必须在UI线程执行,原代码跨线程操作UI也存在风险。

解决:

  • 采用回调机制,在服务端接收到图形对象后通知UI线程绘制;
  • 使用Platform.runLater()确保绘制操作在JavaFX UI线程执行。

2. 端口占用异常(BindException)

原因:Server类的run()方法将new ServerSocket(2345)放在while(true)循环内,每次循环都会重新绑定端口,第一次绑定后端口已被占用,后续循环触发异常。

解决:将ServerSocket的创建移到循环外部,仅绑定一次端口,循环内只处理客户端连接。

修改后的完整代码

Server.java

import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import javafx.application.Platform;
import javafx.scene.shape.Rectangle;

public class Server implements Runnable {
    private final RectangleReceivedListener listener;

    // 回调接口,用于通知UI更新
    public interface RectangleReceivedListener {
        void onRectangleReceived(Rectangle rect);
    }

    public Server(RectangleReceivedListener listener) {
        this.listener = listener;
    }

    @Override
    public void run() {
        // ServerSocket移到循环外,仅绑定一次端口
        try (ServerSocket server = new ServerSocket(2345)) {
            System.out.println("[!] Server waiting.");
            while (true) {
                // 使用try-with-resources自动关闭Socket
                try (Socket pipe = server.accept()) {
                    ObjectInputStream inputStream = new ObjectInputStream(pipe.getInputStream());
                    SerializableRectangle serialRect = (SerializableRectangle) inputStream.readObject();
                    System.out.println(serialRect.toString());

                    // 转换为JavaFX Rectangle
                    Rectangle fxRect = new Rectangle(
                            (int) serialRect.getX(),
                            (int) serialRect.getY(),
                            (int) serialRect.getWidth(),
                            (int) serialRect.getHeight()
                    );

                    // 通知UI线程绘制
                    Platform.runLater(() -> {
                        if (listener != null) {
                            listener.onRectangleReceived(fxRect);
                        }
                    });
                } catch (IOException | ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ServerApplication.java

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class ServerApplication extends Application {
    @Override
    public void start(Stage stage) {
        stage.setTitle("Server Application");
        Group root = new Group();
        Canvas canvas = new Canvas(300, 250);
        GraphicsContext context = canvas.getGraphicsContext2D();
        root.getChildren().add(canvas);
        stage.setScene(new Scene(root));
        stage.show();

        // 启动服务端并设置回调
        Server server = new Server(rect -> {
            // 在UI线程绘制矩形
            context.setFill(Color.LIGHTGRAY);
            context.fillRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
            context.setStroke(Color.BLUE);
            context.strokeRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
        });
        Thread serverThread = new Thread(server);
        serverThread.setName("Server Thread");
        serverThread.setDaemon(true);
        serverThread.start();
    }

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

额外说明

  • 确保SerializableRectangle类实现Serializable接口,否则序列化会失败;
  • 使用try-with-resources自动关闭网络资源,避免资源泄漏;
  • 客户端发送对象后,服务端会实时接收并更新Canvas上的图形。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 05:17:33