Java网络代码在接收对象之前执行
我有一个使用网络的Java游戏,我有一个客户端(使用套接字)从ObjectInputStream获取对象,运行在自己的线程中 发件人: 这很有效。获取对象并将其传递给侦听器,侦听器是链接到my main GameApp类的类 从侦听器(): “app”是处理接收到的所有新对象并处理它们的实例 从应用程序()(编辑:非摘要提供了更大的上下文): 我已经在调试器中一步一步地运行了这段代码,它工作得非常好。但是,当我正常运行它时,会得到一个空指针(输出如下所示:) 现在,我看到这个物体被打印了两次,所以我知道它已经收到了。。。但我得到一个空指针 我在函数中间添加了一个睡眠函数:Java网络代码在接收对象之前执行,java,sockets,networking,nullpointerexception,objectinputstream,Java,Sockets,Networking,Nullpointerexception,Objectinputstream,我有一个使用网络的Java游戏,我有一个客户端(使用套接字)从ObjectInputStream获取对象,运行在自己的线程中 发件人: 这很有效。获取对象并将其传递给侦听器,侦听器是链接到my main GameApp类的类 从侦听器(): “app”是处理接收到的所有新对象并处理它们的实例 从应用程序()(编辑:非摘要提供了更大的上下文): 我已经在调试器中一步一步地运行了这段代码,它工作得非常好。但是,当我正常运行它时,会得到一个空指针(输出如下所示:) 现在,我看到这个物体被打印了两次,所
else if(o instanceof GameList) {
GameList gameList = (GameList) o;
System.out.println("gamelist: " + gameList);
try {
Thread.sleep(1000); // sleep 100 still gave null pointer
} catch (InterruptedException e) {}
this.lobbyControl.gotGameList(gameList);
}
让它睡一阵子,这一切终于奏效了
知道我为什么要这样睡觉吗?有什么我应该做的不同吗?我不知道为什么我能够打印对象,而它仍然被认为是空的
编辑:添加了更多的上下文。好吧,我首先要说的是,发布的代码片段似乎有助于说明这个问题,但我不认为完整的情况已经描绘出来了。我会要求更多的代码,以帮助获得完整的上下文 话虽如此,我将提供以下指导:
…没有更多的代码,这是我能提供的最多的了。看起来
游说控制
是null
,而不是游戏列表
。如果gameList
为空,堆栈顶部将是gotGameList()
方法,而不是gotObject()
如果睡眠有助于解决问题,那么您必须在没有适当并发保护措施的情况下操作lobbyControl
成员。ObjectInputStream
在从流中完全读取对象之前不会返回对象,因此您的问题与没有完全读取对象无关
更新:我无法理解所有的代码,但似乎对正在构造的对象的引用泄漏到了线程(NetControl中的
客户端
),该线程在构造函数完成之前启动。如果是这样的话,那就太糟糕了。您不应该允许部分构造的对象对另一个线程可见。只是为了改进我的评论…当我需要等待一个或多个线程完成时,我喜欢使用java.util.concurrent.CountDownLatch。很简单:
//game class
public class DummyGame
{
CountDownLatch signal;
public DummyGame( CountDownLatch signal)
{
this.signal = signal;
}
public void run()
{
doLogic();
signal.countDown();
}
}
//game controller class
public void run()
{
while (! gameOver)
{
CountDownLatch signal = new CountDownLatch(1); //wait one thread to finish
new thread(newGame(signal)).start();
//wait for game run() to finish
signal.await();
updateInterface();
}
}
这只是一个想法,希望能有所帮助。更多信息:我向一位朋友询问了这一点,他提到了阻塞,例如等待先准备好的东西。我不熟悉这个概念,但我猜我可能想做的是等待我的对象完全接收(伪代码:Thread.sleep(直到对象完全接收);但我不确定当我需要等待一个(或多个)线程时,将如何完成(或如何检查何时完成)我使用倒计时锁存器进行同步。为什么要测试null?是否计划发送null?@EJP代码最初是在while(in.readObject()!=null)时读取的,但我将其更改为您看到的,因为这不起作用…我现在看到,这可能在一段时间内是不必要的(true)循环。谢谢你指出这一点!@ZachHall,但你为什么要测试null?除非你发送null,否则你不会得到null。也许你认为这是一个EOS测试,但事实并非如此。
EOFEException
告诉你。Java序列化非常成熟和健壮。它没有任何不稳定或容易出错的地方。特别是相对于你自己的滚动序列化协议。在恢复以相同命名类的旧格式存储的二进制数据时,我们实际上遇到了多个问题。此外,如果一个类在包a中存在时被序列化,那么随后的重构会导致该类移动到包B,那么每次对所述对象的反序列化都会失败。a“roll your own”协议可以很容易地利用工厂和命令模式的强大功能,并允许您完全避免使用java的内省功能对性能造成的影响。而且,根据我的经验,它允许更好的线程安全性和更高的健壮性。感谢您的回复!如果您喜欢,我的所有代码都可以在线获得ll故事。-这是游戏应用程序,显示NetControl和其他一些。(我想还不能包含超过2个链接,但其余的可以在com.lgposse.net中找到。(我知道我的软件包太多了)。同样,这些都是我的经验,就像Java一样。许多方法可以简化序列化猫。出于上述原因,我不喜欢使用内置的Java对象序列化机制。我会看看你的代码,然后再给你回复。有趣的一点!我想我现在意识到,我的代码并没有给出完整的故事,正如我所说的那样bbyControl是一个从服务器请求对象的类。-这是我一直在处理的非抽象类(忘记了这一点),它创建了一个新的LobbyControl,然后在其构造函数方法中请求一个新的游戏列表。(可能它被认为是空的,因为请求在构造函数完成之前通过?)嗯,我再次检查,添加了System.out.println(LobbyControl);我得到了null。你肯定有什么问题。我将尝试移动请求,以便仅在类完全成形时调用它。完成后我会发回结果。感谢你的洞察力!我知道了
public void gotObject(Object o) {
// select instance:
if(o instanceof GameList) {
GameList gameList = (GameList) o;
System.out.println("gamelist: " + gameList);
this.lobbyControl.gotGameList(gameList);
}
}
Game ID: 0. Name: game1. Players: 1 / 1. // the object, as it is printed in Client.java
gamelist: Game ID: 0. Name: game1. Players: 1 / 1. // the object, as it is printed again in GameApp.java
Exception in thread "Thread-1" java.lang.NullPointerException
at com.lgposse.game.app.GameApp.gotObject(GameApp.java:61)
at com.lgposse.game.net.NetControl.gotObject(NetControl.java:47)
at com.lgposse.net.client.Client.run(Client.java:49)
else if(o instanceof GameList) {
GameList gameList = (GameList) o;
System.out.println("gamelist: " + gameList);
try {
Thread.sleep(1000); // sleep 100 still gave null pointer
} catch (InterruptedException e) {}
this.lobbyControl.gotGameList(gameList);
}
//game class
public class DummyGame
{
CountDownLatch signal;
public DummyGame( CountDownLatch signal)
{
this.signal = signal;
}
public void run()
{
doLogic();
signal.countDown();
}
}
//game controller class
public void run()
{
while (! gameOver)
{
CountDownLatch signal = new CountDownLatch(1); //wait one thread to finish
new thread(newGame(signal)).start();
//wait for game run() to finish
signal.await();
updateInterface();
}
}