Portal > Foren > Java > Allgemeine Java-Programmierung > Netzwerk / Thread syncronisieren
Antwort
 
Themen-Optionen Thema durchsuchen
Alt 13.11.2005, 00:07 Nach oben    #1
Sesselkleber
 
Benutzerbild von sparrow
 
Registriert seit: 17.01.2005
Beiträge: 576
Standard Netzwerk / Thread syncronisieren

Hallo Forum,

ich hoffe ihr könnt mir auf die Sprünge helfen.
Ich hab das ganze mal unter Netzwerkprogrammierung gepackt, ich denke es passt hier ganz gut rein.
Das ganze wird ein Testcode für ein Spiel.

Also folgendes Szenario:

Ich habe ein Serverprogramm und ein Clientprogramm.
Der Server lauscht auf einen Port, Client verbindet sich darauf. Anfangs gibt es mehrere kleine Verbindungen. Alle über TCP/IP. Die Verbindung wird jeweils dadurch beendet, dass der Server eine Anfrage beantwortet und anschließend den Socket schließt.

Jetzt kommt aber der Hauptteil des Programms, und damit habe ich ein wenig Probleme. Die Verbindung soll nun dauerhaft offen bleiben und die Clients sollen die neuen Informationen der jeweils anderen Clients erhalten.
Wenn der Spieler auf Client1 sich bewegt sollen alle anderen Clients die Information darüber erhalten.
Das ganze muss möglichst Zeitnah erfolgen.

Meine Idee war folgende:
Jeder Spieler hat auf dem Server einen eigenen Thread. Das ist nötig da jegliche Verbindung jederzeit Daten empfangen und senden kann.
Die Threads werden von einer Klasse (Handler) verwaltet.
Bewegt sich einer der Spieler sendet er seine neuen Koordinaten an den Server.
Der Server erkennt anhand des eingehenden Strings was er tun soll (update der Spielerposition), durchsucht alle Spieler und trägt in einen Buffer (ein Vector) eine Zeichenfolge (UpdateSpielerPosition,Spielername,neuX,neuY).
In Regelmäßigen Abständen (sehr oft) fragt der Client bei dem Server an ob neue Daten für ihn vorliegen. Der Server antwortet mit dem Inhalt des Vector-Buffers der für den Spieler gefüllr wurde. Der Client weiß was die Einträge bedeuten und wendet sie auf die Spieler an.

Das funktioniert wunderbar. Um zeitnah zu sein muss der Client aber bei wirklich jeder kleinen Bewegung des Spielers die neue Position an den Server senden.
Ebenfalls muss der Client sehr oft nach Updates fragen.
Zwar wird die Verbindung zwischen Client und Server zwischenzeitlich nicht abgebaut (also die Sockets bleiben dauerhaft offen, es wird lediglich ein Byte gesendet, dass den Ende eines "Befehls" angbit), trotzdem sind das eine Menge Daten die ausgetauscht werden müssen.

Außerdem habe ich ein Problem mit der Synchronisation der Threads auf dem Server. Theoretisch könnte ich auch dauerhaft alle den Client betreffenden Updates einfach an den Client senden anstatt den Client anfragen zu lassen, allerdings könnte das zu Schwierigkeiten kommen wenn der Client nicht mit der Abarbeitung hinterher kommt. Daher die Anfrage.
Wenn im Moment aber ein Thread bevorzugt wird, bekommt er sehr viele Daten, während andere Threads ewig auf Ihre Daten warten müssen. Das kann es meiner Meinung auch nicht sein.

Daher meine Frage ob ihr andere Vorschläge habt, oder ob ihr Ideen für das funktionierende syncen der Threads habt.

Gruß
Sparrow
sparrow ist offline  
Add Post to del.icio.usBookmark Post in TechnoratiDiesen Beitrag zu Mister Wong hinzufügen!
Mit Zitat antworten
Alt 16.11.2005, 12:17 Nach oben    #2
Benutzer
 
Registriert seit: 15.11.2005
Beiträge: 75
Standard

Zitat:
Zitat von sparrow
Jeder Spieler hat auf dem Server einen eigenen Thread. Das ist nötig da jegliche Verbindung jederzeit Daten empfangen und senden kann.
Die Threads werden von einer Klasse (Handler) verwaltet.
Es ist ohnehin sinnvoll, bei einem Server für jede Connection einen eigenen Thread zu starten, um sofort wieder bereits für neue Connections zu sein.


