1

У меня есть Java-игра, в которой используется сеть, и у меня есть клиент (используя Socket), который извлекает объекты из ObjectInputStream, работающие в своем потоке.Выполнение Java-кода сети до получения объекта

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

«приложение» является экземпляр, который обрабатывает все новые объекты, полученные и сделок с ними.

Из приложения (GameApp.java) (Edit: неабстрактный 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) 

Теперь я вижу объект печатаемого дважды, так что я знаю, что это было получено ... но я получаю null указатель.

Я добавил функцию сна в середине функции:

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

и установка его спать на некоторое время, все это наконец-то работал.

Любая идея, почему мне нужно спать в этой теме? Есть ли что-то, что я должен делать по-другому? Я не уверен, почему я смог напечатать объект, пока он все еще считается нулевым.

Редактировать: добавлен контекст.

+0

Дополнительная информация: Я спросил друга об этом, и он упомянул о блокировке, например. ожидая, что что-то будет готово в первую очередь. Я не знаком с этой концепцией, но я предполагаю, что я могу сделать, это подождать, пока мой объект будет полностью принят (псевдокод: Thread.sleep (UNTIL OBJECT FULLY RECEIVED), но я не уверен, как это будет (или как проверить, когда это будет сделано.) –

+0

Когда мне нужно подождать нить (или несколько), я использую CountDownLatch для синхронизации. – Fernando

+0

Почему вы тестируете нуль? Планируете ли вы отправлять нули? – EJP

ответ

1

Похоже, lobbyControl является null, а не gameList. Если gameList были нулевыми, вершиной стека был бы метод gotGameList(), а не gotObject().

Если сон помогает решить проблему, тогда вы должны манипулировать членом lobbyControl без надлежащих гарантий параллелизма. ObjectInputStream не вернет объект, пока он не будет полностью прочитан из потока, поэтому ваша проблема не имеет ничего общего с тем, что он не полностью прочитал объект.


Update: Я не могу следовать весь код, но это, кажется, что ссылка на объект строится просочилась в потоке (client в NetControl), который запускается перед конструктором завершается. Если это так, это очень, очень плохо. Вы должны Никогда позволяют частично сконструированному объекту стать видимым для другого потока.

+0

Интересный момент!Думаю, я понимаю, что теперь я не рассказывал весь свой код, поскольку LobbyControl - это тот, кто запрашивает объект с сервера. https://github.com/lgp/LGP-Multiplayer-Network-Game-Framework/blob/HEAD/JCards/src/com/lgposse/cards/app/CardGameApp.java - вот не абстрактный класс, который я имею (забыл об этом), который создает новый LobbyControl, который затем в своем методе конструктора запрашивает новый список игр. (Возможно, это считается нулевым, потому что запрос проходит до завершения конструктора?) –

+0

@zshall Точно. См. Мое обновление. – erickson

+0

Хм, я проверил еще раз, добавив System.out.println (LobbyControl); и я получил нуль. Ты определенно готов к чему-то. Я попытаюсь переместить запрос так, чтобы он вызывается только тогда, когда класс полностью сформирован. Я отправлю назад результаты, когда это будет сделано. –

1

Ну, я начну, сказав, что опубликованные фрагменты кода помогают проиллюстрировать проблему, но я не думаю, что полная картина нарисована. Я бы попросил немного больше кода, чтобы помочь получить полный контекст.

Это, как говорится, я бы предложить следующие рекомендации:

  1. Не опирайтесь на Java встроенный в сериализации объекта. Это хорошо и проста в использовании, но может быть очень неустойчивой и подвержена ошибкам во время работы. Я бы предложил сериализацию пользовательских объектов и десериализацию .

  2. В зависимости от объема вашей игры NIO может быть нетривиальным выбором. Если вы придерживаетесь обычного ввода-вывода, то убедитесь, что у есть твердотельный менеджер потоков на месте, чтобы правильно обрабатывать потоки , имеющие дело с гнездом IO.

.. без дополнительного кода, это самое лучшее, что я могу предложить.

+0

Сериализация Java очень зрелая и надежная. Нет ничего нестабильного. r об этом склонна. Особенно по отношению к вашему протоколу сериализации. – erickson

+0

Мы столкнулись с несколькими проблемами, связанными с восстановлением двоичных данных, хранящихся в более старых форматах одного и того же класса. Кроме того, если класс сериализуется, когда он существует в пакете A, то рефакторинг по дороге заставляет этот класс перемещаться в пакет B, тогда десериализация указанного объекта будет терпеть неудачу каждый раз. Протокол «сворачивать свой собственный» может легко использовать возможности шаблонов Factory и Command и позволяет полностью избежать хитов производительности при использовании функций самоанализа java. Кроме того, это позволяет, по моему опыту, улучшить безопасность потока и повысить надежность. – claymore1977

+0

Спасибо за ответ! Весь мой код доступен онлайн, если вам нужна полная история. https://github.com/lgp/LGP-Multiplayer-Network-Game-Framework/blob/HEAD/GameFramework/src/com/lgposse/game/app/GameApp.java. Вот GameApp, https: // github. com/lgp/LGP-Multiplayer-Network-Game-Framework/tree/HEAD/GameFramework/src/com/lgposse/game/net показывает NetControl и некоторые другие. (Не могу включить более двух ссылок, но я думаю, но остальное можно найти в com.lgposse.net (у меня слишком много пакетов, я знаю). –

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

Это просто идея, надеюсь, что это поможет.