# "Jungfernflug" der Socketprogrammierung für ein Mühlespiel



## Nachbar (17. Dez 2009)

Anforderungen :
Die OnlineVariante des MühleSpiels soll in Form einer Client/ ServerArchitektur umgesetzt werden. Zu diesem Zweck soll ein Server implementiert werden, der auf Spielanfragen wartet. Melden sich zwei Spieler an, wird ein Spiel gestartet. Der Spielzustand soll zentral auf dem Server verwaltet werden.

Der Informationsaustausch zwischen den zwei Spielern und dem Server soll
über ein verbindungsorientiertes und sicheres Protokoll realisiert werden.
Greifen Sie zu diesem Zweck auf die im Paket java.net bereitgestellten
Klassen Socket ( = Client ) und ServerSocket ( = Server ) zurück.

Jeder Spieler soll zu jedem Zeitpunkt in der Lage sein, ein Spiel abzubrechen.
Der Gegenspieler soll über den Abbruch des Spiels informiert werden. Nach
Ende oder Abbruch eines Spiels soll ein neues Spiel gestartet werden können.

--------------------------------------------------------------------------------------------------

da hab ich gleich mal ne Frage...also ganz rustikal natürlich, wie immer *g*, ich muss einen Server  programmieren, das steht in nem Buch( zumindest Ansatzweise)...die Frage ist gleich ...bei meiner Klasse Mühle, muss ich doch den Konstruktor ändern oder???, denn ich schicke ja dem, der mitspielen will ein Paket an Daten, mit nem zweiten Konstrukuor (dem ich nen Port und ne Internetadresse übergebe), spielt der andere Spieler und "wirft" alles wieder zu mir, ...hab da sowas in der Art gelesen ??? geht das in die richtige Richtung ???

nochmal so für mich: wir haben ja die Offlinevariante fertig, müssen die Klasse, im speziellen, dem Konstruktor wie oben beschrieben abändern und etwaige Ausnahmenbedingen hier und da einbauen und vielleicht noch ein paar Methoden in der Klasse Mühle ergänzen ... ist das so für den Anfang der richtige Weg oder laufen wir Gefahr in einer Sackgasse zu landen ???

wie wäre denn im worst case, der Fahrplan für einen Ausweg ???

Grüße !!!


----------



## SlaterB (17. Dez 2009)

alle bisherigen Klassen zum Spiel sollten weitgehend unverändert bleiben,
die Sockets müssen den Spielstand übertragen und höchstens noch anstelle der Spieler spielen,

wenn es also bisher etwa

Zug x = liesEingabeVonKonsoleFürSpieler1();
spiel.verarbeiteZug(x);

gab, dann lautet es nun

Zug x = liesEingabeVonSocketSpieler1();
spiel.verarbeiteZug(x);

für die Spiel-Klasse ändert sich also gar nix,
außer zur Übertragung muss es vielleicht Methoden a la
speichereSpielstandAlsString() 
+
baueSpielstandAusStringAuf()
oder so geben,
vielleicht aber auch nicht mal das, wenn alle den Anfangsstand kennen + die Liste der getätigten Spielzüge, 
dann weiß jeder wie es steht,

der Server muss natürlich prüfen, dass die Sockets erlaubte Züge liefern, aber das sollte im Einzelspiel bei den Eingaben auch passieren,
ebenso Sieg bestimmen usw., das müsste es alles geben bzw. ist recht unabhängig davon, ob das Spiel übers Netz läuft und von einem unabhängigen Server kontrolliert wird


----------



## madboy (17. Dez 2009)

Was mich eher irritiert ist folgendes:


Nachbar hat gesagt.:


> sicheres Protokoll


Was bedeutet "sicher" hier? SSL? One time pad Verschlüsselung? ;-)

Zu deiner Frage: Ich habe keine Ahnung, wie dein Konstruktor aussieht und was du bisher gemacht hast. Unabhängig davon vermute ich aber, dass der Konstruktor dein geringstes Problem sein wird und der auch nicht angepasst werden muss. Versuch's einfach, dann siehst du ob der Konstruktor passt oder evtl. ein zweiter her muss.


