2016-10-14 2 views
-1

Hallo all :) Entschuldigung für diese wirklich lange Frage, aber das braucht etwas Erklärung.Senden von Nachrichten an Clients TCP-Verbindung schlägt mit einer Nullpointexception

Ich bekam einen Auftrag, wo ich ein sehr einfaches Spiel in ein 2-Spieler-Multiplayer-Spiel verwandeln muss. Der Grund, warum wir dieses Spiel machen müssen, ist, mehr über Threads und Nebenläufigkeit zu lernen. Ich habe noch nie mit Parallelität oder mehreren Threads gearbeitet.

Meine Idee ist, einen TCP-Server zu erstellen, wie ich es in GameServer.java gemacht habe, wo ich ein neues ServiceObject für jeden Spieler erstelle. Ich erstelle für jedes ServiceObject einen Thread, in dem ich Befehle von einem Client erhalte, handle und sende.

Gameserver.java

package server; 

import java.io.IOException; 
import java.net.ServerSocket; 
import java.net.Socket; 
import java.util.ArrayList; 

public class GameServer { 
    public static void main(String[] args) throws IOException { 
     ServerSocket server = new ServerSocket(6789); 
     System.out.println("Waiting for clients to connect . . . "); 

     Socket s1 = server.accept(); 
     System.out.println("Clients connected."); 
     PlayerService servicep1 = new PlayerService(s1); 
     Thread t1 = new Thread(servicep1); 
     Socket s2 = server.accept(); 
     System.out.println("Clients connected."); 
     PlayerService servicep2 = new PlayerService(s2); 
     Thread t2 = new Thread(servicep2); 
     t1.start(); 
     t2.start(); 
     servicep1.sendDataToClient("ready"); 
     servicep2.sendDataToClient("ready"); 
    } 
} 

PlayerService.java

package server; 

import java.io.IOException; 
import java.io.OutputStream; 
import java.io.PrintWriter; 
import java.net.Socket; 
import java.util.ArrayList; 
import java.util.Scanner; 
import java.util.concurrent.LinkedBlockingQueue; 

import game2016.Player; 

public class PlayerService extends Thread { 
    private Socket s; 
    private PlayerService opponent; 
    private Scanner in; 
    private PrintWriter out; 


    public PlayerService(Socket aSocket) { 
     this.s = aSocket; 
    } 

    public void setOpponent(PlayerService opponent) { 
     this.opponent = opponent; 
    } 

    public void run() { 
     try { 
      in = new Scanner(s.getInputStream()); 
      out = new PrintWriter(s.getOutputStream()); 
      try { 
       doService(); 
      } finally { 
//    s.close(); 
      } 
     } catch (IOException exception) { 
      exception.printStackTrace(); 
     } 
    } 

    public void doService() throws IOException { 
     while (true) { 
      if (!in.hasNext()) { 
       return; 
      } 
      String command = in.next(); 
      if (command.equals("QUIT")) { 
       return; 
      } else 
       recieveFromClient(command); 
     } 
    } 

    public void recieveFromClient(String command) throws IOException { 
     System.out.println(command); 
     if(command.equals("player")) { 
      String newPlayerName = in.next(); 
      int xPos = in.nextInt(); 
      int yPos = in.nextInt(); 
      String direction = in.next(); 
//   sendDataToOpponent("addOpponent " + newPlayerName + " " + xPos + " " + yPos + " " + direction); 
     } 
    } 

    public void sendDataToClient(String response) { 
     out.write(response + "\n"); 
     out.flush(); 
    } 

    public void sendDataToOpponent(String response) { 
     opponent.sendDataToClient(response); 
    } 

    } 

Um Daten von einem Client zu einem anderen Client-i an den Gegner servicelayer eine Referenz zu senden haben, wie ich die sendDataToOpponent aufrufen kann() Methode um Daten an ihn zu senden und wenn der Server kommunizieren muss, kann ich einfach sendDataToClient() vom Server aufrufen.

Mein Problem ist, dass ich das Öffnen meiner Kunden GUI zu beiden Clients aufschieben möchte.

Main.java (Client) - GUI-Code wurde weggelassen

