在收到对象之前执行的Java网络代码

时间:2012-03-16 00:43:50

标签: java sockets networking nullpointerexception objectinputstream

我有一个使用网络的Java游戏,我有一个客户端(使用Socket)从ObjectInputStream中获取对象,在自己的线程中运行。

来自Client.java

        Object input = null;
        while(true) {
            input = in.readObject();
            if(input != null) {
                listener.gotObject(input);
            }
        }

这非常有效。获取该对象并将其传递给侦听器,该侦听器是链接到我的主GameApp类的类。

来自听众(NetControl.java):

public void gotObject(Object o) {
    System.out.println(o);
    app.gotObject(o);
}

“app”是处理收到的所有新对象并处理它们的实例。

来自应用(GameApp.java)(编辑:非抽象CardGameApp.java提供更大的背景):

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)

现在,我看到对象被打印了两次,所以我知道它已被收到......但我得到一个空指针。

我在函数中间添加了一个sleep函数:

    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);
    }

让它睡了一会儿,这一切终于奏效了。

知道为什么我需要像这样睡觉吗?有什么我应该做的不同吗?我不知道为什么我能够打印对象,因为它仍然被认为是空的。

编辑:添加了更多上下文。

3 个答案:

答案 0 :(得分:1)

好吧,我首先要说的是,发布的代码片段似乎有助于说明问题,但我不认为完整的图片是绘制的。我会要求更多代码,以帮助获得完整的上下文。

话虽如此,我会提供以下指导:

  1. 不要依赖java的内置对象序列化。这很好,而且 易于使用,但在运行时可能非常不稳定且容易出错。 我建议自定义对象序列化和反序列化 方案

  2. 根据您正在制作的游戏范围,NIO可能是一个 netter选择。如果你坚持使用常规IO,那么请确保你 有一个坚如磐石的线程管理器到位,以妥善处理 处理套接字IO的线程。

  3. ..没有更多代码,这是我能提供的最多。

答案 1 :(得分:1)

看起来lobbyControlnull,而不是gameList。如果gameList为空,则堆栈顶部为gotGameList()方法,而不是gotObject()

如果睡眠有助于解决问题,那么您必须在没有适当的并发安全措施的情况下操纵lobbyControl成员。 ObjectInputStream在从流中完全读取之前不会返回对象,因此您的问题与未完全读取对象无关。


更新:我无法遵循所有代码,但似乎对正在构造的对象的引用泄露给一个线程(client中的NetControl }),它在构造函数完成之前启动。如果是这种情况,则非常非常糟糕。您应该从不允许部分构造的对象对另一个线程可见。

答案 2 :(得分:0)

只是为了改进我的评论...当我需要等待一个或多个线程完成时,我想使用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();
    }
 }

这只是一个想法,希望它有所帮助。