----------



## SlaterB (17. Dez 2009)

sicher vielleicht auch im Sinne von 'verlustfrei', da bieten sich Sockets mit verbindungsorientierten TCP ja an


----------



## Nachbar (17. Dez 2009)

aja ok,

falls es noch wichtig werden wird, was ich vermute, hier schon ma der link , wo die offline Variante des Spiels zu finden ist...

http://www.java-forum.org/awt-swing-swt/92203-muehlespiel-funktioniert-neustartproblem.html

ich muss mich auch erstma weiter in das Thema einlesen, was ich aber noch nicht verstehe wie das funzt... immer alles schon gesagt, irgendwas hier und da auf nem Server ablegen und listener und sonst was spielen...
direkt meine frage ist, wie man das selbst am rechner testet, dass das was man geproggt hat,auch funktioniert. 
ich meine das so: (p.s. laut anforderungen kann man das testen mit localhost oder sowas, moment ich guck mal eben nach:

 "Sie können ihre Umsetzung der OnlineVariante
des Spiels zunächst auf einem
Rechner testen, indem Sie das Loopback Network Interface dieses Rechners
nutzen ( localhost, IP4Adresse:
127.0.0.1 )" ...........

die wohl eigentlichen fragen kommen jetzt ( sorry bin schon völlig wirre *g*):

1. ich sitze vor meinem eclipse , starte das programm und muss es dann nochmal starten ??? dies implizert meine 2. frage

2. wenn ich anfange , bin ich dann gleichzeitig Server und Client ....also wie muss ich die Anforderungen s.o. interpretieren, ist es egal, sollte ich lieber nochmal nachfragen wie die das meinen oder wie oder was...


----------



## Nachbar (17. Dez 2009)

nochmal ich...

wenn ich folgendes lese:

"Im zweiten Schritt soll das MühleSpiel
um eine OnlineVariante
erweitert werden.
Diese soll es zwei Spielern ermöglichen, das Spiel auch auf entfernten aber durch
ein Netzwerk miteinander verbundenen Rechnern zu spielen. Die Spieloberfläche soll
dabei wiederum durch eine graphische Benutzerschnittstelle implementiert werden,
die einzelne Spielzüge über die Maus empfängt."

verstehe ich nicht...oder besser ist es mir momentan fast egal ob das Mühlespiel nun auf einem Server liegt ....aja ich glaube langsam ... der andere Mitspieler am anderen Ende des Raumes an einem anderen PC , bekommt von mir ne Internetadresse, da kann er dann klicken und mitspielen egal ob ich nun gleichzeitig der server bin oder der server nun woanders ist....ach man, alles nicht so einfach, für mich zumindest....

ja die nächste frage wäre ja dann( wenn ich so halb recht hab), was wäre einfacher: ich gleichzeitig server und client oder den server irgendwohin ( wie auch immer..."noch") und mein gegenspieler und ich clienten


----------



## Nachbar (4. Jan 2010)

so, da bin ich mal wieder... ob nun schlauer als vorher, wird sich noch zeigen...

ich bin nun der meinung, das ich gleichzeitig server und client sein kann und woanders der client sitzt...

was ich nun brauche sind die klassen server und client, auch mit reichlichen beispielen, seh ich da noch kein land, was da wo wie hinkommt, ich brauche flush, close, accept, input, output... etc pp, das is aber erstma mein zweites problem ^^

das größere problem is die spielklasse umzuschreiben... ich dreh mich gerad im Kreis ob ich nun initialisieren oder den Konstrukor ändern muss...für weiteres folgen ersteinmal die originalen methoden


```
public Mühle() {
        frame = new JFrame("Mühle");
        frame.addWindowListener(new WindowAdapter() {
 
            @Override
            public void windowClosing(WindowEvent arg0) {
                System.exit(0);
            }
        });
        brett = new Spielbrett(300, 300);
        pool = new Steinpool(300, 100);
        lPlayer = new JLabel("", 0);
        lAktion = new JLabel("", 0);
        menubar = new JMenuBar();
        mSpiel = new JMenu("Spiel");
        mOptionen = new JMenu("Optionen");
        miNeu = new JMenuItem("Neues Spiel");
        miExit = new JMenuItem("Spiel Beenden");
        mSpiel.add(miNeu);
        mSpiel.add(miExit);
        menubar.add(mSpiel);
        menubar.add(mOptionen);
        miExit.addActionListener(this);
        miNeu.addActionListener(this);
        pnlMain = new JPanel(new BorderLayout());
        pnlLinks = new JPanel(new BorderLayout());
        pnlRechts = new JPanel(new GridLayout(0, 1));
        brett.addMouseListener(this);
        pnlLinks.add(brett, BorderLayout.NORTH);
        pnlLinks.add(pool, BorderLayout.SOUTH);
        pnlRechts.add(lPlayer);
        pnlRechts.add(lAktion);
        pnlMain.add(pnlLinks, BorderLayout.WEST);
        pnlMain.add(pnlRechts, BorderLayout.EAST);
        pnlRechts.setPreferredSize(new Dimension(200, 0));
        frame.add(pnlMain);
        frame.setJMenuBar(menubar);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        initialisieren();
    }
```



```
public void initialisieren() {
        pos1 = 26;
        pos2 = 26;
        regel = "keine";
        spielGestartet = true;
        spielZustand = new String[3];
        steineAufBrett = new int[3];
        steineImPool = new int[3];
        Random rnd = new Random();
        aktSpieler = rnd.nextInt(2) + 1;
        if (aktSpieler == 1) {
            spielZustand[1] = "SteineSetzen";
            spielZustand[2] = "SteineSetzen";
            lPlayer.setText("Weiß dran.");
            steineAufBrett[1] = 0;
            steineImPool[1] = 9;
            steineAufBrett[2] = 0;
            steineImPool[2] = 9;
            brett.spieler = 1;
            lAktion.setText(spielZustand[1]);
            
        } else {
            spielZustand[2] = "SteineSetzen";
            spielZustand[1] = "SteineSetzen";
            lPlayer.setText("Schwarz dran.");
            steineAufBrett[2] = 0;
            steineImPool[2] = 9;
            steineAufBrett[1] = 0;
            steineImPool[1] = 9;
            brett.spieler = 2;
            lAktion.setText(spielZustand[2]);
            
        }
      pool.init();
      brett.init();

        
    	
        
    }
```

so, als erstes hatte ich mir gedacht den methode initialisieren umzuschreiben respektive aufzuteilen in ini1 und ini2 


```
public void initialisieren() {
        pos1 = 26;
        pos2 = 26;
        regel = "keine";
        spielGestartet = true;
        spielZustand = new String[3];
        steineAufBrett = new int[3];
        steineImPool = new int[3];
        aktSpieler = 1;
        
        spielZustand[1] = "SteineSetzen";
        spielZustand[2] = "SteineSetzen";
        lPlayer.setText("Weiß dran.");
        steineAufBrett[1] = 0;
        steineImPool[1] = 9;
        steineAufBrett[2] = 0;
        steineImPool[2] = 9;
        brett.spieler = 1;
        lAktion.setText(spielZustand[1]);
            
        pool.init();
        brett.init();  
        try
		{
			ServerSocket server = new ServerSocket(0);
			System.out.println("Port: " + server.getLocalPort());
			initVerbindung(server.accept());
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
    }
    public void initialisieren2(String inetAddr, int port) {
        pos1 = 26;
        pos2 = 26;
        regel = "keine";
        spielGestartet = true;
        spielZustand = new String[3];
        steineAufBrett = new int[3];
        steineImPool = new int[3];
        
        aktSpieler = 2;
        
        spielZustand[2] = "SteineSetzen";
        spielZustand[1] = "SteineSetzen";
        lPlayer.setText("Schwarz dran.");
        steineAufBrett[2] = 0;
        steineImPool[2] = 9;
        steineAufBrett[1] = 0;
        steineImPool[1] = 9;
        brett.spieler = 2;
        lAktion.setText(spielZustand[2]);
           
        pool.init();
        brett.init(); 
        try
		{
			initVerbindung(new Socket(inetAddr, port));
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
    }
```
zusätzlich noch 


```
public void initVerbindung(Socket socket)
	{
		try
		{
			ps = new PrintStream(socket.getOutputStream());
			scan = new Scanner(socket.getInputStream());
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		new Thread((Runnable) this).start();
	}
```

so, müssten noch server und client her und alles wäre cool...achja initialisiere2 noch aufgerufen im konstruktor....und nebenbei denke ich mir, könnte es mit dem zufälligen anfang probleme geben, falls nicht, kommt der eben wieder rein

ABER, zwischenzeitlich dachte ich mir. da ja in der methode initialisieren alles rein kommt was man braucht um das spiel sauber starten zu können, kommt mir meine erste idee ein wenig komisch vor.. oder wie seht ihr das ???

ich sag nochmal, falls es noch niemanden aufgefallen sollte, wir sind keine javaprofis ^^

wiederum den konstruktor zu ändern, halte ich für schwachsinn...da ....ah warte mal...vielleicht muss ich doch nur den Konstrukor ändern der art wie ich initialisieren geändert habe und lasse die methode initialisieren so wie sie war...im konstrukor sind alle einmaligen sachen und initialisieren wird überall aufgerufen, könnte wiederrum ein problem mit dem zufallsanfang sein...quellcode kommt gleich....übrigens habe ich noch PrintStream ps;Scanner scan; vorher deklariert


```
PrintStream ps;
	Scanner scan;
 
	public Mühle() {
        frame = new JFrame("Mühle");
        frame.addWindowListener(new WindowAdapter() {
 
            @Override
            public void windowClosing(WindowEvent arg0) {
                System.exit(0);
            }
        });
        brett = new Spielbrett(300, 300);
        pool = new Steinpool(300, 100);
        lPlayer = new JLabel("", 0);
        lAktion = new JLabel("", 0);
        menubar = new JMenuBar();
        mSpiel = new JMenu("Spiel");
        mOptionen = new JMenu("Optionen");
        miNeu = new JMenuItem("Neues Spiel");
        miExit = new JMenuItem("Spiel Beenden");
        mSpiel.add(miNeu);
        mSpiel.add(miExit);
        menubar.add(mSpiel);
        menubar.add(mOptionen);
        miExit.addActionListener(this);
        miNeu.addActionListener(this);
        pnlMain = new JPanel(new BorderLayout());
        pnlLinks = new JPanel(new BorderLayout());
        pnlRechts = new JPanel(new GridLayout(0, 1));
        brett.addMouseListener(this);
        pnlLinks.add(brett, BorderLayout.NORTH);
        pnlLinks.add(pool, BorderLayout.SOUTH);
        pnlRechts.add(lPlayer);
        pnlRechts.add(lAktion);
        pnlMain.add(pnlLinks, BorderLayout.WEST);
        pnlMain.add(pnlRechts, BorderLayout.EAST);
        pnlRechts.setPreferredSize(new Dimension(200, 0));
        frame.add(pnlMain);
        frame.setJMenuBar(menubar);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        initialisieren();
        try
		{
			ServerSocket server = new ServerSocket(0);
			System.out.println("Port: " + server.getLocalPort());
			initVerbindung(server.accept());
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
    }
	
	public Mühle(String inetAddr, int port) {
        frame = new JFrame("Mühle");
        frame.addWindowListener(new WindowAdapter() {
 
            @Override
            public void windowClosing(WindowEvent arg0) {
                System.exit(0);
            }
        });
        brett = new Spielbrett(300, 300);
        pool = new Steinpool(300, 100);
        lPlayer = new JLabel("", 0);
        lAktion = new JLabel("", 0);
        menubar = new JMenuBar();
        mSpiel = new JMenu("Spiel");
        mOptionen = new JMenu("Optionen");
        miNeu = new JMenuItem("Neues Spiel");
        miExit = new JMenuItem("Spiel Beenden");
        mSpiel.add(miNeu);
        mSpiel.add(miExit);
        menubar.add(mSpiel);
        menubar.add(mOptionen);
        miExit.addActionListener(this);
        miNeu.addActionListener(this);
        pnlMain = new JPanel(new BorderLayout());
        pnlLinks = new JPanel(new BorderLayout());
        pnlRechts = new JPanel(new GridLayout(0, 1));
        brett.addMouseListener(this);
        pnlLinks.add(brett, BorderLayout.NORTH);
        pnlLinks.add(pool, BorderLayout.SOUTH);
        pnlRechts.add(lPlayer);
        pnlRechts.add(lAktion);
        pnlMain.add(pnlLinks, BorderLayout.WEST);
        pnlMain.add(pnlRechts, BorderLayout.EAST);
        pnlRechts.setPreferredSize(new Dimension(200, 0));
        frame.add(pnlMain);
        frame.setJMenuBar(menubar);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        initialisieren();
        try
		{
			initVerbindung(new Socket(inetAddr, port));
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
    }
 
    public void initialisieren() {
        pos1 = 26;
        pos2 = 26;
        regel = "keine";
        spielGestartet = true;
        spielZustand = new String[3];
        steineAufBrett = new int[3];
        steineImPool = new int[3];
        Random rnd = new Random();
        aktSpieler = rnd.nextInt(2) + 1;
        if (aktSpieler == 1) {
            spielZustand[1] = "SteineSetzen";
            spielZustand[2] = "SteineSetzen";
            lPlayer.setText("Weiß dran.");
            steineAufBrett[1] = 0;
            steineImPool[1] = 9;
            steineAufBrett[2] = 0;
            steineImPool[2] = 9;
            brett.spieler = 1;
            lAktion.setText(spielZustand[1]);
            
        } else {
            spielZustand[2] = "SteineSetzen";
            spielZustand[1] = "SteineSetzen";
            lPlayer.setText("Schwarz dran.");
            steineAufBrett[2] = 0;
            steineImPool[2] = 9;
            steineAufBrett[1] = 0;
            steineImPool[1] = 9;
            brett.spieler = 2;
            lAktion.setText(spielZustand[2]);
            
        }
      pool.init();
      brett.init();
    }
```

das gefällt mir momentan am besten...

ja, wir ihr seht, so ganz schlauer bin ich noch nich geworden...würde erstma gerne wissen wollen, ob irgendwas richtig war, ich hoffe der zweite ansatz ^^ und ja ansonsten bitte ich um Lob, Kritik, Blumensträuse ^^

grüße


----------



## SlaterB (4. Jan 2010)

also ich werd das nicht alles durchlesen  (die zwei Posts vom 17.12. auch gar nicht gesehen bisher)
kleiner Tipp: wenn du generell an den Grundlagen der Kommunikation arbeitest, dann fange mit einem simplen Programm an,
übe z.B. nur, einen einzelnen String zu übertragen und den auf allen Seiten auszugeben,
aber über Konsole, ganz ohne GUI

in deinem Code sehe ich fast nur Massen an JPanels, Menü-Punkte und langweilige Spiel-Informationen wie
> spielZustand[1] = "SteineSetzen";
>        spielZustand[2] = "SteineSetzen";
(12x 'SteineSetzen' in einem Post..)

falls es später um Spiel-Details geht wär das sicher unvermeidbar, 
solange du aber noch mit den Sockets an sich kämpfst ist das nur Rauschen,
ich seh gar nicht wo ein Problem besteht


----------



## homer65 (4. Jan 2010)

Man kann mit dem ObjectOutputStream (bzw ObjectInputStream) ganze Objecte über TCP/IP versenden. Das ist für den Programmierer sehr komfortabel. 
Ob es aber auch sicher ist?


----------

