Server-Komponente

Zum besseren Verständnis wird zunächst nochmals der Klassenaufbau der Server-Komponente aus Kapitel 3.6 angegeben. Diese Hierarchie muss um zwei Klassen erweitert werden, um die Netzwerkfunktionalität des Servers und um die Klasse Daten. Der wesentliche Unterschied zur Darstellung in Kapitel 3.6 besteht darin, dass nun anstelle der lediglich skizzierten Aufrufe der Netzwerkfunktionen die tatsächliche Implementierung erfolgt. Im Folgenden wird zunächst die main-Methode der Klasse betrachtet, die den Server selbst steuert.

kap71 

Abb. 7.1: Klassenhierarchie der Server-Komponente

code 

import java.awt.*;
import java.net.*;
import java.io.*;
public class Server {

    static ServerSpiel ss;
    static SVServer network;

    public static void main(String[] arguments) throws IOException  {

      while (true) {

        try {

          network = new SVServer();
          System.out.println("Netzwerk erstellt"); 
          ss = new ServerSpiel();
          System.out.println("Spiel initialisiert");

        } catch (IOException e) {

          System.out.println("IO-Exception!");

        }
        spiele();

      }

    }

Die Funktionalität dieser Methode ist leicht zu verstehen. In einer while-Schleife werden Anfragen von Clients verarbeitet. Hierbei wird zunächst eine Netzwerkanbindung erstellt. Anschließend wird die Methode spiele aufgerufen, in der die eigentliche Spielfunktionalität enthalten ist.

Zu Beginn dieser Methode wartet der Server auf Daten vom Client. Hierbei wird stets davon ausgegangen, dass der Client das Spiel beginnt.

code 

    static void spiele () throws IOException{

      boolean serverDran=false, spielEnde=false, einTreffer=false, zweiTreffer=false;

      Point koord = new Point(0,0);
      Daten daten;

      //Warten, bis der Client eine Position (x,y) schickt
      daten = network.getData();

Anschließend wird ein Spieldurchlauf in einer while-Schleife ausgeführt. Hierzu werden zunächst Schüsse des Clients verarbeitet.

code 

      while (spielEnde==false) {

        if (serverDran ==false) {

          if (ss.rechnerTreffer(daten.p)) {

            //Wir sind getroffen, teile dies dem Client mit
            System.out.println("Wir sind getroffen");

            daten.status=daten.TREFFER;
            network.sendData(daten);
            serverDran=false;

            if (ss.schiffZahlServer==0) {

              //Wir haben verloren????
              spielEnde=true;
              network.beenden();
              System.out.println("Server hat verloren");

              return;

            }

            //Erwarte naechsten Schuss
            daten = network.getData();

          } else serverDran=true;

        }

Ist der Server an der Reihe, so werden Koordinaten zunächst nach dem Zufallsprinzip ausgewählt, um einen ersten Treffer erzielen zu können.

code 

        else {

          //Jetzt schiessen wir
          if (einTreffer==false){

            koord=ss.schiesseInsBlaue();
            einTreffer=true;

            //Frage den Client ob Treffer.
            daten.status=daten.SCHUSS;
            daten.p=koord;
            network.sendData(daten);
            daten = network.getData();

            if (daten.status != daten.TREFFER) {

              einTreffer=false;
              serverDran=false;
              ss.spieler.spiel[koord.x][koor d.y]=ss.spieler .WASSER;

            } else

              ss.schiffZahlClient--;

          }

Hat der Server eine Koordinate eines gegnerischen Schiffs als Anhaltspunkt, so wird im Folgenden nach einer zweiten Koordinate gesucht, um die Richtung des Schiffs bestimmen zu können.

code 

          if ((zweiTreffer==false)&& (einTreffer==true)) {

            koord=ss.sucheZweitenTreffer();
            zweiTreffer=true;

            //Frage den Client ob Treffer.
            daten.status=daten.SCHUSS;
            daten.p=koord;
            network.sendData(daten);
            daten = network.getData();
            if (daten.status != daten.TREFFER) {

              // if Treffer beim Client: Richtung des
              //Schiffes jetzt bekannt.
              zweiTreffer=false;
              serverDran = false;
              ss.spieler.spiel[koord.x][koor d.y]=ss.spieler .WASSER;

            } else

              ss.schiffZahlClient--;

          }

Generell werden die Sprünge in die geeignete Schleife mit Hilfe der Variablen einTreffer und zweiTreffer realisiert. Sind zwei Koordinaten eines Schiffs bekannt, so kann dieses versenkt werden, weil die Richtung bekannt ist. Hierbei ist zu beachten, dass die Information, ob ein Schiff bereits versenkt ist, nicht mitgeteilt werden muss. Der Server muss diese Information daher durch geeignete Schüsse herausfinden. Ein Schiff ist dann versenkt, wenn keine Schusskoordinaten mehr gefunden werden können, die zu einem Schiff gehören könnten.

code 

          if ((zweiTreffer==true)&& (einTreffer==true)) {

            //Abschuss
            koord=ss.schiffVersenken();
            if (koord == null){

              //Schiff versenkt
              einTreffer=zweiTreffer=false;

            } else {

              //Frage den Client ob Treffer.
              daten.status=daten.SCHUSS;
              daten.p=koord;
              network.sendData(daten);
              daten = network.getData();
              if (daten.status != daten.TREFFER) {

                serverDran = false;
                ss.spieler.spiel[ss.koord inate3.x][ss.koordinate3. y]= ss.spieler.WASSER;
                ss.koordinate3=null; 

              } else

                ss.schiffZahlClient--;

            }

          }

Nachdem ein Schiff versenkt wurde, muss überprüft werden, ob der Gegner noch über Schiffe verfügt.

code 

          //Sind wir immer noch dran?
          if (ss.schiffZahlClient<=0){

            //gewonnen
            spielEnde=true;
            System.out.println("Server hat gewonnen");

            network.beenden();
            return;

          }

        }//

      } //while spiel

    } //Methode spiel

}//Klasse

Es wurde bereits mehrfach betont, dass das Ziel der Erstellung dieser Anwendung darin besteht, Komponenten so zu entwickeln, dass sie ohne Modifikationen größeren Umfangs kombiniert werden können. Eine Änderung der anderen Klassen des Servers ist daher nicht notwendig.


SPNavRight SPNavRight SPNavRight
BuiltByNOF