# Serverliste aktualisieren



## Dagobert (6. Jun 2008)

Ich möchte gerne eine Serverliste erstellen und nach einer gewissen Zeit x aktualisieren.
Also ich habe mehrere Server im Netz und ein par Clients.
Jetzt soll der Client alle Computer im Netz abfragen ob dort der Socket 665 verfügbar ist und ob er eine Antwort bekommt. Wenn er eine Antwort bekommt weis er das der Server auf dem Computer läuft, und er soll den Namen des Servers, und die IP des Servers in einer JList anzeigen, damit ich mit einem Doppelklich oder durch klick auf den Button genauere Serverinfos bekomme.
Meine Idee sieht so aus:

1. Eine Nachricht an alle Computer im Netz senden auf port 665.
2. Wenn ein Server läuft seine Adresse und Name zum Anfragenen Client zurückschicken
3. Name & IP in der JList ablegen
4. Mit Doppelklick oder Button auf Server connecten

Dort liegt mein Problem momentar daran, das ich nicht weis oder es nicht hinbekomme eine Nachricht an jeden Computer zu schicken, und eine Antwort zum Client zurückzuschicken.

Vielleicht würde mir ein Beispielcode oder vergleichbarer Code schon helfen 

Dannach habe ich das Problem das das connecten nicht richtig funktioniert. Der erste Client kann zum Server verbinden und bekommt auch die richtige Nachhricht zurück. Dannach soll Client 2 verbinden. Doch dabei kommt es zur ein Exception.
Wobei ich das nicht verstehe.
Mein Client soll doch auf Socket 666 verbinden. Wenn dieses nicht klappt, soll er versuchen auf Socket 667 zu verbinden. Dieses klappt jedoch nicht.
Wo liegt denn da mein Fehler?

Server:


```
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.*;

public class Server implements Runnable {
	ServerSocket sp1;
	ServerSocket sp2;
	
	public Server() {
		try {
			sp1 = new ServerSocket(666);
			sp2 = new ServerSocket(667);
			System.out.println("Server gestartet\nAuf Spieler warten...");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void run() {
		try {
			DatagramSocket socket = new DatagramSocket();
			DatagramPacket packet = new DatagramPacket( new byte[1024], 1024);
			socket.receive( packet );
			socket.close();
			InetAddress clintadresse = packet.getAddress();
			System.out.println("clintadresse : " + clintadresse.toString());
			Socket socket1 = sp1.accept();
			System.out.println("Spieler 1 verbunden");
			BufferedWriter out1 = new BufferedWriter(new OutputStreamWriter(
					socket1.getOutputStream()));
			out1.write("Herzlich willkommen Spieler 1");
			out1.flush();
			out1.newLine();
			out1.close();
			Socket socket2 = sp2.accept();
			System.out.println("Spieler 2 verbunden");
			BufferedWriter out2 = new BufferedWriter(new OutputStreamWriter(
					socket2.getOutputStream()));
			out2.write("Herzlich willkommen Spieler 2");
			out2.flush();
			out2.newLine();
			out2.close();
			System.out.println("Server: Verbindung hergestellt\nSp1: "
					+ sp1.getInetAddress() + "\nSp2: " + sp2.getInetAddress());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
```

Client


```
import java.awt.*;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import javax.swing.*;

public class MultiplayLobby implements MouseListener, ActionListener {
	int modus; // 0: Lan; 1: Online;
	GamePanel gamepanel;

	Timer getServerIntervall; // Intervall in dem die Serverliste aktualisiert
								// wird
	JList server; // Liste die alle verfügbaren Server beinhaltet

	JButton startserver, startclient; // Butten um ein Spiel zu Starten

	class listServer {
		String name, ip;

		public listServer() {
			this.name = null;
			this.ip = null;
		}

		public listServer(String name, String ip) {
			this.name = name;
			this.ip = ip;
		}

		public String getName() {
			return this.name;
		}

		public String getIp() {
			return this.ip;
		}
	}

	JTextArea chatSee;
	JTextField chatWrite;

	public MultiplayLobby(int modus, GamePanel gamepanel) {
		this.modus = modus;
		this.gamepanel = gamepanel;
		this.gamepanel.removeAll();
		this.gamepanel.setBackground(Color.black);
		this.gamepanel.updateUI();

		getServerIntervall = new Timer(5000, this);
		getServerIntervall.start();

		server = new JList();
		server.getModel().getElementAt(0);
		server.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		server.addMouseListener(this);
		server.setBounds(10, 10, this.gamepanel.getWidth() - 20, 150);
		this.gamepanel.add(server);

		startserver = new JButton("Spiel erstellen");
		startserver.setSize(120, 40);
		startserver
				.setBounds(this.gamepanel.getWidth() - startserver.getWidth()
						- 10, server.getHeight() + 20, 120, 40);
		startserver.addActionListener(this);
		this.gamepanel.add(startserver);

		startclient = new JButton("Spiel beitreten");
		startclient.setSize(120, 40);
		startclient.setBounds(startserver.getX(), startserver.getY()
				+ startserver.getHeight() + 20, 120, 40);
		startclient.addActionListener(this);
		this.gamepanel.add(startclient);
	}

	public void mouseClicked(MouseEvent e) {
		if (e.getClickCount() == 2) {
			int index = server.locationToIndex(e.getPoint());
			System.out.print("Item: " + index);
		}
	}

	public void actionPerformed(ActionEvent e) {
		if (e.getSource().equals(getServerIntervall)) {
			System.out.print("Neue Serverliste holen....");
			try {
				DatagramSocket ds = new DatagramSocket(665);
				ds.setBroadcast(true);
				byte[] data = new byte[1024];
				DatagramPacket packet = new DatagramPacket(data, data.length);
				ds.send(packet);
				System.out.println(packet.getAddress().toString());
				ds.close();
			} catch (SocketException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			System.out.println(" OK");
		}
		if (e.getActionCommand() == "Spiel erstellen") {
			Server s = new Server();
			Thread t = new Thread(s);
			t.start();
		}
		if (e.getActionCommand() == "Spiel beitreten") {
			Socket socket = null;
			String ip = JOptionPane.showInputDialog(this.gamepanel,
					"Server IP:");
			try {
				System.out.print("Verbinde (666)...");
				socket = new Socket(ip, 666);
				System.out.println(" OK");
			} catch (UnknownHostException ev) {
				System.out.println(" false");
			} catch (IOException ev) {
				System.out.println(" false");
				try {
					System.out.print("Verbinde (667)...");
					socket = new Socket(ip, 667);
					System.out.println(" OK");
				} catch (UnknownHostException e1) {
					System.out.println(" false");
				} catch (IOException e1) {
					System.out.println(" false");
				}
				System.out.println(" false");
			} finally {
				BufferedReader in;
				try {
					in = new BufferedReader(new InputStreamReader(socket
							.getInputStream()));
					System.out.println(in.readLine());
				} catch (IOException e2) {
					System.out.println("Nachrichtenfehler");
				}
			}
		}
	}
}
```

