/* * Engine Alpha ist eine anfängerorientierte 2D-Gaming Engine. * * Copyright (c) 2011 - 2014 Michael Andonie and contributors. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package ea; import ea.edu.net.NetzwerkInterpreter; import ea.internal.util.Logger; import java.io.*; import java.net.Socket; import java.net.UnknownHostException; /** * Diese Klasse ermöglicht das Aufbauen einer Client-Verbindung zu einem Server. * * @author Michael Andonie */ public class Client extends Thread implements Empfaenger, SenderInterface { /** * Die gewünschte Ziel-IP-Adresse des Socket */ private final String ipAdresse; /** * Der Name, mit dem sich der Client beim Server vorstellt. */ private final String name; /** * Der Port des Socket. */ private final int port; /** * Der Socket, über den die Verbindung aufgebaut wird. */ private Socket socket; /** * Diese Verbindung ist ungleich null, sobald die Verbindung mit dem Server aufgebaut wurde. */ private NetzwerkVerbindung verbindung; /** * Falls Verbindungsversuch scheitert, wird diese Variable <code>true</code>. */ private boolean connectFailed; /** * Erstellt einen neuen Client. * @param ipAdresse Die IP-Adresse des Servers, mit dem sich der Client verbinden soll. * @param port Der Port, an dem sich der Client mit dem Server verbinden soll. */ public Client (String ipAdresse, int port) { this("Unbenannter Client", ipAdresse, port); } /** * Erstellt einen neuen Client. * @param name Der Name, mit dem sich der Client (im Hintergrund) dem Server vorstellt. Wird nur * intern verwendet. * @param ipAdresse Die IP-Adresse des Servers, mit dem sich der Client verbinden soll. * @param port Der Port, an dem sich der Client mit dem Server verbinden soll. */ public Client (String name, String ipAdresse, int port) { this.setDaemon(true); this.name = name; this.ipAdresse = ipAdresse; this.port = port; start(); } /** * Die run-Methode des Threads baut eine Verbindung zum Server aus. Sobald dieser Thread * erfolgreich abgeschlossen ist, kann die Verbindung zur Kommunikation genutzt werden. */ @Override public void run () { try { socket = new Socket(ipAdresse, port); // Stelle sicher, dass der Socket auch wieder geschlossen wird. Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run () { verbindungSchliessen(); } }); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); InputStream is = socket.getInputStream(); bw.write("xe" + name); bw.newLine(); bw.flush(); String ip = socket.getInetAddress().getHostAddress(); // set up interpreter NetzwerkInterpreter interpreter = new NetzwerkInterpreter(ip, null, new BufferedReader(new InputStreamReader(is))); interpreter.empfaengerHinzufuegen(this); NetzwerkVerbindung vb = new NetzwerkVerbindung(name, ip, bw, interpreter); verbindung = vb; synchronized (this) { this.notifyAll(); } } catch (UnknownHostException e) { Logger.error("Konnte die IP-Adresse nicht zuordnen..."); connectFailed = true; } catch (IOException e) { Logger.error("Es gab Input/Output - Schwierigkeiten. Sind ausreichende Rechte fuer" + " Internet etc. vorhanden? Das System könnte die Netzwerkanfrage ablehnen."); connectFailed = true; } } /** * Schließt die Verbindung mit dem Server. */ public void verbindungSchliessen () { if (!socket.isClosed()) { verbindung.beendeVerbindung(); try { socket.close(); } catch (IOException e) { Logger.error("Konnte den Verbindungs-Socket nicht mehr schliessen."); } } } /** * Setzt den Empfänger, der über jede Nachricht an diesen Client informiert wird. * * @param e * Der Empfaenger, and den alle Nachrichten an diesen Client weitergereicht werden sollen. */ public void empfaengerHinzufuegen (Empfaenger e) { warteAufVerbindung(); this.verbindung.getInterpreter().empfaengerHinzufuegen(e); } /** * Diese Methode <b>stellt sicher</b>, dass eine Verbindung mit dem Server besteht.<br /> Diese * Methode friert den ausführenden Thread ein, wenn noch keine Verbindung besteht und endet * erst, wenn die Verbindung aufgebaut wurde. */ public void warteAufVerbindung () { if (verbindung == null) { synchronized (this) { try { wait(); } catch (InterruptedException e) { Logger.warning("Achtung. Es könnte trotz warteAufVerbindung() noch " + "keine Verbindung bestehen, da der Warteprozess unterbrochen wurde."); } } } //Additional Waiting: 200 ms to ensure synchronization buffer on server side. try { Thread.sleep(200); } catch (InterruptedException e) {} } /** * {@inheritDoc} Sendet, sofern die Verbindung zum Server bereits aufgebaut wurde. Sonst * passiert <b>wird solange gewartet, bis der Client sich mit einem Server verbinden * konnte.</b>. */ @Override public void sendeString (String string) { warteAufVerbindung(); verbindung.sendeString(string); } /** * {@inheritDoc} Sendet, sofern die Verbindung zum Server bereits aufgebaut wurde. Sonst * passiert <b>wird solange gewartet, bis der Client sich mit einem Server verbinden * konnte.</b>. */ @Override public void sendeInt (int i) { warteAufVerbindung(); verbindung.sendeInt(i); } /** * {@inheritDoc} Sendet, sofern die Verbindung zum Server bereits aufgebaut wurde. Sonst * passiert <b>wird solange gewartet, bis der Client sich mit einem Server verbinden * konnte.</b>. */ @Override public void sendeByte (byte b) { warteAufVerbindung(); verbindung.sendeByte(b); } /** * {@inheritDoc} Sendet, sofern die Verbindung zum Server bereits aufgebaut wurde. Sonst * passiert <b>wird solange gewartet, bis der Client sich mit einem Server verbinden * konnte.</b>. */ @Override public void sendeDouble (double d) { warteAufVerbindung(); verbindung.sendeDouble(d); } /** * {@inheritDoc} Sendet, sofern die Verbindung zum Server bereits aufgebaut wurde. Sonst * passiert <b>wird solange gewartet, bis der Client sich mit einem Server verbinden * konnte.</b>. */ @Override public void sendeChar (char c) { warteAufVerbindung(); verbindung.sendeChar(c); } /** * {@inheritDoc} Sendet, sofern die Verbindung zum Server bereits aufgebaut wurde. Sonst * passiert <b>wird solange gewartet, bis der Client sich mit einem Server verbinden * konnte.</b>. */ @Override public void sendeBoolean (boolean b) { warteAufVerbindung(); verbindung.sendeBoolean(b); } /** * {@inheritDoc} Sendet, sofern die Verbindung zum Server bereits aufgebaut wurde. Sonst * passiert <b>wird solange gewartet, bis der Client sich mit einem Server verbinden * konnte.</b>. */ @Override public void beendeVerbindung () { warteAufVerbindung(); if (!verbindung.istAktiv()) { Logger.error("Die Verbindung zum Server wurde bereits beendet."); } verbindung.beendeVerbindung(); try { socket.close(); } catch (IOException e) { Logger.error("Konnte den Verbindungs-Socket nicht mehr schliessen."); } } /** * {@inheritDoc} Diese Methode kann von einer anderen Klasse ueberschrieben werden.<br /> So * wird man moeglichst einfach von neuen Nachrichten an den Client informiert. Natuerlich kann * man auch direkt einen <code>Empfaenger</code> an diesem Client anmelden. Der Effekt ist * derselbe. * * @see #empfaengerHinzufuegen(Empfaenger) */ @Override public void empfangeString (String string) { // To be overwritten } /** * {@inheritDoc} Diese Methode kann von einer anderen Klasse ueberschrieben werden.<br /> So * wird man moeglichst einfach von neuen Nachrichten an den Client informiert. Natuerlich kann * man auch direkt einen <code>Empfaenger</code> an diesem Client anmelden. Der Effekt ist * derselbe. * * @see #empfaengerHinzufuegen(Empfaenger) */ @Override public void empfangeInt (int i) { // To be overwritten } /** * {@inheritDoc} Diese Methode kann von einer anderen Klasse ueberschrieben werden.<br /> So * wird man moeglichst einfach von neuen Nachrichten an den Client informiert. Natuerlich kann * man auch direkt einen <code>Empfaenger</code> an diesem Client anmelden. Der Effekt ist * derselbe. * * @see #empfaengerHinzufuegen(Empfaenger) */ @Override public void empfangeByte (byte b) { // To be overwritten } /** * {@inheritDoc} Diese Methode kann von einer anderen Klasse ueberschrieben werden.<br /> So * wird man moeglichst einfach von neuen Nachrichten an den Client informiert. Natuerlich kann * man auch direkt einen <code>Empfaenger</code> an diesem Client anmelden. Der Effekt ist * derselbe. * * @see #empfaengerHinzufuegen(Empfaenger) */ @Override public void empfangeDouble (double d) { // To be overwritten } /** * {@inheritDoc} Diese Methode kann von einer anderen Klasse ueberschrieben werden.<br /> So * wird man moeglichst einfach von neuen Nachrichten an den Client informiert. Natuerlich kann * man auch direkt einen <code>Empfaenger</code> an diesem Client anmelden. Der Effekt ist * derselbe. * * @see #empfaengerHinzufuegen(Empfaenger) */ @Override public void empfangeChar (char c) { // To be overwritten } /** * {@inheritDoc} Diese Methode kann von einer anderen Klasse ueberschrieben werden.<br /> So * wird man moeglichst einfach von neuen Nachrichten an den Client informiert. Natuerlich kann * man auch direkt einen <code>Empfaenger</code> an diesem Client anmelden. Der Effekt ist * derselbe. * * @see #empfaengerHinzufuegen(Empfaenger) */ @Override public void empfangeBoolean (boolean b) { // To be overwritten } /** * {@inheritDoc} Diese Methode kann von einer anderen Klasse ueberschrieben werden.<br /> So * wird man moeglichst einfach von neuen Nachrichten an den Client informiert. Natuerlich kann * man auch direkt einen <code>Empfaenger</code> an diesem Client anmelden. Der Effekt ist * derselbe. * * @see #empfaengerHinzufuegen(Empfaenger) */ @Override public void verbindungBeendet () { // To be overwritten } public boolean verbindungGescheitert () { return connectFailed; } }