private static Socket s; 
private static InputStream instream; 
private static OutputStream outstream; 
private static Scanner in; 
private static PrintWriter out; 
private static boolean isOpponentConnected; 

public static void main(String[] args) throws Exception { 
    openConnection(); 
    reciever(); 
    waitOpponentConected(); 
    launch(args); 
} 

public static void waitOpponentConected() throws Exception { 
    while(!isOpponentConnected) { 
     System.out.println("Waiting for opponent"); 
     Thread.sleep(2000); 
    } 
    System.out.println("Opponent is ready now"); 
} 

public static void openConnection() throws IOException { 
    s = new Socket("localhost", 6789); 
    System.out.println("Connection established"); 

    instream = s.getInputStream(); 
    outstream = s.getOutputStream(); 
    in = new Scanner(instream); 
    out = new PrintWriter(outstream); 
} 

public static void responseFromServer() throws IOException { 
    try { 
     while(in.hasNext()) { 
       String response = in.next(); 
       if(response.equals("ready")) { 
        isOpponentConnected = true; 
        System.out.println("Ready"); 
       } 
     } 
     } catch (Exception e) { 
     } 
} 

public static void reciever() { 
    Task<Void> task = new Task<Void>() { 

     @Override 
     protected Void call() throws Exception { 
      while(true) { 
       responseFromServer(); 
      } 
     } 

    }; 
    new Thread(task).start(); 
} 

public static void sendCommandToServer(String command) throws IOException { 
    out.print(command + "\n"); 
    out.flush(); 
} 

Ich habe einen Thread erstellt Befehle vom Server zu empfangen, und wenn beide Clients mit dem Server verbunden sind sendet er eine Zeichenfolge 'bereit' für jeden der Kunden. Mein Gedanke war, dass der Main-Thread schläft, bis isOpponentConnected wahr ist.

Aber mein Gameserver schlägt fehl und druckt eine Nullpointer-Ausnahme aus, wenn der zweite Client eine Verbindung zum Server herstellt. Ich habe seit Tagen gelesen und versucht, diesen Fehler zu beheben. Wenn ich den Code im Debug-Modus starte, erhalten beide Clients das Bereit-Signal und die GUI startet für beide Clients.

Exception in thread "main" java.lang.NullPointerException 
    at server.PlayerService.sendDataToClient(PlayerService.java:67) 
    at server.GameServer.main(GameServer.java:23) 

Können Sie Jungs sehen, was ich offensichtlich falsch mache?

Ich denke, diese Anfrage interessiert, weil es nicht nur die Null-Null-Wahrnehmung ist, es geht darum, TCP-Server-Client-Beziehungen zu strukturieren und die Kette, wenn Dinge initialisiert werden und bereit, wenn Threads und Verbindungen hergestellt werden.

Antwort

0

Es sollte innerhalb der von Ihnen geposteten PlayerService.java-Klasse repariert werden können.
Ich schlage vor, sich bewegende:

in = new Scanner(s.getInputStream()); 
out = new PrintWriter(s.getOutputStream()); 

von public void run() zu Ihrem PlayerService Konstruktor: public PlayerService(Socket aSocket)
Es sieht aus wie die Funktion sendDataToClient versucht, die out Variable zu verwenden, bevor es initialisiert wird.

+0

Es funktioniert jetzt. Also führt der Main-Thread von GameServer den sendDataToClient aus, bevor die Threads korrekt gestartet wurden? –

+0

@MikkelAndersen Threads werden normalerweise als Light Weight-Prozesse bezeichnet, so dass sie einen Bruchteil der Zeit teilen, um nacheinander ausgeführt zu werden.Gehen Sie beispielsweise so vor, dass Sie zwei Threads erstellen und jede Schleife 100 Mal drucken, auf welcher Iteration sie sich befindet. In seltenen Fällen würden Sie dasselbe Ergebnis finden. In Ihrem Beispiel haben Sie die Instanz erstellt, aber den Thread nicht direkt gestartet. Daher war der Hauptthread schneller von Ihrem neu erstellten Thread und wurde vor der Initialisierung der Eingabe- und Ausgabevariablen weiter ausgeführt. Sie hätten Ihren PlayerService-Thread auch direkt starten können, aber ich bin mir nicht ganz sicher –

Verwandte Themen