Wäre sehr nett wenn sich jemand sich meiner Probleme annehmen könnte.

mfg. Dagobert


----------



## tuxedo (6. Jun 2008)

Argh. Der Ansatz ist schlecht. Lasse den Client eine Multicast-Anfrage rausschicken und die Server dann darauf antworten. Einen IP-Adressbereich durch iterieren und alle einzeln fragen ist "schlecht". 

Zur Sache mit mehreren Clients:

Ich finde nirgens ein while() Konstrukt oder sowas (oder ich habs übersehen). Was macht denn dein Server wenn ein Client fertig ist? 

- Alex

P.S. Vermeide die Verwendung von Ports <=1024 wenn du keinen "bekannten" Dienst (wie z.B. WWW, FTP, ...) mit deiner Serveranwendung bereit stellst.


----------



## Dagobert (6. Jun 2008)

Der Server macht erstmal noch gar nix... ich will erstmal zwei Verbindungen zu stande zu bekommen und das Serverlisting.

Ich lese mich gerade in Mulitcast sockets hinein, aber ich glaube damit kriege ich das Problem des Serverlisting auch nicht in den Griff oder irre ich mich da? :?:


----------



## tuxedo (6. Jun 2008)

Du irrst dich. 

Und ein kleiner Tipp: Bevor man irgendwas anfängt von dem man noch nicht weiß ob's klappt, baut man sich ein kleines Testprogramm um den ganzen krempel, der drum rum liegt und einen unerwünschetn seiteneffekt haben könnte, als fehlerquelle ausschließen zu können.

Bist du dir denn schon sicher ob du bei Datagrammen bleiben willst? Passt eine stream-verbindung nicht vielleicht besser? Hast du dir mal RMI oder SIMON angesehen? 


- Alex


----------



## Dagobert (6. Jun 2008)

Ich wollte die Datagrame nur für das ermitteln der Server benutzten.
Für die festen Verbindungen später sind Feste Verbindungen vorgesehen....

Ja ich hab ein bisschen über RMI gelesen, aber ich habe entschieden erstmal was mit Sockets zu machen, ich glaube das ist für das erste Netz nicht schlecht, damit man ein bisschen was von den Grundlagen mitbekommt.
oder meinst du ich soll gleich mit RMI anfangen?
Hab ich dadurch sehr große Vorteile?

Edit: Ok ich wühle mich gerade durch die Insel und ich glaube ich habe davon größere Vorteile als vorher gedacht....


----------



## tuxedo (6. Jun 2008)

Wobei du dir dann gleich auch SIMON anschauen solltest. Vor allem wenn das Spiel über's Internet spielbar sein soll.

Multicasts über Internet sind übrigens nicht möglich (kenne keinen Provider der das zulässt ;-) ). D.h. um über's Internet eine Liste mit Servern zu erhalten bräuchtest du einen Zentralen Server der als Liste fungiert und Buch über die Server führt.

- Alex


----------



## Dagobert (6. Jun 2008)

Ne online Modus ist erstmal nicht vorgesehen... sondern nur lokal...
aber ich blicke druch RMI gerade mal voll net so ganz durch XD
kannst du mir ne gute tut empfehelen?


----------



## tuxedo (7. Jun 2008)

such mal hier im Forum. Irgendwo hat mir vor einiger Zeit einer ein Grundgerüst (sogar mit Callback) gepostet.

Alternativ kannst du auch SIMON benutzen. 
Öffne diesen Link und scrolle bis "Codesample" runter: https://simon.dev.java.net/

RMI verwendet nahezu die gleiche Struktur. Soll heissen: Wenn du mir RMI umgehen kannst, kannst du's auch mit SIMON (und umgekehrt). 

RMI ist zwar im Moment nen Zacken schneller als SIMON, aber die wenigsten werden da einen Unterschied in ihrer Anwendung feststellen. 
Sollte deine Anwendung doch irgendwann mal übers INet laufen können/sollen, dann musst du dich mit RMI vorsehen:

Callbacks, also selbstständige Rückmeldungen vom Server an den Client, sind hier nicht ohne 3fachen Kopfstand möglich. SIMON hingegen hat damit keine Probleme.

Letzten endes bleibt es dir überlassen wa du nimmst: Sockets, Datagrame, RMI oder SIMON

Gruß
Alex

P.S. Lass dich vom englischen Inhalt der SIMON Projektseite nicht abschrecken. Hinter "Discussion Forums" gibts es auch ein deutschsprachiges Board.


----------



## Dagobert (7. Jun 2008)

Neee ich hab mich ans Englisch gewöhnt auch wenn ich jedes mal 1000 Wörter nachschalgen muss und es nicht so mein fall ist...
aber Läuft...
aber meine Frage ist nun ab es auch eine gute TUT für dein Projekt gibt, wenn möglich auch auf deutsch, oder hast du ein par kleine Beispielcodes für mich?

