Upload
kpcmj
View
573
Download
2
Embed Size (px)
DESCRIPTION
Facharbeit in der Stufe 12 des Gymnasiums.Die Funktionsweise eines Servers in Java am Beispiel eines Wettbewerbsystems von Othello
Citation preview
Theodor-Heuss-Gymnasium Jahrgangsstufe 12, 2. Hj
FACHARBEIT
im Grundkurs Informatik
bei
Die Funktionsweise eines Servers
Realisierung eines netzwerkbasierten
Wettbewerbssystems am Beispiel von Othello
Verfasser:
Jahrgangsstufe: 12
Schuljahr: 2011/12
Kursleiter:
- 2 -
Inhaltsverzeichnis
1 Einleitung.......................................................................................................................3
2 Othello...........................................................................................................................3
2.1 Aufbau und Spielregeln...........................................................................................3
2.2 Beispiele für Spielsituationen..................................................................................4
3 Der Server......................................................................................................................5
3.1 Die Anmeldung........................................................................................................6
3.1.1 Der direkte Weg zum Server..............................................................................6
3.1.2 Mit dem Broadcast zum Server..........................................................................8
3.2 Die Vorbereitungen vor dem Spiel – Organisation................................................17
3.3 Das Protokoll.........................................................................................................19
3.3.1 Der erste Kontakt.............................................................................................20
3.3.2 Das Spiel..........................................................................................................20
3.3.3 Die Ergebnisse.................................................................................................22
4 Schluss.........................................................................................................................23
5 Literaturverzeichnis.....................................................................................................24
6 Abbildungsverzeichnis.................................................................................................24
7 Anhang.........................................................................................................................25
7.1 UML Diagramm..................................................................................................25
- 2 -
- 3 -
1 Einleitung
Ich werde mich in dieser Facharbeit mit einem Wettbewerbs- und Rankingsystem auf
Netzwerkbasis anhand von Othello befassen. Präziser formuliert handelt es sich um ein
Client-Server System, das die einzelnen Spiele aller Beteiligten organisiert, durchführt
und auswertet. Der Anwendungsbereich könnte hierbei z.B. bei einem Wettbewerb für
Schachcomputer oder bei meinem Beispiel Othelloturnier liegen. Dabei wird besonderer
Wert auf die Kommunikation und Verwaltung in JAVA in der Version 6 gelegt. Das heißt
Ziel wird die Entwicklung und die genaue Erläuterung des Netzwerkprotokolls, sowie
die Organisation der Clients im Server und der Datenerhebung für die Auswertung und
Bewertung aller Clients sein. Der Client wird eine untergeordnete Rolle spielen und in
großen Stücken als gegeben betrachtet, soweit dies nicht zum allgemeinen Verständnis
notwendig sein sollte. Die Facharbeit wird zunächst die Grundzüge des Spielablaufes
klären, um anschließend Aufbau und genaue Realisierung des Servers zu beschreiben,
wobei auf Probleme bei der Programmierung der einzelnen Aufgabenbereiche
hingewiesen wird.
2 Othello
In diesem Kapitel möchte ich eine kurze Einführung zu dem 1971 aus Reversi
hervorgegangenem Spiel Othello geben und den Aufbau, sowie die Spielregeln und den
Spielverlauf im Allgemeinen klären. Außerdem werden einige Besonderheiten an
konkreten Spielsituationen erklärt.
2.1 Aufbau und Spielregeln
Das Spielfeld besteht aus einem 8x8 Felder messendem Brett (z.B Schachbrett).
Gespielt wird mit zwei Spielern und Spielsteinen in zwei Farben. Das heißt eine Seite
eines Spielsteins ist schwarz, die andere Seite weiß. Jedem Spieler wird eine Farbe
zugewiesen.
- 3 -
- 4 -
Zu Beginn wird das Spielfeld in folgender Weise vorbereitet:
Die mittleren vier Felder (D4,E4,D5,E5) werden nach1
nebenstehendem Muster besetzt und der Startspieler,
derjenige der den ersten Zug vornimmt, wird bestimmt.
Aus historischen Gründen wird normalerweise der Spieler
mit der schwarzen Farbe als Startspieler erklärt.
Es wird abwechselnd gezogen. Der aktive Spieler (der Spieler, der am Zug ist) setzt
einen Spielstein in seiner Farbe auf ein leeres Feld, das horizontal, vertikal oder
diagonal an ein bereits besetztes Feld grenzt. Die Farbe des angrenzenden Spielsteins
spielt hierbei keine Rolle. Außerdem muss es, damit der Zug gültig ist, zum Umdrehen
mindestens eines gegnerischen Steins kommen. Zum Umdrehen eines gegnerischen
Steins kommt es immer dann, wenn zwischen dem neu gelegten Stein und einem oder
mehreren bereits liegenden Stein(en) der selben Farbe ausschließlich Steine der anderen
Farbe liegen. Dazwischen bedeutet in einer geraden Linie, die horizontal, vertikal oder
diagonal verlaufen kann, ohne dass ein Stein der eigenen Farbe vorkommt. Ist ein solch
gültiger Zug nicht möglich, muss der Spieler aussetzen und der andere Spieler ist am
Zug.
Ziel des Spiels ist es, so viele Steine in seiner Farbe auf dem Feld liegen zu haben wie
möglich. Das Spiel endet, wenn das komplette Spielfeld besetzt ist oder beide Spieler
keinen gültigen Zug mehr vornehmen können.
2.2 Beispiele für Spielsituationen
2Weiß setzt auf D3 einen neuen
Stein und erhält dadurch die
vertikale und diagonale Reihe
dazu (E4,D4,D5).
1 http://de.wikipedia.org/wiki/Othello_%28Spiel%29 (20.03.2012)
2 http://de.wikipedia.org/wiki/Othello_%28Spiel%29 (20.03.2012)
- 4 -
- 5 -
In der folgenden Spielsituation ist Weiß am Zug. Da aber alle noch möglichen Spielzüge
dazu führen, dass kein gegnerischer Stein umgedreht wird, muss Weiß seinen Zug
aussetzen und Schwarz ist am Zug.
3In dem Fall, dass auch Schwarz wiederum nicht gültig
setzen kann, wäre das Spiel an diesem Punkt vorbei, und
es würde die Auswertung folgen. Angenommen, dies
wäre die endgültige Situation, hätte Schwarz 16 Punkte
und somit gegen Weiß (15 Punkte) gewonnen.
3 Der Server
Dieses Kapitel stellt den Hauptteil der Arbeit dar und wird sich mit der serverseitigen
Realisierung des Wettbewerbssystems beschäftigen. Das Prinzip des Servers hat man
sich folgenderweise vorzustellen: Die Teilnehmer (Clients) des Othelloturniers müssen
sich beim Server anmelden. Dieser nimmt die Teilnehmer in einer vom Spielleiter
vorgegebenen Zeit an und trägt sie mit Name und sonstigen zur Identifizierung nötigen
Daten (z.B. E-Mail Adresse) in seine Liste ein. Nachdem die Zeit zur Anmeldung
abgelaufen ist, nimmt der Server keine weiteren Teilnehmer mehr an und kümmert sich
im nächsten Schritt um die Austragung des Wettbewerbs. Der Wettbewerb soll so
gestaltet sein, dass jeder gegen jeden zwei Mal antritt (Farbwechsel → Wechsel des
Startspielers) und der Gewinner durch die Anzahl der gewonnen, verlorenen oder
unentschieden gespielten Spiele ermittelt wird. Der Server hat hierbei die Aufgabe dafür
zu sorgen, dass die Kommunikation mit den Teilnehmern funktioniert und die
Spielregeln eingehalten werden. Dazu muss der Server die Regeln des Spiels genauso
gut kennen wie die Teilnehmer. Abschließend ist der Server dafür zuständig die
Spielergebnisse zu notieren und für die anschließende Auswertung bzw. Archivierung
zu speichern. Zur Bewältigung dieser Aufgaben wird der Server in entsprechende
Aufgabenbereiche unterteilt. Im Zuge der Umsetzung der Aufgabenbereiche werden
3 http://de.wikipedia.org/wiki/Othello_%28Spiel%29 (20.03.2012)
- 5 -
- 6 -
Hilfsklassen und Multithreading notwendig. Im Folgenden werde ich möglichst
chronologisch die Umsetzung der genannten Aufgabenbereiche erläutern.
3.1 Die Anmeldung
Dieses Kapitel wird sich mit dem Weg vom Client zum Server befassen. Zum einen
wird der direkte Weg über eine TCP/IP Verbindung behandelt. Andererseits wird auf die
Möglichkeit eingegangen, alle relevanten Server im Netzwerk mit Hilfe eines
Broadcasts automatisiert zu finden.
3.1.1 Der direkte Weg zum Server
Um die Clients annehmen zu können, muss zunächst der Server initialisiert und auf das
Annehmen vorbereitet werden. Dies wird durch folgenden Programmcode erreicht:
public void startAcceptClients(String ip, int port) throws IOException{
stopAcceptClients();
server=new ServerSocket();
server.setSoTimeout(5000);
server.bind(new InetSocketAddress(ip,port));
acceptthread=new AcceptThread();
}
public void stopAcceptClients(){
if(acceptthread!=null){
acceptthread.stop();
acceptthread=null;
}
}
private class AcceptThread{
volatile boolean run=false;
Thread t;
public AcceptThread(){
run=true;
- 6 -
- 7 -
t= new Thread(new Runnable(){
@Override
public void run() {
while(run){
try {
Socket client = server.accept();
} catch (IOException e) {
e.printStackTrace();
}
}
}});
t.start();
}
public void stop(){
run=false;
t=null;
}
}
Zunächst wird die Methode startAcceptClients(String ip, int port) aufgerufen,
die den ServerSocket erzeugt und an die mitgegebene IP-Adresse und den Port bindet.
stopAcceptClients(); wird aufgerufen um zu verhindern, dass ein neuer Thread
erstellt wird, ohne dass ein potentiell noch vorhandener AcceptThread sachgemäß
beendet wurde und unter Umständen ewig weiterlaufen würde. Die eigentliche
Annahme geschieht innerhalb von AcceptThread solange dieser Thread nicht wieder
gestoppt wird werden hier die Clients mit server.accept(); angenommen. Da diese
Methode allerdings blockiert, bis ein Client angenommen wurde, muss dem
ServerSocket ein Timeout zugewiesen werden, welches dafür sorgt, dass der Versuch,
einen neuen Client anzunehmen, alle 5 Sekunden gestoppt wird. Wichtig in dem Thread
ist, dass der boolean run als volatile bezeichnet wird. Da diese Variable regelt, wie
lange der Thread lebendig ist und von zwei oder mehreren Threads verwendet wird,
muss hierdurch erwirkt werden, dass alle Threads von möglichen Änderungen an der
Variable informiert werden4.
4 Thomas Künneth 2011, S.151 und Christian Ullenboom 2012, 11.7 Client-Server-Kommunikation
- 7 -
- 8 -
3.1.2 Mit dem Broadcast zum Server
Die Idee hinter einem Broadcast ist es, alle erreichbaren Computer zu befragen, ob die
gesuchte Funktionalität dort zur Verfügung steht. Dazu wird ein UDP-Paket an alle
Computer innerhalb des lokalen Netzwerks gesendet. Dieses Paket enthält
Informationen darüber, was der Client, der dieses Paket versendet, wissen möchte und
von wem er diese Information bekommen will. Wenn ein passender Server dieses Paket
empfängt stellt er dem Client die geforderten Informationen zur Verfügung. Die
folgende Grafik soll dies noch einmal verdeutlichen:
Im Detail betrachtet muss in Java ein DatagramPacket und ein DatagramSocket erzeugt
werden. Das DatagramSocket ist für den Versand des UDP-Paketes verantwortlich,
sowie für den Empfang der möglichen Antworten. Damit aus dem ganzen ein Broadcast
- 8 -
- 9 -
wird, ist es notwendig DatagramSocket.setBroadcast(true); aufzurufen und als IP-
Adresse die 255.255.255.255 anzugeben. Jetzt wird das Paket an alle empfangsfähigen
Computer im Netzwerk gesendet. Dieses Verfahren ist als Limited Broadcast bekannt.
In dem folgenden Quellcode wird allerdings auf den Directed Broadcast
zurückgegriffen, der etwas mehr Kontrolle erlaubt. Da es auch innerhalb des
erreichbaren Netzes Unterteilungen gibt, kann somit eine genaue Ansteuerung eines
dieser Unternetze erzielt werden. Der einzige Unterschied zum Limited Broadcast liegt
bei der IP-Adresse. Diese wird aus einer Kombination des Zielnetzes und der
Netzmaske gebildet. Sei die eigene IP-Adresse 192.168.100.102 und die Netzmaske
255.255.255.0 so ist die Broadcast IP-Adresse 192.168.100.255. Das heißt alle Stellen
der eigenen IP werden übernommen, wenn die Netzmaske an dieser Stelle den Wert 255
hat ansonsten wird diese Stelle der Broadcast IP-Adresse mit 255 gefüllt5. „Die Adresse
wird durch die Kombination aus Zielnetz und dem Setzen aller Hostbits auf 1
angegeben“6 (Wikipedia - Broadcast).
Damit der Server, der das Paket empfangen soll weiß, dass dieses Paket von einem
Othello Client stammt, wird dem Paket als Datenteil z.B ein Text („Ich bin es Othello“)
hinzugefügt.
Sobald der Server das Paket angenommen und als relevant erkannt hat, schickt dieser
ein neues Paket zurück an den Client, sodass dieser, wie auch der Server, über
DatagramPacket.getAddress() und DatagramPacket.getPort() die notwendigen
Verbindungsdaten erfragen kann. Auf diesem Weg muss ausschließlich der Port bekannt
sein, an den der Broadcast gesendet wird, um alle Server zu finden, die in Frage
kommen, um sich mit ihnen zu verbinden. In Quelltext sieht das ganze dann wie folgt
aus (Fortsetzung des Textteils ab S.17):
Serverseite:
5 aus diesem Grund kann auch kein Rechner eine IP mit einer 255 haben
6 http://de.wikipedia.org/wiki/Broadcast (20.03.2012)
- 9 -
- 10 -
private DatagramSocket socket = null; // Socket für die Datagramme
private int port = 0; // Der Port, der verwendet
wird
private Server server = null; // interne Serverinstanz
private DatagramPacket serverInfo = null; // Serverinfos
private Vector<FindServersListener> listeners = new
Vector<FindServersListener>(1,1); // FindServersListeners verwalten
private int BUFFERSIZE = 1024;
public int getPort() {
return port;
}
public FindServer(int port,int Bufsize,final String stdserverinfo)throws
IllegalArgumentException, SocketException{
if (port < 1 || port > 65535){ throw new IllegalArgumentException
("Port number is out of range!");}
this.port=port;
try {
socket = new DatagramSocket (port); // zum Testen, ob der Port
geht
socket.close();
setServerInfo (stdserverinfo); // StandardServerInfo
startServer();
} catch (SocketException e) {
throw new SocketException ("Can't bind to specified Socket!");
}
this.BUFFERSIZE=Bufsize;
}
/**
* Servernachricht setzen.
*
*/
public void setServerInfo (String str) {
- 10 -
- 11 -
if (str==null) str="";
byte buf [] = str.getBytes();
if (serverInfo!=null) {
synchronized (serverInfo) {
serverInfo = new DatagramPacket (buf, buf.length);
}
} else {
serverInfo = new DatagramPacket (buf, buf.length); }
}
/**
* Den Server starten.
*
*/
public void startServer () {
if (server!=null) return; // Einfach ignorieren
try {
socket = new DatagramSocket(port);
} catch (SocketException e) {
}
server = new Server();
server.start();
}
/**
* Den Server stoppen.
*
*/
public void stopServer () {
if (server==null) return; // Einfach ignorieren
socket.close();
server=null;
socket=null;
}
/**
- 11 -
- 12 -
* Testen, ob ein Server l&uft.
*
*/
public boolean isServerRunning () {
return server!=null;
}
public void addFindServerListener(FindServersListener c){
listeners.add(c);
}
/**
* Interne Klasse Server dient zum Beantworten von Anfragen der Clients.
*/
private class Server extends Thread {
public void run () {
while (server!=null) {
try {
byte[] inbuf = new byte[BUFFERSIZE];
DatagramPacket in = new DatagramPacket(inbuf,
inbuf.length);
socket.receive(in);
boolean send=true;
for (int i=0; i<listeners.size(); i++) {
send=((FindServersListener)listeners.get(i)).clientRequest(FindServer.this,i
n);
}
if(send){
serverInfo.setAddress(in.getAddress());
serverInfo.setPort(in.getPort());
synchronized (serverInfo) {
socket.send(serverInfo);
}
}
- 12 -
- 13 -
} catch (IOException e) {
}
}
}
}
Clientseite:
private DatagramSocket socket = null; // Socket für die Datagramme
private int port = 0; // Der Port, der verwendet
wird
private int timeout = 1000; // Timeout fürs
private Vector<DatagramPacket> receivedDatagramPackets = new
Vector<DatagramPacket>(10,1);
/
private int BUFFERSIZE = 1024;
public FindClient(int port,int Bufsize)throws IllegalArgumentException,
SocketException{
if (port < 1 || port > 65535){ throw new IllegalArgumentException
("Port number is out of range!");}
this.port=port;
try {
socket = new DatagramSocket (port); // zum Testen, ob der Port
geht
socket.close();
} catch (SocketException e) {
throw new SocketException ("Can't bind to specified Socket!");
}
this.BUFFERSIZE=Bufsize;
}
/**
* Timeout für das Anpingen setzen.
- 13 -
- 14 -
*
*/
public void setTimeout (int timeout) throws IllegalArgumentException {
if (timeout < 0) throw new IllegalArgumentException ("No negative
timeout allowed!");
this.timeout=timeout;
}
/**
* Server anpingen.
*
*/
public boolean requestServerInfos (DatagramPacket dp) {
receivedDatagramPackets.clear();
try {
InetAddress ownIP = InetAddress.getLocalHost();
InetAddress broadcastIP = getBroadcastAddress(ownIP);
dp.setAddress(broadcastIP);
dp.setPort(port);
socket = new DatagramSocket (port);
socket.send(dp);
socket.close();
socket = new DatagramSocket (port);
socket.setSoTimeout(timeout);
while (true) {
try {
byte buf [] = new byte[BUFFERSIZE];
dp = new DatagramPacket(buf, buf.length);
socket.receive(dp);
receivedDatagramPackets.add(dp);
} catch (SocketTimeoutException e) {
break;
}
}
socket.close();
- 14 -
- 15 -
} catch (IOException e) {
e.printStackTrace();
}
return (!receivedDatagramPackets.isEmpty());
}
/**
* Serverinfos holen.
*
*/
public DatagramPacket [] getServerInfos () {
return (DatagramPacket [])receivedDatagramPackets.toArray(new
DatagramPacket[0]);
}
/**
* Server anpingen.
*
*/
public boolean requestServerInfos (DatagramPacket dp,InetAddress
broadcastIP) {
receivedDatagramPackets.clear();
try {
dp.setAddress(broadcastIP);
dp.setPort(port);
socket = new DatagramSocket (port);
socket.send(dp);
socket.close();
socket = new DatagramSocket (port);
socket.setSoTimeout(timeout);
while (true) {
try {
byte buf [] = new byte[BUFFERSIZE];
dp = new DatagramPacket(buf, buf.length);
- 15 -
- 16 -
socket.receive(dp);
receivedDatagramPackets.add(dp);
} catch (SocketTimeoutException e) {
break;
}
}
socket.close();
} catch (IOException e) {
}
return (!receivedDatagramPackets.isEmpty());
}
/**
* Server anpingen.
*
*/
public boolean requestServerInfos (String str) {
if (str==null) str="";
byte buf [] = str.getBytes();
DatagramPacket dp = new DatagramPacket (buf,buf.length);
return requestServerInfos(dp);
}
public String getBroadcastAddressS(InetAddress address) throws
UnknownHostException{
return getBroadcastAddress (address).getHostAddress();
}
/**
* anders als im Text wird hier die Broadcast Adresse selbst ermittelt
und nicht über .setBroadcast(true) Java überlassen
*
*/
public InetAddress getBroadcastAddress (InetAddress address) throws
UnknownHostException {
byte ipBytes [] = address.getAddress();
- 16 -
- 17 -
if (ipBytes[0]<(byte)128) ipBytes[1]=(byte)255;
if (ipBytes[0]<(byte)192) ipBytes[2]=(byte)255;
ipBytes[3]=(byte)255;
return InetAddress.getByAddress(ipBytes);
}
3.2 Die Vorbereitungen vor dem Spiel – Organisation
Ist ein Client erst einmal angenommen, muss dieser in irgend einer Weise gespeichert
und mit allen anderen zusammen organisiert werden, bevor das Spielen los gehen kann.
Sobald der Client angenommen und akzeptiert wurde, sorgt der Organisator dafür, dass
- 17 -
Server
Client
verb
indet sich
Organisator Teilnehmer
Games
Annahme und erster Kontakt
Trennung bei FehlernSammeln im Pool bei erfolgreicher Annahme
Keine weiterenAnnahmen
Erstellung und Ausführung aller Spiele
Sind alle Spiele fertig: Ausgabe der ErgebnisseGUI
- 18 -
alle Teilnehmer einheitlich an einem Ort gesammelt werden. Da im Verlauf des
Programms öfter Zugriffe auf bestimmte und unbestimmte bzw. alle Teilnehmer
erfolgen, wird für diesen Ort ein Vector verwand. Da der Vector, der im Prinzip eine
doppelt verkettete Liste ist, wahlfreien Zugriff gewährt, d.h. direkter Zugriff auf ein
Element, ist er die beste zur Verfügung stehende Datenstruktur. Dem zu Gute kommt die
Möglichkeit ohne zusätzliche Arbeit durch den Vector iterieren zu können. Damit alle
Clients gleichzeitig die Chance haben mit dem Server zu kommunizieren, wird jeder
Teilnehmer in einem eigenen Thread ausgeführt. Während des ersten Schritts findet
bereits die erste Kommunikation statt. Der Server informiert sich über den Namen des
Clients und weist diesem einen eindeutigen Identifikationsstring(ID) zu7. Da sich
mehrere Clients von einem realen Computer anmelden können müssen, ist der
manchmal praktizierte Weg, die IP als ID zu verwenden, nicht möglich.
Dieser Vorgang wird fortgeführt, bis der Server keine Teilnehmer mehr akzeptiert. Sei
es, weil der Speicher voll ist, oder weil der Spielleiter (Serveradministrator) keine
weiteren Teilnehmer mehr haben möchte. Ab jetzt sorgt der Organisator dafür, dass alle
Spiele (Games) erzeugt werden. Das heißt, es werden für jedes Spielpaar zwei Objekte
(Hin- und Rückrunde) der Klasse Game erzeugt, die ihrerseits alle benötigten
Informationen für dieses eine Spiel verwaltet. So z.B. die beiden Teilnehmer, das
Spielfeld, den Punktestand und die Kommunikation zwischen den Spielern.
Sobald alle Spiele beendet sind werden die Ergebnisse ausgewertet. Dazu gehören auch
Verstöße gegen die Spielregeln. Schließlich werden die so eben erstellten Daten über die
Server-GUI dem User angezeigt. Es wäre außerdem möglich den Clients die Ergebnisse
mitzuteilen. Da es hier aber um ein lokales Netzwerk handelt, wird kaum eine so große
räumliche Distanz existieren, dass dies nötig wird (Ein möglicher Vorschlag zur
Realisierung folgt).
7 Für genauere Beschreibung siehe Das Protokoll
- 18 -
- 19 -
3.3 Das Protokoll
Dieses Kapitel beschäftigt sich mit der konkreten Ausführung eines Spiels aus Sicht der
Netzwerkebene. Bei der Kommunikation wird auf ein textbasiertes Befehlsprotokoll
gesetzt, das dem Interaktionsmodell der synchronen Kommunikation folgt (vgl. Dietmar
Abts 2007, S. 9). Das bedeutet, dass jede Anfrage erst eine Antwort bekommen muss,
bevor das Programm bzw. der jeweilige Thread weiterarbeiten kann. Da es für die
Kommunikation wichtig ist, dass alles und in der richtigen Reihenfolge bei dem Partner
ankommt, benutze ich eine TCP/IP Verbindung. Diese wird außerdem, solange kein
Fehler auftritt, während des gesamten Lebenszyklus des Programms nicht unterbrochen.
Der Eingabe, sowie der Ausgabestrom werden aufgrund der textbasierten
Kommunikation in ein BufferedReader bzw. ein BufferedWriter geschachtelt. Somit
muss jeder Befehl als eindeutiger String der Form „Befehl:Parameter<;>Parameter“
versendet werden. Die Parameter sind je nach Befehl optional oder nicht zulässig. Die
einzigen dringend notwendigen einzuhaltenden Konventionen sind, dass auf beiden
Seiten, d.h. Server und Client, die gleiche Zeichencodierung und der gleiche Aufbau für
einen Befehl verwendet werden muss. So wird in diesem Beispiel UTF-8 benutzt, da
durch JAVA im Gegensatz zu UTF-8 nicht notwendigerweise alle Zeichencodierungen
auf allen Plattformen unterstützt werden (vgl. Dietmar Abts 2007, S. 115 ff.).
Sind diese Grundvoraussetzungen gegeben, muss allen Beteiligten nur noch eine
gemeinsame Sprache, das Protokoll bekannt sein, nachdem die einzelnen Befehle ihre
Bedeutung erhalten. Wenn der Server den Befehl zum Trennen der Verbindung mit
„SHUTDOWN“ sendet, versteht der Client nicht was von ihm gewollt wird, wenn in
seinem Protokoll „GEHNACHHAUSE“ für diese Aufgabe vermerkt ist. So hat jede
Aufgabe ihren eigenen Befehl. Im Grunde kann so ein für jeden Menschen und
Computer verständliches Protokoll erzeugt werden, das Zugriff auf alle Funktionen
gewährleisten kann. Im folgenden wird dieses Vorgehen in der Kommunikation anhand
des zu behandelnden Othello-Server erläutert.
- 19 -
- 20 -
3.3.1 Der erste Kontakt
Wurde der Client vom Server akzeptiert/angenommen, wird ab sofort in regelmäßigen
Abständen ein einfacher Befehl (PING) versendet, um die Verbindung aufrecht zu
erhalten und von einem möglichen Verbindungsverlust informiert zu werden. Sollte die
Verbindung abbrechen, bekommt dies der Server in JAVA erst mit, wenn der Client auf
die bestehende Verbindung geprüft wird.
Weiter werden alle benötigten Identifikationsinformationen vom Client zugesandt. So
wird der Name des Spielers übermittelt und die Uhrzeit mit
System.currentTimeMillis() erfragt und an den Namen angehängt, damit dieser Name
als eindeutige ID genutzt werden kann. Außerdem fordert der Server den Client auf,
sich auf die Spiele vorzubereiten und informiert den Client damit gleichzeitig darüber,
dass möglicherweise eine längere Wartephase beginnt, bis keine Clients mehr
angenommen werden und die Spiele beginnen.
Client Server
NAME:maxmustermann
PREPAREGAME
PING (immer wiederholen)
3.3.2 Das Spiel
Sobald der Server die Spiele eingeleitet hat, erstellt dieser einen Spielplan und sorgt
dafür, dass alle Spiele ausgetragen werden. Sollte die Verbindung zu einem der Spieler
abbrechen oder dieser einen nicht gültigen Zug ausführen, wird dies als Niederlage
gewertet und seine Spielpunkte werden auf -1 gesetzt.
Der reguläre Spielablauf sieht zunächst vor, dass alle Beteiligten eines Spiels über ihre
Farbe, sowie den Startspieler informiert werden. Außerdem werden die Clients
aufgefordert das Spielfeld vorzubereiten. Ich habe mich in meinem Protokoll dazu
- 20 -
- 21 -
entschieden, dass jeder Client sein eigenes Spielfeld verwaltet und nur über die
vorgenommenen Züge informiert wird, um unnötigen Datenverkehr zu vermeiden, da
der Prozessor weniger Zeit für die zusätzlichen Berechnungen benötigt, als es das
Versenden des ganzen Spielfeldes kosten würde. Es kann allerdings auch die
Möglichkeit implementiert werden, dass der Client das momentane Spielfeld abfragen
kann, sollte dieser es für nötig halten. Anschließend wird, wie traditionell geregelt, der
erste Spieler, der Schwarze, nach seinem Zug befragt.
Ab diesem Zeitpunkt werden die folgenden Aktionen bis zum Spielende immer wieder
ausgeführt:
Der Server wartet auf den Zug des ersten Spielers und überprüft ihn auf Gültigkeit.
Sollte der Zug ungültig sein, wird dies als Fehler gewertet und der Spieler hat verloren.
Ist dies nicht der Fall, wird der zweite Teilnehmer über den Zug informiert und der
Server führt diesen Zug selbstredend auch auf seinem Spielfeld aus, sowie jeden
anderen gültigen Zug.
Nun überprüft der Server, ob der zweite Spieler überhaupt in der Lage ist einen gültigen
Zug zu tätigen. Sollte ein solcher Zug möglich sein, wird nun der zweite Spieler
aufgefordert seinen Zug durchzuführen. Auch hier wird der Zug geprüft, übernommen
und an den anderen Spieler geschickt.
Sollte nun wiederum der erste Spieler in der Lage sein einen Zug zu tätigen, wird dieser
erneut aufgefordert zu ziehen. Kann dieser allerdings nicht ziehen, wird überprüft , ob
überhaupt ein Spieler einen Zug durchführen kann. Sollte dies so sein, wird der zweite
Spieler solange aufgefordert zu ziehen, bis der erste Spieler wieder einen gültigen Zug
erwirken kann. Sollte keiner der beiden Spieler einen Zug ausführen können, ist das
Spiel vorbei und beiden Spielern wird das Spielende mitgeteilt.
- 21 -
- 22 -
Client Server
C1 COLOR:BLACK
C2 COLOR:WHITE
PREPAREPLAYGROUND
C1 DOTURN
Wiederholung Wiederholung
C1 TURN:x<;>y
C2 TURNDONE:x<;>y
C2 DOTURN
C2 TURN:x<;>y
C1 TURNDONE:x<;>y
C1 DOTURN
Wiederholung Wiederholung
GAMEOVER
3.3.3 Die Ergebnisse
Sobald alle Spiele fertig sind, also den GAMEOVER Status erreicht haben, wertet der
Server die gesammelten Ergebnisse aus. Dabei wird ein Sieg mit drei Punkten belohnt
und ein Unentschieden mit einem Punkt. Einen Niederlage ändert die Punkte in keiner
Weise. Sollten zwei Spieler die gleiche Punktzahl haben, entscheidet das
Punkteverhältnis zwischen Schwarzen und Weißen Steinen innerhalb der einzelnen
Spiele. Haben zwei Spieler gleiche Punkte und z.B. in allen Spielen zusammen ein
Verhältnis von eigenen zu gegnerischen Steinen von 1.5 und 1.7 so hat der Spieler mit
dem Verhältnis von 1.7 gewonnen.
Dieses Ranking wird auf der Serverseite innerhalb einer GUI angezeigt und den Clients
übermittelt. Das Übermitteln findet in der Form statt, dass der Server mitteilt, dass die
Rankingtabelle folgt und von bestem zu schlechtesten alle Spieler übermittelt.
Client Server
RANKINGTABLE
PLAYER:Name<;>Punkte<;>Eigene Steine<;>Gegnerische Steine
- 22 -
- 23 -
Es wäre möglich noch weitere Informationen zu sammeln. So zum Beispiel die Dauer
der Spiele oder das Spielverhalten. Doch soll hierauf keine Rücksicht genommen
werden, da sich dadurch keinerlei Änderung für das Vorgehen entsteht.
4 Schluss
Abschließend möchte ich noch einmal auf die Vielschichtigkeit eines Servers
hinweisen. In meinem Beispiel ist der Server in Annahme der Clients, deren
Verwaltung, die Ausführung der Spiele und das dazu gehörende Protokoll zu unterteilen.
Wichtig ist für den reibungslosen Ablauf einer Kommunikation über TCP/IP eine
einheitliche Zeichencodierung und ein für beide Seiten bekanntes Protokoll. Das dazu
gehörende Programm, sowie der Quelltext sind auf der digitalen Version dieser
Facharbeit zu finden.
- 23 -
- 24 -
5 Literaturverzeichnis
• Abts, Dietmar. Masterkurs Client/Server-Programmierung mit Java. Wiesbaden
2007: Friedr. Vieweg & Sohn Verlag | GWV Fachverlag GmbH April 2007 (2.,
erweiterte und aktualisierte Auflage)
• Künneth, Thomas. Android 3. Apps entwickeln mit dem Android SDK. Bonn
2011: Galileo Press (1.Auflage 2011 1. korrigierter Nachdruck 2012)
• Ullenboom, Christian: Java ist auch eine Insel. Das umfassende Handbuch,
Galileo Press. 10.Auflage. Onlineausgabe (aufgerufen 25.03.2012)
• Ullenboom, Christian. Java 7 - Mehr als eine Insel. Das Handbuch zu den Java
SE-Bibliotheken. Galileo Press. Onlineausgabe (Stand 25.03.2012)
• Wikipedia. Othello (Spiel) [online]. Update: 18. Februar 2012 um 00:16 Uhr.
URL:<http://de.wikipedia.org/wiki/Othello_%28Spiel%29> (Stand 21.03.2012)
• Wikipedia. Broadcast [online]. Update: 10. März 2012 um 10:20 Uhr.
URL:<http://de.wikipedia.org/wiki/Broadcast> (Stand 21.03.2012)
6 Abbildungsverzeichnis
Zu finden auf Seiten 4 und 5.
Alle Spielfelder sind als Vorlage Wikipedia. Othello (Spiel)
[online] entnommen
- 24 -
- 25 -
7 Anhang
7.1 UML Diagramm
- 25 -
- 26 -
- 26 -
- 27 -
- 27 -
- 28 -
- 28 -
- 29 -
- 29 -
- 30 -
- 30 -