Zitat:
Außerdem habe ich ein Problem mit der Synchronisation der Threads auf dem Server. Theoretisch könnte ich auch dauerhaft alle den Client betreffenden Updates einfach an den Client senden anstatt den Client anfragen zu lassen, allerdings könnte das zu Schwierigkeiten kommen wenn der Client nicht mit der Abarbeitung hinterher kommt. Daher die Anfrage.
Wenn im Moment aber ein Thread bevorzugt wird, bekommt er sehr viele Daten, während andere Threads ewig auf Ihre Daten warten müssen. Das kann es meiner Meinung auch nicht sein.
Ich würde tatsächlich den Server die Daten direkt an den Client schicken lassen (also auf das regelmäßige Abfragen (Polling) durch den Client verzichten). Sollte der Client wirklich nicht mit der Verarbeitung nachkommen, kann man immer noch Client- und/oder Server-seitig eine Art Event-Queue zwischenschalten (also noch einen Thread, der Daten schnell entgegennimmt und sequenziell ausgibt). Ich glaube aber nicht, dass es da Probleme gibt: normalerweise müssten den Daten wesentlich schneller verarbeitet werden können als dass sie über das Nett verschickt werden).

Das Prinzip könnte so aussehen:

Server:
Code:
import java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Server {

	class Handler implements Runnable {
	
		protected BufferedReader Rdr;
		protected OutputStream Out;
		protected int Val;
		protected String CliID;
		
		public Handler( Socket cs) throws IOException {
		
			//--- create a Reader that can be used to read data from the client 
			Rdr = new BufferedReader( new InputStreamReader( cs.getInputStream()));
			
			//--- create an OutputStream that can be used to send data to the client
			Out = cs.getOutputStream();
			
			//--- spawn a thread for client-input-handling
			new Thread( this).start();
			
			//--- register ourselves in Server's list
			Handlers.add( this);
		}
		
		public void run() {
		
			while ( true) {
				try {
		 		//--- read data from this Handler's client and pass it to all connected clients
					String cmd = Rdr.readLine();
		 		System.out.println( this + ".run(:( cmd = " + cmd);
					propagate( cmd);
				} catch( Exception e) {
		 		//--- simple error-handling: just disconnect ;-)
					e.printStackTrace();
					Handlers.remove( this);
					break;
				}
			}
		}
		
		public void send( String data) throws IOException {
			//--- send the passed command to this Handler's client
			Out.write( (data + "\r\n").getBytes());
			Out.flush();
		}
		
	}
	
	protected int Port;
	protected ServerSocket Sock;
	protected List<Handler> Handlers = new ArrayList<Handler>();
	
	public Server( final int port) {
		Port = port;
	}

	public void start() throws IOException {
	
		//--- create a socket and listen for connections
		Sock = new ServerSocket( Port);
		while ( true) {
			//--- acceptConnection is supposed to spawn a new thread
			acceptConnection();
		}
	}
	
	protected void acceptConnection() throws IOException {
	
		//--- wait for request (this one blocks until a new client connects)
		Socket cs = Sock.accept();
		
		//--- handle in a different thread to avoid  server-blocking
		new Handler( cs);
	}
	
	protected void propagate( String cmd) throws IOException {
	
		//--- send the passed command to all connected clients
		Iterator<Handler> handlers = Handlers.iterator();
		while ( handlers.hasNext()) {
			handlers.next().send( cmd);
		}
	}
	
	public static void main( String[] args) {
	
		try {
			Server srv = new Server( 8100);
			srv.start();
		} catch ( Exception e) {
			e.printStackTrace();
		}
	
	}
}
Client:
Code:
import java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Client implements Runnable {

	protected String ID;
	protected String Host;
	protected int Port;
	protected Socket Sock;
	protected int Val;
	protected BufferedReader Rdr;

	public Client( final String host, final int port) {
	
		//--- Server's address
		Host = host;
		Port = port;
	
		//--- a unique id 
		ID = "Cli" + System.currentTimeMillis();
		
		//--- application data (just a dummy for real-world data)
		Val = 0;
	}
	
	public void connect() throws IOException {
	
		//--- establish a connection to our server
		Sock = new Socket( Host, Port);
		
		//--- create a  Reader that can be used to read data from the server
		Rdr = new BufferedReader( new InputStreamReader( Sock.getInputStream()));

		//--- spawn a thread for server-message-handling
		(new Thread( this)).start();
	
	}
	
	protected void handleInput( String cmd) {
	
		//--- this one is not much more than a dummy: dispatch user-input and recalculate application-data
		if ( "+".equals( cmd)) {
			Val++;
			sync();
		} else if ( "-".equals( cmd)) {
			Val--;
			sync();
		} else if ( cmd != null) {
			System.out.println( "?? " + cmd + " ??");
		}
	}
	
	public void run() {
	
		while ( true) {
			try {
				//--- read a message from the server
				String cmd = Rdr.readLine();
				System.out.println( "Server-Message: " + cmd);
				
			} catch ( Exception e) {
				e.printStackTrace();
				break;
			}
		}
	}
	protected void sync() {
	
		//--- send application-data to our server
		try {
		 Sock.getOutputStream().write( ("ID=" + ID + ",VAL="+Val + "\r\n").getBytes());
			Sock.getOutputStream().flush();
		} catch ( IOException iox) {
			iox.printStackTrace();
		}
	}
	
	public static void main( String[] args) {
	
		try {
			Client cli = new Client( "localhost", 8100);
			cli.connect();
			
			BufferedReader rd = new BufferedReader( new InputStreamReader( System.in));
			System.out.println( "Connected.");
		 System.out.println( "Type <+>+<Enter> or <->+<Enter> to send data to the server");
			while ( true) {
				cli.handleInput( rd.readLine());
			}
		} catch ( Exception e) {
			e.printStackTrace();
		}
	
	}
}
Murray ist offline  
Add Post to del.icio.usBookmark Post in TechnoratiDiesen Beitrag zu Mister Wong hinzufügen!
Mit Zitat antworten
Alt 16.11.2005, 19:45 Nach oben    #3
Sesselkleber
 