mfg. Dagobert


----------



## tuxedo (7. Jun 2008)

Naja, das "Codesample" bildet nahezu die gesamte Funktionalität ab.

Alles was du tun musst ist das Codesample bei dir ans laufen zu bringen, und dann die zwei Interfaces und deren Implementierung nach deinen Wünschen anpassen.

Oder hast du die prinzipielle Funktion von RMI/SIMON noch nicht verstanden?

Vielleicht hilft das dabei:
http://de.wikipedia.org/wiki/Remote_Method_Invocation

SIMON macht prinzipiell exakt das gleiche.

- Alex


----------



## Dagobert (7. Jun 2008)

Ich hab ja das Codesampel am laufen..
Ich weiß auch grob was geschehen soll... also wenn ich das richtig verstanden habe, dann kann man damit entfernte Methoden auf dem Server aufrufen... das finde ich auch super und ist eigentlich genau sowas wie ich gesucht habe. Nur leider  scheiterts bei mir an der Umsetznug weil ich noch net ganz weiß wie ich das anstellen soll. Ich weiß das ich ein Interface brauche dann noch eine Implementierung des Interface und dann eine Instanz die ich beim Server anmelden muss, oder irre ich mich da?
Beim Client muss ich das gleiche machen, auser das ich den Client nicht regestrieren muss?!

Ich habe in Simon einfach mal versucht eine Mthode hinzuzufügen, die zwei Zahlen addiert, leider bin ich daran schon gescheiter.

Ich habe beim Server die Methode 
	
	
	
	





```
public int addieren(int x, int y){return x+y;}
```
 hinzugefügt. Doch wie rufe ich diese von Client auf. Ich habs mit 
	
	
	
	





```
server.addieren(1,1);
```
 versucht, aber so scheint das nicht zu klappen.
Wie stelle ich das nun an, das die Remotemethode ausgeführt wird?

mfg. Dagonert


----------



## tuxedo (7. Jun 2008)

Hi,

Glückwunsch zum funktionierenden Codesample ;-)

Also, mit dem addieren:

1. Schritt:

Nimm das Interface 'ServerInterface' und baue folgende Methode ein:


```
public int addiere(int zahl1, int zahl2) throws SimonRemoteException;
```

Dann gehst du in die Klasse 'ServerInterfaceImpl'. Dort musst du die Methode dann "implementieren":


```
public int addiere(int zahl1, int zahl2) throws SimonRemoteException
{
return zahl1+zahl2;
}
```

Beim Client kannst du dann folgendes machen:


```
ServerInterface server = (ServerInterface) Simon.lookup("127.0.0.1", 2000, "server");

int ergebnis = server.addiere(10, 123); 
System.out.println("Ergebnis="+ergebnis);
```

Das mit dem "Callback" funktioniert dann genau so. Alle Methoden die du in das Callback-Interface rein schreibst und in der Callback-Klasse implementierst, kann der Server aufrufen. Diese Aufrufe finden dann aber beim Client statt. Deshlab auch "Call back" (Aufruf zurück an den Client). 

- Alex


----------



## Dagobert (7. Jun 2008)

Ok das hatte ich eignetlich so (oder hab ich was übersehen), aber er sagt mir das die Methode im ServerInt nicht vorhanden ist. Ich poste mal alle 3 Server Klassen und den Client

Client:

```
package SIMON;

import java.net.ConnectException;

import de.root1.simon.Simon;
import de.root1.simon.SimonRemoteException;
import de.root1.simon.codesample.common.ServerInterface;

public class Client {

	public static void main(String[] args) throws SimonRemoteException,
			ConnectException {

		ClientImpl clientCallbackImpl = new ClientImpl();
		System.out.println("callback object created");

		ServerInterface server = (ServerInterface) Simon.lookup("127.0.0.1",
				2000, "server");

		System.out.println(server.addieren(1,1));	
	}

}
```
Server Interface:

```
package SIMON;

import de.root1.simon.SimonRemote;
import de.root1.simon.SimonRemoteException;

public interface ServerInt extends SimonRemote {

	public void login(ClientInt clientCallback) throws SimonRemoteException;
	public int addieren(int x, int y) throws SimonRemoteException;
}
```
Server Imp

```
package SIMON;

import de.root1.simon.Simon;
import de.root1.simon.SimonRemoteException;
import de.root1.simon.codesample.common.ClientCallbackInterface;
import de.root1.simon.codesample.common.ServerInterface;

public class ServerImpl implements ServerInterface {

	private static final long serialVersionUID = 1L;

	public void login(ClientCallbackInterface clientCallback)
			throws SimonRemoteException {
		clientCallback.callback("This it the callback. " + "Your address is "
				+ Simon.getRemoteInetAddress(clientCallback) + " "
				+ "and your are connected from port "
				+ Simon.getRemotePort(clientCallback));
	}
	public int addieren(int x, int y) throws SimonRemoteException{
		return x+y;
	}
}
```
Server

```
package SIMON;

import de.root1.simon.Simon;

public class Server {

	public static void main(String[] args) {
		ServerImpl serverImpl = new ServerImpl();
		Simon.createRegistry(2000);
		Simon.bind("server", serverImpl);
		System.out.println("Server started");
	}

}
```


----------



## tuxedo (7. Jun 2008)

Kein Wunder. Fällt dir was auf?


```
public interface ServerInt extends SimonRemote
```


```
public class ServerImpl implements ServerInterface
```

Du solltest zusehen dass du auch das richtige Interface verwendest/implementierst.


----------



## Dagobert (7. Jun 2008)

Ok das war ein interessanter Einwand^^ jetzt hats auch geklappt.
Dann hab ich ein bisschen weitergeschrieben...

Ich wollte ein kleinen Chatserver + Client erstellen... aber da gibt es auch ein par Probleme.
1. Ich bekomme folgende Fehlermeldung 





