![]() |
|
Es wurde bereits erläutert, dass eine Übertragung von Daten über Netzwerke in einer Client-Server-Architektur immer aus einem Client und einem Server besteht, die Daten austauschen. Weiterhin kann man unterscheiden zwischen Inhalten, die ausgetauscht werden, und Protokolldaten, die den Zweck und Aufbau der gesendeten bzw. empfangenen Daten angeben. Im Folgenden werden die Funktionen des Servers, des Clients und des Protokolls erläutert. Protokollformat In Kapitel 5.4 (Streams, Sicherheit und Networking) wurde bereits erläutert, dass in einer Client-Server-Architektur eine Protokollkomponente, die den Einsatzzweck gesendeter bzw. empfangener Daten festlegt, durchaus sinnvoll ist. Eine derartige Komponente kann entweder in den Code des Servers bzw. des Clients integriert sein oder im Fall eines sehr komplexen Protokolls auch in Form einer weiteren Klassenhierarchie. Betrachtet man das Beispiel Schiffe versenken, so stellt man fest, dass Client und Server immer die folgenden zwei Arten von Daten austauschen:
Es ist daher die Aufgabe des Protokolls, ein vollständiges Spielobjekt zu senden bzw. zu empfangen. Hierzu wurde die Klasse Daten implementiert, die wie folgt realisiert ist:
import java.awt.*; static final int SCHUSS = 0; public Daten () { p = new Point(-1,-1); } } Diese Klasse wird sowohl vom Server als auch vom Client eingesetzt. Server bzw. Client können Schüsse abfeuern und Wassertreffer oder Treffer melden. Jedes Daten-Objekt besteht daher aus der Art dieser Daten (status) und einem Koordinatenpaar, das angibt, worauf sich die Statusinformation bezieht. Im Folgenden wird die Verwendung dieser Klasse im Server-Programm bzw. im Client-Programm erklärt. Server Der Server muss, ähnlich wie auch der Client, die folgenden Aufgaben durchführen:
Diese Schritte wurden wie folgt implementiert. Den Aufbau einer Verbindung hat der Leser bereits kennengelernt.
import java.net.*; String sendeZeile, empfangsZeile; public SVServer() throws IOException { serverSocket = null; serverSocket = new ServerSocket(4444); } catch (IOException e) { System.err.println("Port nicht verfügbar: 4444."); } clientSocket = serverSocket.accept(); } catch (IOException e) { System.err.println("Accept fehlgeschlagen."); } senden = new PrintWriter(clientSocket.getOutputStream(), true); empfangen= new BufferedReader(new InputStreamReader(clientSocket.getInputStream()) ); } Wie bereits erläutert wurde, werden hierbei Sockets eingerichtet, über die Daten gesendet bzw. empfangen werden können. Zum Senden und Empfangen werden Streams implementiert. Die im Folgenden beschriebene Methode getDaten() empfängt Daten vom Client. Hierbei findet auch das Protokoll Verwendung. Die Methode liest zuerst die Statusinformation, anschließend das Koordinatenpaar.
public Daten getData () throws IOException{ Daten d = new Daten(); //Auslesen der Statusinformation if (empfangsZeile.equalsIgnoreCase("0")) d.status = d.SCHUSS; if (empfangsZeile.equalsIgnoreCase("2")) d.status = d.TREFFER; } //Empfangen der Koordinaten d.p.x = Integer.parseInt(empfangsZeile); if ((empfangsZeile = empfangen.readLine()) != null) d.p.y = Integer.parseInt(empfangsZeile); System.out.println("Client schickt: ("+d.p.x+","+d.p.y+")"); return d; } Zum Senden von Daten wird die Methode sendData() verwendet, die die Statusinformation und ein Koordinatenpaar sendet. Hierbei sei auf einen kleinen Trick hingewiesen. Um eine Zahl in einen String konvertieren zu können, wird eine Kombination aus Anführungszeichen und der Zahl verwendet.
public void sendData (Daten d) { //Senden der Statusinfo, der x- und y-Koordinaten } Zum Abbau der Verbindung müssen die Streams und die Sockets mittels close geschlossen werden.
public void beenden () { try { senden.close(); } catch (IOException e) { System.out.println("Fehler beim Schliessen der Verbindung"); } } } Client Die Funktionalität des Clients unterscheidet sich von der des Servers in Bezug auf den Aufbau und den Abbau einer Verbindung. Die Prozeduren zum Senden und Empfangen der Daten sind jedoch gleich.
import java.io.*; String sendeZeile, empfangsZeile; public SVClient (GUISVUser s) throws IOException{ this.s = s; SVSocket = new Socket("localhost", 4444); empfangen = new BufferedReader(new InputStreamReader(SVSocket.getInputStream() )); } catch (UnknownHostException e) { System.err.println("Host unbekannt: localhost."); } catch (IOException e) { System.err.println("I/O abgelehnt."); return; } } Der Verbindungsaufbau des Clients wurde bereits in Kapitel 5.4 detailliert betrachtet. Es sei darauf hingewiesen, dass eine Fehlermeldung ausgegeben werden muss, wenn keine Netzwerkverbindung zustande kommt. Hierzu wird die Methode ausgabeFenster aufgerufen, die ein dementsprechendes Popup-Fenster erzeugt. Die Methoden getData und sendData entsprechen denen des Servers.
public Daten getData () throws IOException{ Daten d = new Daten(); if (empfangsZeile.equalsIgnoreCase("0")) d.status = d.SCHUSS; if (empfangsZeile.equalsIgnoreCase("2")) d.status = d.TREFFER; } if ((empfangsZeile = empfangen.readLine()) != null) d.p.x = Integer.parseInt(empfangsZeile); if ((empfangsZeile = empfangen.readLine()) != null) d.p.y = Integer.parseInt(empfangsZeile); System.out.println("Server schickt: ("+d.p.x+","+d.p.y+")"); } //Senden der Statusinfo, der x- und y-Koordinaten } Ähnlich wie beim Server werden abschließend die Streams und die Sockets geschlossen.
public void beenden () throws IOException{ try { senden.close(); } catch (IOException e) { s.ausgabeFenster("Probleme bei der Netzwerkverarbeitung"); } } } Es sei darauf hingewiesen, dass im Unterschied zu Kapitel 5.4 das Protokoll in die Netzwerkklassen integriert wurde. Aufgrund der geringen Komplexität würde eine eigenständige Verarbeitung nicht sinnvoll sind. |
|
|