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
相关产品推荐
相关产品推荐