> Exception in thread "main" java.lang.ClassCastException: $Proxy0 cannot be cast to SIMON.ChatServerImpl
> at SIMON.ChatClient.<init>(ChatClient.java:10)
> at SIMON.ChatClient.main(ChatClient.java:17)


Warum dies schon wieder?
Kannst mir so ein Tipp geben oder brauchst du wieder den Code?


----------



## tuxedo (7. Jun 2008)

Zu einer Fehlermeldung brauch ich immer etwas code. Zumindest das, was in und um Zeile 10 der Datei ChatClient.java steht (siehe Exception).

- Alex


----------



## Dagobert (7. Jun 2008)

Ok danke Poste ich gleich meinen ganzen Code, weil er auch eine "Rückanwrwort-Methode" nicht findet.

```
package SIMON;

import java.util.Vector;

import de.root1.simon.SimonRemote;
import de.root1.simon.SimonRemoteException;

public interface ChatServerInterface extends SimonRemote {
	Vector<ChatClient> client = null;
	public int newClient(ChatClient chatClient) throws SimonRemoteException;
	public void disconnectClient(ChatClient chatClient) throws SimonRemoteException;
	public void sendNachricht(int clientNr, String nachricht) throws SimonRemoteException;
	public String getStatus() throws SimonRemoteException;
	public void shutdown() throws SimonRemoteException;
}
```


```
package SIMON;

import java.util.Vector;

import de.root1.simon.SimonRemoteException;

public class ChatServerImpl implements ChatServerInterface {

	private static final long serialVersionUID = 1L;
	Vector<ChatClient> clients;
	public ChatServerImpl() {
		clients = new Vector<ChatClient>();
	}

	@Override
	public void disconnectClient(ChatClient chatClient) throws SimonRemoteException {
		clients.remove(chatClient);
		System.out.println("Server: Client entfernt");
	}

	@Override
	public String getStatus() throws SimonRemoteException {
		return "Test";
	}

	@Override
	public int newClient(ChatClient chatClient) throws SimonRemoteException {
		clients.add(chatClient);
		System.out.println("Server: Client hinzugefügt");
		return clients.indexOf(chatClient);
	}

	@Override
	public void sendNachricht(int clientNr, String nachricht) throws SimonRemoteException {
		for(int i = 0; i <= clients.size();i++){
			if(i != clientNr)
			clients.elementAt(i).sendNachricht(clients.elementAt(clientNr).getName() + ": " + nachricht); // Kann er nicht finden
		}
		
	}

	@Override
	public void shutdown() throws SimonRemoteException {
		System.exit(0);
		
	}
}
```


```
package SIMON;

import de.root1.simon.Simon;

public class Server {

	public static void main(String[] args) {
		ChatServerImpl chatserverImpl = new ChatServerImpl();
		Simon.createRegistry(2000);
		Simon.bind("ChatServer", chatserverImpl);
		System.out.println("Chatserver started");
	}

}
```


```
package SIMON;

import de.root1.simon.SimonRemote;
import de.root1.simon.SimonRemoteException;

public interface ChatClientInterface extends SimonRemote {
	public void sendNachricht(String nachricht) throws SimonRemoteException;
}
```


```
package SIMON;

import de.root1.simon.SimonRemoteException;

public class ChatClientImpl implements ChatClientInterface {

	private static final long serialVersionUID = 1L;

	@Override
	public void sendNachricht(String nachricht) throws SimonRemoteException {
		System.out.println(nachricht);
	}

}
```


```
package SIMON;

import java.net.ConnectException;

import de.root1.simon.Simon;
import de.root1.simon.SimonRemoteException;

public class ChatClient{
	private int clientNr;
	private String name;
	public ChatClient(String name) throws SimonRemoteException , ConnectException {
		this.name = name;
		ChatServerImpl server = (ChatServerImpl) Simon.lookup("127.0.0.1",
				2000, "ChatServer");
		System.out.println("Anfragen an Server werden gestartet");
		clientNr = server.newClient(this);
		server.sendNachricht(clientNr ,"test");
	}
	public String getName(){
		return name;
	}
	public static void main(String[] args) throws ConnectException, SimonRemoteException {
		new ChatClient("Testclient");
	}
}
```
Wie gesagt das vorhin beschriebene Problem besteht weiter, desweiter kann er die Methode sendNachricht des Clients nicht finden.
Dort muss ich doch eigentlich nur das gleiche Prinzip verwenden wie Client --> Server oder? nur hat anders herum Server --> Client?


----------



## EgonOlsen (8. Jun 2008)

Warum eigentlich so rum? Wieso fragt jeder Client jede andere Maschine im Netz, ob sie ein Server ist? Der Server selber weiß das doch am besten. Also kann er das einfach in gewissen Zeitabständen per Broadcast rausschicken. Dann hast du alle X Sekunden einen Broadcast durchs Netz laufen und nicht soviele, wie du Clients hat (+ die Antwort...).


----------



## Dagobert (8. Jun 2008)

Anders herum würde es natürlich auch gehen... hab ich wenn ich erlich bin noch gar nicht beachtet, hört sich aber besser an, abwohl so eine kleine Anfrage das Netz eigentlich nicht belasten dürfte XD
Aber mir stellt sich immer noch die Frage wie ich in Java ein Broadcast anstelle, allso das ich an alle Clients eine Nachricht schicke???

mfg. Dagobert


----------



## tuxedo (8. Jun 2008)

Die Suche nach Servern im Netz würd ich auch über einen Broadcast machen. Das kann SIMON bis jetzt noch nicht (RMI auch nicht). 

Zu dem Casting-Problem: Der Client, der sich das Serverobjekt holt, castet immer nur auf das ServerINTERFACE und nicht auf die ServerIMPLEMENTIERUNG.

