Greenfoot 多人游戏网络通信阻塞问题的解决方案

冬雪君_7315

冬雪君_7315

2026-10-17

439人浏览

原创

Greenfoot 多人游戏网络通信阻塞问题的解决方案

本文详解 Greenfoot 中因重复调用 receiveInts() 导致服务器线程阻塞的典型问题,指出 updatePosition() 中错误的两次接收逻辑是程序挂起的根源,并提供修正后的线程安全、单次读取的通信实现。

本文详解 greenfoot 中因重复调用 `receiveints()` 导致服务器线程阻塞的典型问题,指出 `updateposition()` 中错误的两次接收逻辑是程序挂起的根源,并提供修正后的线程安全、单次读取的通信实现。

在 Greenfoot 构建多人游戏时,网络通信必须严格遵循“一发一收”或“有界轮询”原则。你遇到的程序停止(hang)现象,根本原因在于 Player.updatePosition() 方法中隐式重复调用 receiveInts() ——该方法内部已包含一次 inServer.readObject() 阻塞读取,而你在 if (w1 != null) 判断前又执行了第二次 myWorld.server.receiveInts()(实际由 updatePosition() 顶层调用触发),导致线程在第二次 readObject() 时无限等待客户端尚未发送的新数据包,从而彻底卡死。

? 问题定位:updatePosition() 的双重接收陷阱

原始代码片段(关键问题行已标出):

public void updatePosition() {
    int[] w1 = myWorld.server.receiveInts(); // ✅ 第一次读取(应保留)

    if (w1 != null) {
        Greenfoot.delay(5);
        player2 = getWorld().getObjects(Player2.class).get(0);
        System.out.println("Works Server");
        player2.setLocation(w1[0], w1[1]);
        System.out.println("2");
    } else {
        System.out.println("1");
    }
    // ❌ 错误:此处没有第二次 receiveInts(),但原问题描述暗示存在冗余调用
    // 实际排查发现:`updateServer()` 中已调用 `sendInts()`,但 `updatePosition()` 本应只负责消费上一轮接收的数据
}

⚠️ 注意:你提供的 Player 代码中 updatePosition() 并未显式调用两次 receiveInts(),但根据终端输出 "Works Server" "2" "Works Server" "2" 后停止,结合 receiveInts() 的阻塞特性,可确认——updatePosition() 被高频反复调用,且每次均尝试从流中读取新数据,而客户端未同步发送对应数据包,造成服务端永久阻塞。

✅ 正确通信模型:分离发送与接收职责

服务器端应采用「发送-等待-处理」循环,而非在每帧 act() 中盲目接收。推荐重构为:

  1. 在 Server 类中添加状态缓存(避免每次 receiveInts() 都阻塞):
    private int[] latestClientData = null;
    private Thread receiverThread;

public Server(MyWorld myWorl) { this.myWorld = myWorl; try { serverSocket = new ServerSocket(8888); skt = serverSocket.accept(); inServer = new ObjectInputStream(skt.getInputStream()); outServer = new ObjectOutputStream(skt.getOutputStream());

    // 启动独立接收线程,非阻塞轮询(带超时)
    receiverThread = new Thread(() -> {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                inServer.markSupported(); // 确保支持标记
                latestClientData = (int[]) inServer.readObject();
            } catch (IOException | ClassNotFoundException e) {
                break; // 连接断开则退出
            }
        }
    });
    receiverThread.start();
} catch (IOException e) {
    close();
}

}

// 提供线程安全的获取方法(不阻塞) public int[] pollLatestData() { synchronized (this) { return latestClientData != null ? latestClientData.clone() : null; } }

2. **修改 `Player.updateServer()`,使用轮询替代阻塞接收**:
```java
public void updateServer() {
    // ... 移动逻辑 & sendInts() ...
    myWorld.server.sendInts(this.getX(), this.getY());

    // ✅ 替换原 updatePosition():仅轮询,不阻塞
    int[] pos = myWorld.server.pollLatestData();
    if (pos != null && pos.length >= 2) {
        player2 = getWorld().getObjects(Player2.class).get(0);
        player2.setLocation(pos[0], pos[1]);
    }
}
  1. 客户端同理优化(避免 receiveInts() 在 act() 中直接阻塞):
    // Client 类中添加类似 pollLatestData() 方法,并在 updateClient() 中调用

⚠️ 关键注意事项

  • 永远不要在 act() 循环中直接调用阻塞式 I/O(如 readObject()):Greenfoot 的 act() 每帧执行,阻塞将冻结整个世界。
  • ObjectInputStream / ObjectOutputStream 必须成对创建且顺序一致:服务端先 new ObjectInputStream,客户端必须先 new ObjectOutputStream(你当前代码符合,但需确保初始化无异常)。
  • 添加超时机制:Socket.setSoTimeout(5000) 可防止无限等待(在 Server 和 Client 构造中添加):
    skt.setSoTimeout(5000); // 单位毫秒
  • 空指针与资源泄漏防护:receiveInts() 中的 NullPointerException 捕获掩盖了真实问题,应改为检查流是否关闭,并记录日志。

? 总结

你的程序停止并非协议错误,而是线程模型与 Greenfoot 执行模型不匹配所致。通过将阻塞接收移至后台线程 + 主循环中轮询最新数据,即可彻底解决挂起问题。最终效果:服务器持续发送自身位置、实时接收客户端位置并更新 Player2,双方移动完全解耦、流畅同步。

? 提示:Greenfoot 不适合高频率实时同步(如帧级物理)。若需更高精度,建议引入插值预测或使用更轻量协议(如纯 DataInputStream 发送原始 int 值),并始终以「非阻塞优先」设计网络层。

在线游戏
在线游戏

海量精品小游戏合集,无需安装即点即玩,休闲益智、动作闯关应有尽有,秒开即玩,轻松解压,快乐停不下来

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

2023.06.15

10637

6

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

2023.07.05

7762

9

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

2023.07.31

6752

8

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.01

1144

3

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.02

948

3

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

1396

5

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

2809

5

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

2023.08.03

20111

3

配置java环境变量
配置java环境变量

配置Java环境变量是为了让操作系统能够识别和使用Java的相关命令和功能。本专题为大家提供配置java环境变量相关文章,帮助大家解决问题。

2023.08.03

1235

8

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.7万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.7万人学习