Benutzerbild von sparrow
 
Registriert seit: 17.01.2005
Beiträge: 576
Standard

Ok, dann bin ich ja gar nicht so falsch mit dem Code den ich hier habe.
Der sieht nämlich recht ähnlich aus.

Gut, wahrscheinlich hast du Recht und wir gehen davon aus, dass der Client entsprechend schnell die Daten verarbeitet. Also lassen wir ihn nicht mehr anfragen.

Eine andere Sache ist, dass der Client recht häuftig die updates vom Server erhalten muß.
Nach möglichkeit 30 mal pro sekunde, das wäre nämlich die Framerate in der das Spiel läuft.
Wenn ein Spieler geht, dann soll man ja sehen wie er sich bewegt. Da er ja jederzeit die Richtiung wechseln kann muß ich den anderen Clients (zumindest die auf der selben Map sind) die neuen Koodinaten mit jedem neuen Frame mitschicken.
Ist das raelistisch, oder sollte man eine andere Möglichkeit finden?

Gruß
Sparrow
sparrow ist offline  
Add Post to del.icio.usBookmark Post in TechnoratiDiesen Beitrag zu Mister Wong hinzufügen!
Mit Zitat antworten
Alt 16.11.2005, 20:31 Nach oben    #4
Benutzer
 
Registriert seit: 15.11.2005
Beiträge: 75
Standard

Vermutlich muss es wohl gewäherleistet sein, dass alle Spiele die Bewegungen gleichzeitig sehen; es geht also eigentlich nicht, dass man sich auf seinem eigenen Rechner bewegt und diese Bewegung dann lediglich an den Server meldet, der diese dann an alle anderen Clients propagiert; vielmehr muss die lediglich dieAnforderung, sich zu bewegen, an den Server geschickt werden. Der Server berechnet dann die neue Position und schickt diese dann quasi gleichzeitig an alle Clients.

Ob man allerdings 30 Updates pro Sekunde erreichen kann, halte ich noch für fraglich. Dazu muss der Server nicht nur einige Rechenpower besitzen, sondern auch eine gewisse Bandbreite bes. im Upstream haben (er multipliziert ja den Traffic: ein Update eines Clients führt zu einem Update an alle Clients).
Murray ist offline  
Add Post to del.icio.usBookmark Post in TechnoratiDiesen Beitrag zu Mister Wong hinzufügen!
Mit Zitat antworten
Alt 16.11.2005, 21:11 Nach oben    #5
Benutzer
 
Registriert seit: 15.11.2005
Beiträge: 75
Standard

Noch eine Idee: wenn man es nicht schafft, die Updates mit der Frequenz der Framerate zu verteilen, dann könnte man dem Client etwas mehr Arbeit überlassen: die Spieler bekommen vom Server bei jedem Update nicht nur eine Position, sondern auch einen Vektor (also Richtung und Geschwindigkeit der Bewegung), mit dem sie dann beliebig viele Zwischenschritte bis zum nächsten Update interpolieren können. Das dient aber nur der flüssigeren Darstellung; insbesondere sind in der zeit zwischen zwei Updates keine Richtungsänderungen möglich. Daher kann eine eventuelle Collision-Detection trotzdem (vorab) auf dem Server erfolgen.
Murray ist offline  
Add Post to del.icio.usBookmark Post in TechnoratiDiesen Beitrag zu Mister Wong hinzufügen!
Mit Zitat antworten
Antwort

Lesezeichen


Aktive Benutzer in diesem Thema: 1 (Registrierte Benutzer: 0, Gäste: 1)
 
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche

Forumregeln
Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Ähnliche Themen
Thema Autor Forum Antworten Letzter Beitrag
Thread mehrfach starten jack77 Desktop-Applikationen und Grafik 18 07.03.2007 17:44
Anzeige, wann ein Thread erstellt wurde hinzugefügt Ben Archiv 5 25.01.2006 22:55
Problem bei der Einbindung eines Applets in HTML exoskelett Desktop-Applikationen und Grafik 13 24.01.2006 18:45
Thread in Applet wird nicht gestartet? Ben Desktop-Applikationen und Grafik 2 26.10.2005 20:33
Problem bei Thread für mp3 player Eddi Allgemeine Java-Programmierung 6 30.01.2005 16:18


Alle Zeitangaben in WEZ +2. Es ist jetzt 14:39 Uhr.


Powered by vBulletin® Version 3.7.3 (Deutsch)
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.2.0

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44