Allein schon aus "Sicherheitsgründen" ist es ratsam dem Client nur das Serverinterface zu geben, und nicht dessen implementierung. Denn genau die soll ja den Client gar nicht interessieren.

- Alex


----------



## tuxedo (8. Jun 2008)

Google kennst du? --> http://www.google.de/search?hl=de&q=java+broadcast&btnG=Google-Suche&meta=

Allein die ersten 3 Suchergebnisse umfassen alles was man wissen muss. Inklusive Beispielcode

- Alex


----------



## EgonOlsen (8. Jun 2008)

Dagobert hat gesagt.:
			
		

> Aber mir stellt sich immer noch die Frage wie ich in Java ein Broadcast anstelle, allso das ich an alle Clients eine Nachricht schicke???


Ein DatagramPacket an 255.255.255.255 schicken. Das ist ein Broadcast ins LAN. Alle Clients, die auf dem entsprechenden Port lauschen bekommen das dann...ok, es ist UDP, also nicht zwangsläufig, aber im LAN eigentlich schon und es ist ja auch nicht so wichtig, wenn mal ein Datagram verloren geht.


----------



## Illuvatar (8. Jun 2008)

Dagobert hat gesagt.:
			
		

> Aber mir stellt sich immer noch die Frage wie ich in Java ein Broadcast anstelle, allso das ich an alle Clients eine Nachricht schicke???



Ich verwende eigentlich immer diese Klasse.


----------



## Titanpharao (8. Jun 2008)

Hey alex0801 ich wollte jetzt auch mal Simon benutzen. Ich glaube das ist ganz hilfreich für mein Spiel, was ich machen möchte. Wie sieht es mit der Geschwindigkeit aus? Es arbeitet doch auf TCP/IP Basis, ich denke dadurch wird es im Internet sehr langsam, weil die meisten Spiele laufen doch auf UDP oder? 
Also wird so ein kleines Rollenspiel mit 4-5 Spielern auf einer Karte gehen? 

Oder wofür wäre es besser benutzbar?
Ich hätte mir das sonst so vorgestellt.
1.Schicke byte[] mit  spieler,laufen,richtung
2.switchcase oder sowas wo "laufen" ausgewertet wird und der server daran, die Methode bei sich aufruft.

Bei SIMON, kann ich doch direkt auf dem Client Server.laufen(x,y,z) aufrufen, oder habe ich das falsch verstanden  :###


----------



## Dagobert (8. Jun 2008)

Nee das haste schon richtig verstanden.
Aber ich hänge momentarn mal wieder fest. XD
Also ich dachte mir ich schreibe eine Kleine Chataplikation, doch jetzt finde ich kein "zurück" mehr.
Also ich kann am Server neue Clients hinzufügen und diese auch wieder entfernen. Von den Clients werden nur die IP Adressen in einem Vector auf dem Server gespeichert.
Jetzt schickt ein Client X eine Nachricht mit 
	
	
	
	





```
server.sendMessage(action, InetAddress.getLocalHost());
```
Jetzt soll der Server eigentlich die Nachricht nur zu jedem Client der im Vector gespeichert ist weiterleiten, mit Ausnahne des Senders.
Dazu habe ich diese Methode
	
	
	
	





```
public void sendMessage(String message, InetAddress ClientIP) throws SimonRemoteException{
		String completmessage = ClientIP.getHostName()+": "+message;
		ChatClientInterface client = (ChatClientInterface) new ChatClientImpl();
		for(int i = 0; i<clients.size(); i++){
			if(i != clients.indexOf(ClientIP)){
				client.getMessage(completmessage, clients.get(i));
			}
		}
	}
```
Wie bekomme ich die Nachricht jetzt alledings zu den Clients übertragen???
Hier noch mein ClientInterface und die ClientImpl:
Interface:

```
package SIMON;

import java.net.InetAddress;

import de.root1.simon.SimonRemote;
import de.root1.simon.SimonRemoteException;

public interface ChatClientInterface extends SimonRemote {
	public void getMessage(String message, InetAddress toIP) throws SimonRemoteException;
}
```
Implementierung
	
	
	
	





```
package SIMON;

import java.net.InetAddress;

import de.root1.simon.SimonRemoteException;

public class ChatClientImpl implements ChatClientInterface {

	private static final long serialVersionUID = 1L;

	@Override
	public void getMessage(String message, InetAddress toIP)
			throws SimonRemoteException {
		System.out.println(message);
	}

}
```

Danke für die Server-finde-Klasse die ist echt super  und ich kenne Google nur mangels mir manchmal an gedult immer die par Hits zu durchwühlen^^


----------



## tuxedo (8. Jun 2008)

Titanpharao hat gesagt.:
			
		

> Hey alex0801 ich wollte jetzt auch mal Simon benutzen. Ich glaube das ist ganz hilfreich für mein Spiel, was ich machen möchte. Wie sieht es mit der Geschwindigkeit aus? Es arbeitet doch auf TCP/IP Basis, ich denke dadurch wird es im Internet sehr langsam, weil die meisten Spiele laufen doch auf UDP oder?
> Also wird so ein kleines Rollenspiel mit 4-5 Spielern auf einer Karte gehen?



Wo hast du den Unfug mit "TCP ist zu langsam für Spiele" her??? 

Bestes Beispiel: World of Warcraft arbeitet auch mit TCP. Und da spielen ein paar tausend pro Server.

UDP ist für Echtzeitdaten wie Audio und Videostreaming wohl besser geeignet.
Bzgl. der Geschwindigkeit: Schau dir die Startseite der Projektseite an. Da findest du die Antwort.

Bzgl. den Methodenaufrufen: Ja, das hast du richtig verstanden.

- Alex


----------



## Dagobert (8. Jun 2008)

Ok das mit dem Chat klappt jetzt, kann ich jetzt irgendwie von dem Remoteobject nicht nur die IP bekommen, sondern auch den Namen?
Und wie kann ich mit Simon Complexere Objekte übertragen, wie z.B. Instanzen von Klassen o.a.. z.B. wenn ich meine ganzen Clients in einem Vector aufm Server abspeichern möchte?

EDIT: Ich habe da doch noch ein Problem gefunden. Ich füge ein Objekt in einen Vektor hinzu, aber kann dieses nicht löschen, vergleichen o.a..
Was mache ich denn da schon wieder falsch? :?: 


```
package SIMON;

import java.net.InetAddress;
import java.util.Vector;

import de.root1.simon.Simon;
import de.root1.simon.SimonRemoteException;

public class ChatServerImpl implements ChatServerInterface {

	private static final long serialVersionUID = 1L;
	private Vector<ChatClientInterface> clients;

	public ChatServerImpl() {
		clients = new Vector<ChatClientInterface>();
	}

	public boolean newClient(ChatClientInterface chatClient)
			throws SimonRemoteException {
		System.out.println(chatClient);
		clients.add(chatClient);
		return true;
	}

	public boolean deleteClient(ChatClientInterface chatClient)
			throws SimonRemoteException {
		System.out.println(chatClient);
		clients.remove(chatClient);
		return true;
	}

	public void sendMessage(String message, ChatClientInterface chatClient)
			throws SimonRemoteException {
		for (int i = 0; i < clients.size(); i++) {
				String completmessage = Simon.getRemoteInetAddress(chatClient)
						+ ": " + message;
				clients.elementAt(i).getMessage(completmessage);
		}
	}

	public String getStatus() {
		String status = "Es sind " + clients.size() + " Clients vorhanden";
		return status;
	}

	public void closeServer() throws SimonRemoteException {
		System.out.println("Server: Server wird heruntergefahren");
		System.exit(0);
	}
}
```


```
package SIMON;

import java.io.*;
import java.net.*;
import de.root1.simon.Simon;
import de.root1.simon.SimonRemoteException;

public class ChatClient {
	boolean connectToServer = false;

	public ChatClient() throws SimonRemoteException, IOException {
		BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
		ChatServerInterface server = null;
		ChatClientImpl chatclientimpl = new ChatClientImpl();
		do {
			System.out.println("Bitte geben sie die Server-IP ein: ");
			String serverip = bf.readLine();
			try {
				System.out.print("Client anmeldung... ");
				server = (ChatServerInterface) Simon.lookup(serverip, 2000,
						"server");
				connectToServer = server.newClient(chatclientimpl);
				System.out.println("OK");
			} catch(ConnectException e){
				connectToServer = false;
				System.out.println("false");
			}
		} while (!connectToServer);
		System.out.println("Serverstatus: " + server.getStatus());
		System.out.println("Nachticht tippen");
		System.out.println("disconnect zum Verbindung trennen");
		do {
			String action = bf.readLine();
			if ("disconnect".equals(action)) {
				server.deleteClient(chatclientimpl);
				connectToServer = false;
				System.out.println("Verbindung getrennt");
			} else if ("shutdown".equals(action)) {
				server.closeServer();
				connectToServer = false;
				System.out.println("Server heruntergefahren");
			} else if("status".equals(action)){
				server.getStatus();
			}else {
				server.sendMessage(action, chatclientimpl);
			}
		} while (connectToServer);
		bf.close();
		System.exit(0);
	}

	public static void main(String[] args) throws SimonRemoteException,
			IOException {
		new ChatClient();
	}
}
```
Wenn ich mal ein Test mache mit Hinzufügen und entfernen:


> Exception in thread "main" java.lang.NullPointerException
> at $Proxy0.deleteClient(Unknown Source)
> at SIMON.ChatClient.<init>(ChatClient.java:35)
> at SIMON.ChatClient.main(ChatClient.java:54)


bekomme ich diese Fehlermeldung. Wenn ich im Server das Objekt printen lasse einmal bein Hinzufügen und einmal beim löschen kommen dort andere Werte hinaus:


> [Proxy=SIMON.ChatClientImpl@52fe85|endpoint=/192.168.1.102:49858|invocationHandler=de.root1.simon.SimonProxy@1c29ab2|remote=SIMON.ChatClientImpl@52fe85]
> [Proxy=SIMON.ChatClientImpl@52fe85|endpoint=/192.168.1.102:49858|invocationHandler=de.root1.simon.SimonProxy@179c285|remote=SIMON.ChatClientImpl@52fe85]


mfg. Dagobert[/quote]


----------



## Titanpharao (9. Jun 2008)

Also bis jetzt finde ich SIMON super  :toll: 

Aber wie kann ich meine Klasse "unbinden"? Also möchte den Server stoppen sozusagen. 
Habe einfach mal Simon.bind("server",null") gemacht, geht, aber hässliche Fehlermeldung...

Und wie komme ich vom Server auf den Client? Also ich meine jetzt das beim Server etwas ausgelöst wird und er eine Methode beim Client aufrufen muss. Genauso wie beim Client -> Server? Aber das wären dann 2x Server und 2xClient oder  ???:L

Noch eine Frage, bevor ich schlafen gehe   

Ich wollte ein eigen erstelltest Objekt über eine Methode auf den Server bringen. Habe es mit einem großen und kleinen Objekt getestet. Dabei hat er mit beim Clienten aufruf der Methode diese hübsche Fehlermeldung ausgegeben.. :### 


```
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
	at $Proxy0.login(Unknown Source)
	at server.Client.main(Client.java:17)
Caused by: java.io.NotSerializableException: daten.SFeld
	at java.io.ObjectOutputStream.writeObject0(Unknown Source)
	at java.io.ObjectOutputStream.writeObject(Unknown Source)
	at de.root1.simon.Endpoint.wrapValue(Endpoint.java:664)
	at de.root1.simon.Endpoint.sendInvocationToRemote(Endpoint.java:403)
	at de.root1.simon.SimonProxy.invoke(SimonProxy.java:86)
	... 2 more
```


----------



## xote (9. Jun 2008)

Ich hab vor Jahren sowas mal an der Uni programmiert, waren eigentlich nur 4 Klassen:

1) Ein HostInfoObjekt in dem alles drinsteht was der Client von den Hosts wissen soll.
2) Ein TimerTask der in periodischen Abständen über einen Multicast-Socket ein HostInfoObjekt (#1) an die Adresse 224.0.0.1 schickt.
3) Ein Listening-Thread der die empfangenen Datagrampackets in eine Hashmap (#4) reinstöpselt
4) Eine Hashmap mit einer privaten Klasse (ebenfalls TimerTask) die alle Einträge nach einem gewissen Intervall wieder raushaut.

Hat super funktioniert. Nur mit der synchronized Methoden bei der Hashmap muss man aufpassen.


----------



## tuxedo (9. Jun 2008)

@Titanpharao
@Dagobert
Ich denke mittlerweile läuft der Thread am eigentlichen Thema vorbei. Support für SIMON gibts über die Projektseite http://simon.dev.java.net
Seid doch bitte so nett und meldet euch auf java.net an (das kann man als Java Entwickler sowieso gebrauchen) und benutzt den Link "Discussion forums" am linken Rand der Webseite. Dort gibts ein deutschsprachiges Support-Board. 

Nebenbei: Der Support für die NICHT-NIO-Variante wird auslaufen, da NIO einfach mehr Vorteile bietet. Nichts desto trotz kann man natürlich auch die normale Variante weiter benutzen. Diese ist zwar auch noch "ALPHA", aber einige haben damit schon Projekte erfolgreich umgesetzt.

Aber um die letzten Fragen zu SIMON hier noch zu beantworten:

>> Ich wollte ein eigen erstelltest Objekt über eine Methode auf den Server bringen. 

Simon Basier auf der Serialisierung. D.h. Objekte die Remote-Methoden übergeben werden müssen serialisierbar sein ("implements Serializable"). Ist irgendwas davon nicht serialisierbar, kommt z.B. die Meldung "java.io.NotSerializableException: daten.SFeld "

>> Aber wie kann ich meine Klasse "unbinden"? Also möchte den Server stoppen sozusagen. 

Bis jetzt noch nicht. Steht aber schon auf meiner TODO-Liste. Workaround: Du kannst Serverseitig sicherstellen dass kein Client mehr verbunden ist. Dann kannst du alle Threads und Datenbankanbindungen "beenden" und danach, als finales Statement "System.exit(0)" aufrufen. 

>> Und wie komme ich vom Server auf den Client? Also ich meine jetzt das beim Server etwas ausgelöst wird und er eine Methode beim Client aufrufen muss.

Stichwort: Callback. Schau dir den Sample-Code auf der Projektseite an. Speziell die Klassen/Interfaces die "Callback" im Namen tragen.

>> kann ich jetzt irgendwie von dem Remoteobject nicht nur die IP bekommen, sondern auch den Namen? 

?? Im Falle des Server-Objekts weißt du doch den Namen. Immerhin hast du ja ein "lookup" gemacht und dort einen Namen angegeben. Was für einen Namen willst du denn haben? Vielleicht den "qualified host name"? Das könnte ich noch einbauen. Aber man könnte auch ein reverse-lookup auf die IP-Adresse machen. Allerdings ist das etwas mehr aufwand als nur SIMON danach zu fragen ;-)

>> Ich habe da doch noch ein Problem gefunden. Ich füge ein Objekt in einen Vektor hinzu, aber kann dieses nicht löschen, vergleichen o.a.. Was mache ich denn da schon wieder falsch? 

Hab über deinen Code 2x drüber schauen müssen. Hab das Problem auch nicht gleich erkannt. Die Ursache ist, dass du jedesmal mit dem ChatClientImpl herum hantierst. Da sieht auf den ersten Blick okay aus. Aber Simon kann diese (im Moment noch) nicht unterscheiden. Jedesmal wenn ein Methodenargument auftaucht, wird darin nachgesehen ob es SimonRemote implementiert. Wenn ja, wird intern ein InvocationHandler aufgebaut und in einer entsprechenden Map gemerkt. Und da liegt der Knackpunkt: Da die Callback-Objekte sich (noch) nicht unterscheiden lassen, wird bei jedem Methodenaufruf dem ein solches Callback-Objekt beiliegt, ein neuer InvocationHandler aufgebaut. Und somit hast du serverseitig ständig neue Callback-Objekte in deinem Vector.
Workaround: Mach eine Login-Methode der du das Callback-Objekt mitgibst. In der ServerInterfaceImpl lässt du dann eine eindeutige ID generieren (z.B. ganz primitiv einen Zähler pro Login inkrementieren) und gibst die, bei erfolgreichen Login (also user + pw okay) dann als Return-Wert zurück. Wenn der Client sich nun ausloggen will oder sonst irgendwas, kannst du  statt dem Callback-Objekt die ID mitgeben. Der Server muss sich intern nur die ID und das Callback-Objekt ein einer HashMap merken. ANhand der ID kannst du dann alles was du mit dem Callback-Objekt machen willst/musst erledigen. 
Das klingt vielleicht auf den ersten Blick umständlich, hat aber auch den großen Vorteil dass nur die ID serialisiert werden muss und nicht die "komplexe" Objektstruktur deines Callback-Objekts. Nimmst du für die ID einen Integer, so hast du max. 4 bytes die Übertragen werden. Nimmst du ein (komplexes) Objekt muss das nicht nur aufwendig serialisiert werden (kostet Zeit), sondern es verbrät auch etliche bytes mehr in der Übertragung. 
Es gibt aber noch andere Designansätze (z.B. Session-Pattern) die das "mitführen" einer eindeutigen ID verhindern und so nochmal die 4bytes für die ID pro Methodenaufruf gespart werden können. Aber dazu dann mehr im Support-Board.



Zuguter letzt, nochmal für alle die nur wieder oberflächlich lesen: 
Support für SIMON gibts auf der Projektseite http://simon.dev.java.net unter "Discussion Forums" (Anmeldung auf java.net vorrausgesetzt).

- Alex


----------



## Dagobert (12. Jun 2008)

So ich habe jetzt mal länger ein bisschen hin und her geprutscht und habe mal das zusammengescriptet...

```
public InetAddress findServer() {
		System.out.println("Finde Server starten...");
		byte[] byts = new byte[265];
		System.out.println("Data packet erstellt");
		dp = new DatagramPacket(byts, 256, broadcastIP, getRequestPort());
			System.out.println("Socket starten");
			try {
				socket = new DatagramSocket();
				socket.setSoTimeout(TIMEOUT);
				System.out.println("Packet senden auf Port " + getRequestPort());
				socket.send(dp);
				System.out.println("Packet empfangen");
				socket.receive(dp);
				System.out.println("Packet von " +dp.getAddress().toString());
				System.out.println("Socket schließen");
				socket.close();
				InetAddress serverIP = dp.getAddress();
				return serverIP;
			} catch (SocketException e) {
				e.printStackTrace();
				return null;
			} catch (IOException e) {
				e.printStackTrace();
				return null;
			}
```
Auf einem Server auf einem anderen Computer läuft folgende Schleife:

```
public void run() {
		byte[] byts = new byte[265];
		dp = new DatagramPacket(byts, 256);
		try {
			System.out.println("Erstelle Sockel");
			socket = new DatagramSocket(getRequestPort());
		} catch (SocketException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		while (1 == 1) {
			try {
				System.out.println("Warte auf Packet");
				socket.receive(dp);
				System.out.println("Ermittel IP");
				InetAddress remoteIP = dp.getAddress();
				System.out.println("Packet von: " + remoteIP.toString());
				dp = new DatagramPacket(byts, 256, remoteIP, requestPort);
				System.out.println(dp.getAddress());
				socket.send(dp);
				System.out.println("Antwort gesendet");
				
			} catch (IOException e) {
				e.printStackTrace();
				System.out.println("Fehler beim Senden der Antwort");
			}
		}
	}
```

Der Server bekommt das Packet und kann die Absenderadresse richtig herauslesen,
im gegensatz zur findServer-Methode...
diese wartet auf eine Antwort, und bircht dann mit der Timout ab
aber warum?
was mache ich falsch?

mfg. Dagobert


----------



## tuxedo (13. Jun 2008)

Da der Client nicht explizit auf Pakete lauscht, musst du dem Server sagen, dass er das Paket an den Port schicken soll, von dem das Paket kam.

D.h. du schaust einfach am Server in das empfangene Paket nach dem Port und IP, und schickst das Paket genau dahin zurück.

- Alex

P.S. Deine Logik hat übrigens noch einen Haken:

Was machst du wenn 2 oder mehr Server antworten? Du solltest beim Client noch in einer Schleife für Zeitraum X auf Pakete warten wenn du nicht gleich den erstbesten Server der eben am schnellsten Antwortet benutzen willst.


----------



## Dagobert (13. Jun 2008)

Aber der Server sendet das Packet doch auf dem gleiche Port zurück?!
Der Cleint sowohl auch der Server benutzen den RequestPort, und der ist in beiden fällen der gleiche...oO

und .... Nein da ist kein Hacken...
das ganze System ist so konzepiert, dass es keine zwei Server gibt...
Es ist ja nur ne Methode aus einer Lobby XD


----------



## tuxedo (13. Jun 2008)

Probier aus was ich dir geraten habe und du wirst sehen dass es funzt.

Socketkommunikation hat immer zwei Ports. Den einen dir bekannten Port, auf dem der Server lauscht, und den anderen, von dem die Kommunikation ausgegangen ist. Und das ist der Port, der im Paket drin steht, das du am Server empfängst. 

Und es gibt doch nen Haken:

Was machst du wenn du ein Netzwerk hast, in dem zwei User auf die Idee kommen einen Server zu starten? Klar, das entspricht nicht dem Regelfall (Hast du ja geschrieben). Aber wenn es mal dazu kommt (und technisch ist das eben möglich), dann kriegst du mit deiner Methodik nicht den Server zurück den du haben möchtest, sondern den, der zuerst Antwortet.

Du kannst ja auch nicht nen Button in deine Anwendung einbauen und definieren "Da hat keiner drauf zu klicken". Du musst das so designen dass es technisch erst gar nicht möglich ist, oder dem ganzen einen Fallback-Strategie geben, welche Eintritt wenn "Das passiert wohl vermutlich eh nicht" doch auftritt.

- Alex


----------



## Dagobert (13. Jun 2008)

Wenn zwei Menschen wirklich auf die Idee kommen, sich gleichzeitig (Rechnergeschwindigkeit ausen vor) in die Lobby zu begeben werden sie es ganz schnell bemerken, wenn sie sich gegenseitig nicht sehen, ist aber nur ein Hunderstell unterschied, wird defenitiv kein zweiter Server gestartet, sonder der Client connectet auf den bestehenden Server (Theoretisch, aber daran scheiterts ja momentan XD)


----------



## tuxedo (13. Jun 2008)

Lobby hin oder her, "macht keinen Sinn zwei Server zu starten" hin oder her. Solange es technisch möglich ist in einem Netzwerk zwei Server deines Programm zu betreiben, und Clients sich beim Serversuchen ausschließlich an das erste ankommende Antwort-Datagramm klammern, hat deine Implementierung in sofern einen Haken, als dass der Client nicht zwingend den "richtigen" Server erwischt.

- Alex


----------



## tuxedo (11. Aug 2008)

An die SIMON verwender hier im Thread:

Bin gerade dabei das "Server finden" Feature in SIMON zu integrieren. Anregungen und Wünsche diesbezüglich bitte hier posten:

http://root1.de/node/35

- Alex


----------

