Problem mit vielen Bytes über Socket

xasz

Mitglied
Hallo Leute,

ich mache schon einige Tage an einem kleinen Problem mit Java Sockets rum.
Ich habe ein kleines Spiel geschrieben und möchte um Erfahrung zu sammeln mal ein bisschen mit Netzwerkprogrammierung arbeiten.

Ich habe mir ein kleines Protokollausgedacht, das mit Bytefolgen arbeitet.
Sprich mein Client bekommt ein bestimmtes byte was zum Beispiel für ein Bewegungsbefehl steht. Mein Client weis nun ok jetzt kommen X Bytes die den Bewegungsbefehl repräsentieren und liest die nächste X Bytes ein. Dann kommt das nächste Byte was das nächsten Befehl angibt usw.

Mein Problem ist, dass bei mir alles einwandfrei lokal funktioniert. Sprich wenn ich auf meinem Computer ein Spiel hoste und mit einer zweiten Instanz joine läuft alles perfekt und wunderbar, also über den lokalen TCP Stack.

Jetzt wollte ich die Sache einem Freund zeigen und schicke Ihm einen Client. Portforwardings etc sind gemacht. Ich starte meinen Server und mein Freund kann darauf Joinen und es läuft auch erstmal gut.

Wenn ich in meinem Beispiel z.b. 10 Einheiten erstelle und nur wenig Befehle übertragen werden, dann läuft alles gut und auch über längere zeit stabil. Wenn ich jetzt allerdings 1000 Einheiten habe, dann fängt es an zu laggen. Womit ich auch kein Problem habe ( Dient nur als Stresstest). Allerdings wenn ich jetzt z.b. 2000 Einheiten erstelle dann lagt es stark und der Client Crashed, Weil er eine Falsche Bytefolge einliest, bzw eine ungültige. Also es kommen einfach falsche Bits an, bzw es Fehlen welche. Da der Client z.b. einen Befehl Bewegung bekommt und dann soll er 12 Byte lesen und dann kommt das nächste Kontrollbit. Jetzt kommen allerdings nur z.b. 10 und dann das nächste Kontrollbit, womit mein Client natürlich das falsche Byte als Befehlsbyte liest und crashed.


Mir ist nicht ganz klar warum das passiert und was ich dagegen tun kann.
Das sind gerade meine Funktionen die zum Senden und Empfangen benutze:

Server:
Java:
	public void send(byte[] bytes) throws IOException {
		if(this.socket.isClosed()){
			this.stopListen();
			this.gameServer.removeClientHandler(this);
			return;
		}
		this.outputStream.write(bytes);
		this.outputStream.flush();
	}
	public void read(byte[] bytes){
		try {
			this.inputStream.read(bytes);
		} catch (IOException e) {					
			this.gameServer.removeClientHandler(this);
			this.stopListen();
			e.printStackTrace();
		}
	}

Client:
Java:
	public void send(byte[] bytes) throws IOException {
		this.outputStream.write(bytes);
		this.outputStream.flush();
	}
	public void read(byte[] bytes){
		try {
			this.inputStream.read(bytes);
			System.out.println("Recieve:"+NetworkConnector.bytesToString(bytes));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

Über jede Idee oder Hilfe wäre ich sehr dankbar.

Bisher bin ich davon ausgegangen, dass die Java-Sockets TCP Benutzen und hier dann die Sichere Übertragung gewährleistet werden sein sollte.

Danke euch, xasz
 
Zuletzt bearbeitet:

Tobse

Top Contributor
Das liegt daran, dass bei 20-30 einheiten die Menge an Bytes pro Sekunde eure Bandbreite/Rechengeschwindigkeit noch nicht überschreitet. Sprich: Es kommen weniger Aufrufe an die write-Methode als pro Sekunde übertagen werden könnten.
Wenn es jetzt aber sehr viele Threads sind, kommen die Befehle mit Zeitlicher überschneidung:
Code:
-------------------------------------------------->Zeit    Ausgabe an der Gegenseite:
Wenige Einheiten:                                          
Thread 1:   11 11 11 11                      11 11 11 11   11 11 11 11 22 22 22 22 11 11 11 11
Thread 2:                     22 22 22 22
Viele Einheiten:
Thread 1:   11 11 11 11                      11 11 11 11   11 11 44 11 33 44 11 33 33 44 44 33
Thread 2:                     22 22 22 22                  22 22 22 22 33 44 33 33 11 33 44 11
Thread 3:         33 33 33 33          33 33 33 33         11 44 44 11
Thread 4:      44 44 44 44                44 44 44 44

Du siehst, dass die zusammengehörenden Bytefolgen im Zweiten Beispiel nichtmehr zusammenhängend ankommen.
Die Abhilfe ist synchronisation des sendens der bytes. Das heisst: Es wird immer nur von einem Thread gleichzeitig gesendet. Und erst wenn der fertig ist, kommt der nächste. Siehe Java Keyword synchronized.

Und gleich als Tipp wie du das gescheit umsetzen kannst:
Java:
class ProtocolCommand
{
    private byte nummer;
    private byte[] argumente;
    
    /** getter und setter **/
}
class Protocol
{
    public synchronized sendCommand(ProtocolCommand cmd)
    {
        out.write(cmd.getNummer());
        out.write(cmd.getArgumente());
    }
    public sendMoveCommand(Einheit e, Position neuePosition)
    {
        sendCommand(new PositionsÄnderungsCommand(e, neuePosition));
    }
}
Es wird also immer nur eine zusammengehörende Kette an Bytes gleichzeitig gesendet. Das Ergebnis auf das Diagramm von oben wäre:
Code:
-------------------------------------------------->Zeit    Ausgabe an der Gegenseite:
Viele Einheiten:
Thread 1:   11 11 11 11                      11 11 11 11   11 11 11 11 44 44 44 44 33 33 33 33
Thread 2:                     22 22 22 22                  22 22 22 22 33 33 33 33 44 44 44 44
Thread 3:         33 33 33 33          33 33 33 33         11 11 11 11
Thread 4:      44 44 44 44                44 44 44 44

Jetzt wird aber natürlich der Befehl nichtmehr direkt an dem Zeitpunk übertragen wo er beim Client aufgetreten ist. D.h. du solltest bei größeren Datenmengen den Zeitpunkt mitschicken damit alle Clients zeitlich synchron sind.
 
Zuletzt bearbeitet:

xasz

Mitglied
Hallo und schonmal vielen Dank für deine ausführliche Antwort,

ich habs jetzt mit dem syncronized noch nicht ausprobiert. Werd ich heute nicht mehr zu kommen, werd mir das erstmal genau durchlesen, was das Schlüsselwort macht.

Allerdings weis ich nicht, ob das mein Problem ist, weil ich hab eigentlich nur ein Thread der die Netzerkommunikation macht. Bzw habe ich den "GameThread" und den "NetworkThread" auf Client als auch Serverseite.
 

Tobse

Top Contributor
Hallo und schonmal vielen Dank für deine ausführliche Antwort,

ich habs jetzt mit dem syncronized noch nicht ausprobiert. Werd ich heute nicht mehr zu kommen, werd mir das erstmal genau durchlesen, was das Schlüsselwort macht.

Allerdings weis ich nicht, ob das mein Problem ist, weil ich hab eigentlich nur ein Thread der die Netzerkommunikation macht. Bzw habe ich den "GameThread" und den "NetworkThread" auf Client als auch Serverseite.

Wenn du das Spiel nicht komplett mit einer Hauptschleife implementiert hast (was ich hoffen will) macht das keinen Unterschied. Die Threads aus dem Spiel rufen eine Methode auf der Netzwerk-Klasse auf. Und sobald das zu viele sind gibts eben das Chaos wie oben beschrieben.
Wenn die Anzahl der Threads nicht die Ursache ist, weiss ich absolut nicht, wo sonst das Problem liegen könnte.
 

xasz

Mitglied
So nochmal ich.
Ich hab das jetzt mal getestet und irgenwie bin ich dem Problem noch nicht aus dem Weg gekommen.

Habe erst nur die Send Funktion syncronized gemacht und das Problem war noch da.
Dann hab ich auch die Read Funktion syncronized gemacht, dann hat irgendwie nicht mehr wirklich was funktioniert.

Nach meinem Verständnis müsste ich auch nur die Send Funktion syncronized machen.

Noch irgendne idee ?
 

Tobse

Top Contributor
So nochmal ich.
Ich hab das jetzt mal getestet und irgenwie bin ich dem Problem noch nicht aus dem Weg gekommen.

Habe erst nur die Send Funktion syncronized gemacht und das Problem war noch da.
Dann hab ich auch die Read Funktion syncronized gemacht, dann hat irgendwie nicht mehr wirklich was funktioniert.

Nach meinem Verständnis müsste ich auch nur die Send Funktion syncronized machen.

Noch irgendne idee ?

Kommt darauf an, ob die zusammengehörenden Pakete mit einem einzigen Aufruf an send() übertragen werden. Wenn es mehrere sind bringt dir das synchronized nichts.
 

Tobse

Top Contributor
Hm, dann sollte das eigentlich klappen. Wenn es das nicht ist kann ich mir nurnoch vorstellen dass die Computer mit der schieren Menge an Einheiten nichtmehr klar kommen. Aber dann bin ich mit meinem Latein auch am Ende, sorry.
 

xasz

Mitglied
Ich hab für mich nun beschlossen, ich mach nen neuen Branch auf meim svn aus und probier das mal mit java nio2.
Vielleicht tut nen kleiner Rewrite zu dieser Zeit des Entwicklungsstadiums nicht so weh, wie später.
Trotzdem danke.

Werde mich melden, falls ich noch was rausfinde.

Edit: Leider find ich keine wirklich guten Quellen zum lernen von java socketchannel etc.
Überall finde ich nur so Sachen .. so gehts.. Codebeispiel einfügen .. fertig. Aber ohne wirkliche Erklärung.
Hat da jemand nen guten Tipp ? Wäre Dankbar.
 
Zuletzt bearbeitet:

Barista

Top Contributor
Ich hab für mich nun beschlossen, ich mach nen neuen Branch auf meim svn aus und probier das mal mit java nio2.

Das löst nicht das ursprüngliche Problem.

Ich würde mir die gesendeten und empfangenen Daten in jeweils eine Log-Datei schreiben und diese dann prüfen.

Eventuell würde ich das Protokoll mit Prüfsummen absichern und bei einem Fehler eine Exception werfen, damit sich der Fehler nicht ausbreitet(Folgefehler).
 
Zuletzt bearbeitet:

xasz

Mitglied
Ok,

ich hab mich doch dazu entschieden mal beim alten Modell zu bleiben. Egal ob ich das später nochmal rewrite. Ich möchte das auch des Wissens-Willen hinbekommen.

Ich habe meine Send Funktion am Server mal so umgebaut:

Java:
	public synchronized void send(byte[] bytes) throws IOException {
		if(this.isSending)System.out.println("Das sollte nicht sein");
		this.isSending = true;
		if(this.socket.isClosed()){
			this.stopListen();
			this.gameServer.removeClientHandler(this);
			return;
		}
		synchronized(this.outputStream){
			this.outputStream.write(bytes);
			this.outputStream.flush();
		}
		this.isSending = false;
	}

Das Problem ist, dass ich das so verstehe, dass durch das syncronized die Funktion nicht zur gleichen Zeit aufgerufen werden kann, sondern gequed wird, bis die andere fertig ist.

Mein Problem ist, dass "Das sollte nicht sein" trotzdem ausgegeben wird, und damit der Fehler leider immernoch da ist.

Noch irgendne Idee ?

Interessanter weise, crashed mein Client aber auch manchmal, ohne das am Server die Meldung kommt und somit ja quasi keine bytes am server durcheinander versendet werden, aber am Client in falscher Reihenfolge ankommen.

Ich setz hier mal noch ein bisschen Quellcode an:

Das hier ist mein Client:

Java:
package de.xasz.ParkTycoon.Controller.Networking;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Vector3;

import de.xasz.ParkTycoon.GameObject;
import de.xasz.ParkTycoon.Controller.Entity.Entity;
import de.xasz.ParkTycoon.Controller.Level.ClientLevel;
import de.xasz.ParkTycoon.Controller.Level.Level;
import de.xasz.ParkTycoon.Controller.Level.Tiles.TerrainTile;
import de.xasz.ParkTycoon.Controller.Level.Tiles.Tile;
import de.xasz.ParkTycoon.Controller.Ways.Way;
import de.xasz.ParkTycoon.Controller.Ways.WayIsAlreadyInListException;
import de.xasz.ParkTycoon.Utill.IntVector3;

public class GameClient extends Thread {
	protected Socket socket;
	protected int serverPort;
	protected String host;
	protected BufferedInputStream inputStream;
	protected BufferedOutputStream outputStream;
	protected boolean isListening = false;
	protected GameObject gameObject;
	public GameClient(String host, int port, GameObject gameObject) {
		this.serverPort = port;
		this.host = host;
		this.gameObject = gameObject;
	}

	public void connect() throws UnknownHostException, IOException {
		this.socket = new Socket(host, serverPort);
		this.inputStream = new BufferedInputStream(this.socket.getInputStream());
		this.outputStream = new BufferedOutputStream(
				this.socket.getOutputStream());
	}

	@Override
	public void run() {
		this.isListening = true;
		while (this.isListening)
			listen();
	}

	public void stopListen() {
		this.isListening = false;
	}

	public void listen() {
		byte[] code = new byte[1];
		byte[] bytes = null;
		if (socket.isClosed()) {
			this.stopListen();
			Gdx.app.exit();
		}
		read(code);
		switch (code[0]) {
		case NetworkActionCode.GAME_PAUSE:
			this.gameObject.pauseGame();
			break;
		case NetworkActionCode.GAME_UNPAUSE:
			this.gameObject.unpauseGame();
			break;
		case NetworkActionCode.TERRAINTILE_CREATE:
			bytes = new byte[16];
			read(bytes);
			((ClientLevel)this.gameObject.getLevel())
					.remoteAddTile(NetworkParser.parseCreateTileEvent(bytes));
			break;
		case NetworkActionCode.TERRAINTILE_REMOVE:
			bytes = new byte[12];
			read(bytes);
			((ClientLevel)this.gameObject.getLevel())
					.remoteRemoveTile(NetworkParser.parseRemoveTileEvent(bytes));
			break;
		case NetworkActionCode.ENTITY_REMOVE:
			bytes = new byte[4];
			read(bytes);
			((ClientLevel)this.gameObject.getLevel())
					.remoteRemoveEntity(NetworkParser.parseRemoveEntityEvent(bytes));
			break;
		case NetworkActionCode.ENTITY_ADD:
			bytes = new byte[24 + 4 + 4];
			read(bytes);
			((ClientLevel)this.gameObject.getLevel())
					.remoteAddEntity(NetworkParser.parseAddEntityEvent(bytes));
			break;
		case NetworkActionCode.ENTITY_MOVE:
			bytes = new byte[24 + 4];
			read(bytes);
			NetworkParser.parseEntityMoveEvent(bytes, this.gameObject.getLevel());
			break;
		case NetworkActionCode.WAY_REMOVE:
			bytes = new byte[12];
			read(bytes);
			((ClientLevel)this.gameObject.getLevel())
					.remoteRemoveWay(NetworkParser.parseRemoveWayEvent(bytes));
			break;
		case NetworkActionCode.WAY_ADD:
			bytes = new byte[12];
			read(bytes);
			((ClientLevel)this.gameObject.getLevel())
					.remoteAddWay(NetworkParser.parseAddWayEvent(bytes));
			break;
			case NetworkActionCode.LEVEL_DIMENSION:
				this.gameObject.getLevel().createNewEmptyLevel();
			break;
		}
	}

	public synchronized void send(byte[] bytes) throws IOException {
		this.outputStream.write(bytes);
		try{
			this.outputStream.flush();
		}catch(Exception e){
			System.out.println(e.getLocalizedMessage());
		}
	}

	public void read(byte[] bytes) {
		try {
			this.inputStream.read(bytes);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void closeStockets() {
		try {
			this.inputStream.close();
			this.outputStream.close();
			this.socket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void loadLevel() {
		try {
			send(new byte[] { NetworkActionCode.LEVEL_FULL_SYNC });
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void sendCreateTileEvent(Tile tile){
		byte[] bytes = NetworkParser.packCreateTileEvent(tile);
		try {
			this.send(bytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void sendRemoveTileEvent(IntVector3 vec){
		byte[] bytes = NetworkParser.packRemoveTileEvent(vec);
		try {
			this.send(bytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void sendAddWayEvent(Way way){
		byte[] bytes = NetworkParser.packAddWayEvent(way);
		try {
			this.send(bytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void sendRemoveWayEvent(IntVector3 vec){
		byte[] bytes = NetworkParser.packRemoveWayEvent(vec);
		try {
			this.send(bytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void sendAddEntityEvent(Entity ent){
		byte[] bytes = NetworkParser.packAddEntityEvent(ent);
		try {
			this.send(bytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void sendRemoveEntityEvent(int entID){
		byte[] bytes = NetworkParser.packRemoveEntityEvent(entID);
		try {
			this.send(bytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void sendFullSync() {
		try {
			send(new byte[]{NetworkActionCode.LEVEL_FULL_SYNC});
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

Das hier ist mein Clienthandler am Server. Für jeden Client läuft am Server ein Clienthandler als extra Thread:
Java:
package de.xasz.ParkTycoon.Controller.Networking;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;

import com.badlogic.gdx.math.Vector3;

import de.xasz.ParkTycoon.Controller.Entity.Entity;
import de.xasz.ParkTycoon.Controller.Level.Level;
import de.xasz.ParkTycoon.Controller.Level.ServerLevel;
import de.xasz.ParkTycoon.Controller.Level.Tiles.TerrainTile;
import de.xasz.ParkTycoon.Controller.Level.Tiles.Tile;
import de.xasz.ParkTycoon.Controller.Ways.Way;
import de.xasz.ParkTycoon.Controller.Ways.WayIsAlreadyInListException;
import de.xasz.ParkTycoon.Utill.IntVector3;

public class ClientHandler extends Thread{
	protected NetworkServer gameServer;
	protected Socket socket;
	protected BufferedInputStream inputStream;
	protected BufferedOutputStream outputStream;
	protected boolean isListening = false;
	protected boolean isSending = false;
	public ClientHandler(NetworkServer gs, Socket socket) throws IOException{
		this.gameServer = gs;
		this.socket = socket;
		this.inputStream = new BufferedInputStream(this.socket.getInputStream());
		this.outputStream = new BufferedOutputStream(this.socket.getOutputStream());
	}
	@Override
	public void run(){
		this.isListening = true;
		while(this.isListening) listen();
	}
	public void stopListen(){
		this.isListening = false;
	}
	public void listen(){
			byte[] bytes = new byte[0];
			byte[] code = new byte[1];
			try {	
				read(code);
				switch(code[0]){
				case NetworkActionCode.GAME_PAUSE:
					this.gameServer.getGameObject().pauseGame();
					this.gameServer.send(bytes);
				break;
				case NetworkActionCode.GAME_UNPAUSE:
					this.gameServer.getGameObject().unpauseGame();
					this.gameServer.send(bytes);
				break;
				case NetworkActionCode.LEVEL_FULL_SYNC:
					this.gameServer.getGameObject().pauseGame();
					this.gameServer.send(new byte[]{NetworkActionCode.GAME_PAUSE});
					while(!this.gameServer.getGameObject().canSync());
					this.gameServer.send(new byte[]{NetworkActionCode.LEVEL_DIMENSION});
					
					Level level = this.gameServer.getGameObject().getLevel();
					for(int x = 0; x < level.LEVEL_X_SIZE;x++){
						for(int y = 0; y < level.LEVEL_Y_SIZE;y++){
							for(int z = 0; z < level.LEVEL_Z_SIZE;z++){
								Tile tile = level.getTile(x, y, z);
								if(tile != null){
									this.send(NetworkParser.packCreateTileEvent(tile));
								}
							}	
						}	
					}
					for(Way way : level.getWayManager().getWays()){
						this.send(NetworkParser.packAddWayEvent(way));
					}
					for(Entity ent: level.getEntities()){
						this.send(NetworkParser.packAddEntityEvent(ent));
					}
					this.gameServer.getGameObject().unpauseGame();
					this.gameServer.send(new byte[]{NetworkActionCode.GAME_UNPAUSE});
				break;
					case NetworkActionCode.TERRAINTILE_CREATE:
						bytes = new byte[16];
						read(bytes);
						this.gameServer.getGameObject().getLevel().addTile(NetworkParser.parseCreateTileEvent(bytes));
					break;
					case NetworkActionCode.TERRAINTILE_REMOVE:
						bytes = new byte[12];
						read(bytes);
						this.gameServer.getGameObject().getLevel().removeTile(NetworkParser.parseRemoveTileEvent(bytes));
					break;
					case NetworkActionCode.ENTITY_REMOVE:
						bytes = new byte[4];
						read(bytes);
						this.gameServer.getGameObject().getLevel().removeEntity(NetworkParser.parseRemoveEntityEvent(bytes));
					break;
					case NetworkActionCode.ENTITY_ADD:
						bytes = new byte[24+4+4];
						read(bytes);
						this.gameServer.getGameObject().getLevel().addEntity(NetworkParser.parseAddEntityEvent(bytes));
					break;
					case NetworkActionCode.ENTITY_MOVE:
						bytes = new byte[24+4];
						read(bytes);
						NetworkParser.parseEntityMoveEvent(bytes,this.gameServer.getGameObject().getLevel());
						
					break;
					case NetworkActionCode.WAY_REMOVE:
						bytes = new byte[12];
						read(bytes);
						this.gameServer.getGameObject().getLevel().removeWay(NetworkParser.parseRemoveWayEvent(bytes));
					break;
					case NetworkActionCode.WAY_ADD:
						bytes = new byte[12];
						read(bytes);
						this.gameServer.getGameObject().getLevel().addWay(NetworkParser.parseAddWayEvent(bytes));
					break;
				}
			} catch (SocketException e) {
				this.gameServer.removeClientHandler(this);
				this.stopListen();
				return;
			} catch (IOException e) {
				e.printStackTrace();
			}
	}
	public void closeStockets() {
		try {
			this.inputStream.close();
			this.outputStream.close();
			this.socket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}	
	public synchronized void send(byte[] bytes) throws IOException {
		if(this.isSending)System.out.println("Das sollte nicht sein");
		this.isSending = true;
		if(this.socket.isClosed()){
			this.stopListen();
			this.gameServer.removeClientHandler(this);
			return;
		}
		synchronized(this.outputStream){
			this.outputStream.write(bytes);
			this.outputStream.flush();
		}
		this.isSending = false;
	}
	public void read(byte[] bytes){
		try {
			this.inputStream.read(bytes);
		} catch (IOException e) {					
			this.gameServer.removeClientHandler(this);
			this.stopListen();
			e.printStackTrace();
		}
	}
}


Das hier ist mein Networkserver, der die Clienthandlers managed:

Java:
package de.xasz.ParkTycoon.Controller.Networking;


import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import de.xasz.ParkTycoon.GameObject;
import de.xasz.ParkTycoon.Controller.Entity.Entity;
import de.xasz.ParkTycoon.Controller.Level.Tiles.Tile;
import de.xasz.ParkTycoon.Controller.Ways.Way;
import de.xasz.ParkTycoon.Utill.IntVector3;


public class NetworkServer extends Thread{
	protected int port;
	protected ClientAccepter clientAccepter;
	protected List<ClientHandler> clientHandler;
	protected GameObject gameObject;
	public NetworkServer(int port, GameObject gameObject){
		this.port = port;
		this.clientHandler = new ArrayList<ClientHandler>();
		this.clientAccepter = new ClientAccepter(this);
		this.gameObject = gameObject;
	}
	public GameObject getGameObject(){
		return this.gameObject;
	}
	public void stopListen(){
		this.clientAccepter.stopListen();
		for(ClientHandler ch : this.clientHandler){
			ch.stopListen();
		}
	}
	@Override
	public void start(){
		this.clientAccepter.start();
		super.start();
	}
	public synchronized void send(byte[] bytes) throws IOException {
		for(ClientHandler ch : this.clientHandler){
			ch.send(bytes);
		}
	}
	public void addClientHandler(ClientHandler newClient){
		synchronized( this.clientHandler){
			this.clientHandler.add(newClient);
			newClient.start();
		}
	}
	public void removeClientHandler(ClientHandler deleteClient){
		synchronized( this.clientHandler){
			this.clientHandler.remove(deleteClient);
		}
	}
	public int getPort(){
		return this.port;
	}

	public int getClientCount(){
		return this.clientHandler.size();
	}
	
	public void closeStockets() {
		this.clientAccepter.stopListen();
		this.clientAccepter.closeSockets();
		try {
			this.clientAccepter.join();
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		for(ClientHandler ch : this.clientHandler){
			ch.stopListen();
			try {
				ch.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	public void sendCreateTileEvent(Tile tile){
		byte[] bytes = NetworkParser.packCreateTileEvent(tile);
		try {
			this.send(bytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void sendRemoveTileEvent(IntVector3 vec){
		byte[] bytes = NetworkParser.packRemoveTileEvent(vec);
		try {
			this.send(bytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void sendAddWayEvent(Way way){
		byte[] bytes = NetworkParser.packAddWayEvent(way);
		try {
			this.send(bytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void sendRemoveWayEvent(IntVector3 vec){
		byte[] bytes = NetworkParser.packRemoveWayEvent(vec);
		try {
			this.send(bytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void sendAddEntityEvent(Entity ent){
		byte[] bytes = NetworkParser.packAddEntityEvent(ent);
		try {
			this.send(bytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void sendRemoveEntityEvent(int entID){
		byte[] bytes = NetworkParser.packRemoveEntityEvent(entID);
		try {
			this.send(bytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void sendEntityMoveEvent(Entity ent){
		byte[] bytes = NetworkParser.packEntityChangeDestinationEvent(ent);
		try {
			this.send(bytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
Ich hoffe, dass mir jemand helfen kann, weil ich nun echt schon ewig hier hänge.
 
Zuletzt bearbeitet:

Tobse

Top Contributor
Das
Code:
synchronized (this.outputstream)
sollte eigentlich schon reichen. Nun kann es aber sein, dass ein Socket mehrere OutputStream-Instanzen herausgibt. Ich habe jetzt mal meine Server-Codes ausgegraben die anfangs ein änliches Problem hatten. Dort ist es auf Client-Ebene so gelöst:

Java:
abstract class ServerProtocol
{
    private static Socket currentSocket;
    public static void doSomething() throws ProtocolException
    {
        synchronized (currentSocket)
        {
            out.write(...);
        }
    }
}

Auf dem Server wird auch für jeden Client ein neuer Thread gestartet der so ausschaut:
Java:
class UserHandler implements Runnable
{
    public void run()
    {
        try
        {
            while (!socket.isClosed())
            {
                byte opCode = in.read();
                switch (opCode)
                {
                    case OC_LOGIN:
                        doLogin();
                        break;
                    ...
                }
            }
        }
        catch (Excepiton ex) { ... }
    }
    private void doLogin() throws IOException
    {
        in.read(...);
    }
}

Alles was ich weiss ist, dass es funktioniert... Wenn ein
Code:
synchronized(socket)
bei dir auch nicht zieht würde sich mir die Frage stellen auf was für einer Maschine das läuft während es crashed. Wenn das eine Shared-VM ist würds mich nicht wundern...
 

Barista

Top Contributor
Sorge einfach mal dafür, dass Du genau ein Socket und genau einen Stream hast.

Auf diesen kannst Du synchronisieren.

Shared-VM? Kenn ich nicht.
 

Tobse

Top Contributor
Sorge einfach mal dafür, dass Du genau ein Socket und genau einen Stream hast.

Auf diesen kannst Du synchronisieren.

Shared-VM? Kenn ich nicht.
Ähm, schon vergessen dass er mehrere User auf dem Server hat? Wie soll das denn mit nur einem Socket gehen?

Es gibt Server-Anbieter die Mehrere Server-Softwares in einer VM laufen lassen. Das funktioniert in der Theorie, die Praxis sieht da manchmal anders aus.
 

Barista

Top Contributor
Besorg Dir mal ein Buch, ich habe hier

Java in verteilten Systemen

Ähm, schon vergessen dass er mehrere User auf dem Server hat? Wie soll das denn mit nur einem Socket gehen?

Ja, dann ein einziger Serversocket aber mehrere Cient-Sockets mit jeweils genau einem Stream:

Java:
import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;


public final class ChatServer
{
    public ChatServer(
            final int port )
    throws IOException
    {
        final ServerSocket serverSocket = new ServerSocket( port );
        
        while ( true )
        {
            final Socket clientSocket = serverSocket.accept();
            
            final DataInputStream inStream = new DataInputStream( clientSocket.getInputStream() );
            
            final ChatHandler chatHandler = new ChatHandler( clientSocket );
            chatHandler.start();
        }
        
    }

    public static void main( 
            final String[] args )
    throws IOException
    {
        new ChatServer( Integer.parseInt( args[ 0 ] ) );
    }

}


import java.net.Socket;


Java:
public final class ChatHandler
extends Thread
{
    private final Socket clientSocket;

    /**
     * Konstruktor.
     * @param clientSocket
     */
    public ChatHandler(
            final Socket clientSocket )
    {
        this.clientSocket = clientSocket;
    }

}
 

xasz

Mitglied
Ich hab am Client genau einen Socket.
Am Server habe ich einen Serversocket und für jeden Client genau einen Socket.
Aus dem Socket wird dann genau ein InputStream und ein OutputStream erzeugt.

Wenn ich nur ein Stream erzeuge, habe ich ja nur Kommunikation in eine Richtung.
 

Barista

Top Contributor
Ich hab am Client genau einen Socket.
Am Server habe ich einen Serversocket und für jeden Client genau einen Socket.
Aus dem Socket wird dann genau ein InputStream und ein OutputStream erzeugt.

Wenn ich nur ein Stream erzeuge, habe ich ja nur Kommunikation in eine Richtung.

Und die send-Methode von weiter oben, ist die an einem Objekt, das single ist?
 

Barista

Top Contributor
Das könntest Du zusätzlich mit
Java:
if(this.isSending)System.out.println("Das sollte nicht sein" + System.identityHashCode( this ) );
absichern.

Wenn dies nachweist, dass es sich immer um das selbe Objekt handelt,
dann würde ich mir mal das Protokoll ansehen.

Ist das Protokoll wirklich eindeutig, lässt keine Bytes übrig?
 

xasz

Mitglied
So ich bins mal wieder.
Ich hab wieder einiges getestet und rumgespielt.

Also ich glaube ich kann den Serverseitigen Fehler jetzt ausschließen. Das syncronized scheint zu funktionieren, da hab ich einfach irgendwie verkuckt.

Mein Protokoll hab ich überprüft und keinen Fehler gefunden.

Jetzt war mein Ansatz folgender. Um einen Stresstest zu simulieren, habe ich einen Server mit 100K Entites erstellt.
Wenn ich jetzt mit meinem Client gejoint bin, ist er sofort gecrasht, weil er ein falsches Kontrollbyte bekommen hat.
Dann dachte ich mir, ok, ich lasse mir alle meine Bytes, die ich am Client empfange ausgeben und in eine Datei schreiben, dann kann ich später eventuell irgendwie ein Fehler nachvollziehen.
Meine Read Funktion am GameClient.java sieht nun wie folgt aus:

Java:
	public void read(byte[] bytes) {
		try {
			this.inputStream.read(bytes);
			logger.log(java.util.logging.Level.INFO, NetworkParser.bytesToString(bytes));
		} catch (Exception e) {
			logger.log(java.util.logging.Level.WARNING, "Could not Read:"+NetworkParser.bytesToString(bytes));
			e.printStackTrace();
		}
	}

Mein Problem ist, es funktioniert einwandfrei. Sobald ich die log-Befehle auskommentiere crashed es wieder.
Für mich bedeutet das in irgendeiner weise.

Die read Funktion kann in meinem Verständnis nicht gleichzeitig aufgerufen werden. Sie wird nur innerhalb der listen-Funktion verwendet und wird doch nacheinander aufgerufen.

Meine Vermutung ist: Der Logger benötigt ja eine Gewisse Zeit zur Ausführung. Ich vermute, dass ich irgendwie schneller Lese, als es der Inputstream auf die Reihe bekommt, bei großen Mengen, mir die Bytes bereitzustellen. Aber wirklich ne Theorie hab ich auch nicht.

Um Meine Theorie zu testen hab ich folgendes gemacht:

Java:
	public void read(byte[] bytes) {
		try {
			this.inputStream.read(bytes);
			Thread.sleep(1);
			//logger.log(java.util.logging.Level.INFO, NetworkParser.bytesToString(bytes));
		} catch (Exception e) {
			//logger.log(java.util.logging.Level.WARNING, "Could not Read:"+NetworkParser.bytesToString(bytes));
			e.printStackTrace();
		}
	}

Und siehe da: Es funktioniert.
Allerdings verstehe ich nicht warum.
 

Barista

Top Contributor
Java:
int readedByteCount = 0;

while ( readedByteCount < bytes.length )
{
    readedByteCount +=
        this.inputStream.read(
                bytes ,
                readedByteCount ,
                bytes.length - readedByteCount );
}

Einfach mal so aus dem Handgelenk hingetippt, ungetestet.
 

xasz

Mitglied
Ich bin bisher davon ausgegangen, dass die read Funktion blocking ist, bis die entsprechenden Bytes ankommen.
Ich werde es später dann mit ner Schleife testen. Danke erstmal dafür.
Melde mich dann zurück.

Edit: Es scheint das Problem wirklich gelöst zu haben. Da hab ich wohl einfach beim Doku-Lesen versagt.
Ich danke euch für all eure Hilfe.
 
Zuletzt bearbeitet:
Ähnliche Java Themen
  Titel Forum Antworten Datum
I Socket Problem mit den WebSocket Antworten der Discord API Netzwerkprogrammierung 0
K Java Websocketserver Problem | Android to Pi Netzwerkprogrammierung 1
C RMI Produzent-Verbraucher-Problem - Code review Netzwerkprogrammierung 12
B Socket Bilder verschicken via Sockets. Heap-Problem. Netzwerkprogrammierung 2
S Problem bei dem Bluetoothverbindungsaufbau Netzwerkprogrammierung 2
G Server-Client IO Problem Netzwerkprogrammierung 6
M Netty - TCP Problem Netzwerkprogrammierung 4
L Socket Problem mit Server Netzwerkprogrammierung 1
J Chat Server starten über GUI problem Netzwerkprogrammierung 4
M Problem bei Socket (MultiplayerSpiel) Netzwerkprogrammierung 4
M Socket CDI, Websocket reference Problem ! Netzwerkprogrammierung 2
Shams Problem mit Eventbus in Verbindung mit Server Netzwerkprogrammierung 0
G apache httpClient Problem. Netzwerkprogrammierung 5
H Problem mit ObjectStreams Netzwerkprogrammierung 3
A Problem beim Senden von Client zu Server Netzwerkprogrammierung 10
D Socket BufferedWriter/Reader Problem Netzwerkprogrammierung 1
Maxim6394 Problem mit Socks5 Implementierung Netzwerkprogrammierung 0
C Handle Connection Problem Netzwerkprogrammierung 3
E HttpUrlConnection Cookie Problem Netzwerkprogrammierung 0
O 4Gewinnt Multiplayer - Netzwerk Problem (TCP) Netzwerkprogrammierung 1
A Socket Socket-Problem - Object wird nicht übertragen Netzwerkprogrammierung 3
R Problem beim Programmieren eines Chatprogramms Netzwerkprogrammierung 5
E einfaches Problem: Session-Handling bei Servlets Netzwerkprogrammierung 5
G Problem mit einem FileWatcher Netzwerkprogrammierung 7
T Socket Server starten Thread Problem Netzwerkprogrammierung 12
B Client/Server Connection Problem Netzwerkprogrammierung 2
G Problem mit STATIC-Verständnis Netzwerkprogrammierung 8
S Umstellung AS400 auf Postgre - Problem beim Arbeiten mit Metadaten Netzwerkprogrammierung 2
J Facelets Include Rendered Problem Netzwerkprogrammierung 2
J Socket Problem mit C++/Java Netzwerkprogrammierung 20
P Problem mit Datagram-Sockets Netzwerkprogrammierung 2
G Socket NIO2 Problem mit AsynchronousSocketChannel beim Schließen Netzwerkprogrammierung 3
G Cookie Verwaltungs Problem nach Login auf InetSeite (Wo utma-Cookie?) Netzwerkprogrammierung 18
C Socket Problem mit ObjectInput/OutputSream Netzwerkprogrammierung 7
B Socket Problem mit Netzwerkchat Netzwerkprogrammierung 21
D RMI Problem beim shutdown von verteilter CORBA-Anwendung Netzwerkprogrammierung 6
Maxim6394 ipv6 Problem Netzwerkprogrammierung 2
Maxim6394 Proxyserver Performance Problem Netzwerkprogrammierung 11
M Problem Client - Server Sockets: .ready() wird nie true! Netzwerkprogrammierung 6
C Socket Problem mit ObjectInput/OutputSream Netzwerkprogrammierung 5
B RMI und Problem mit rmic-Tool Netzwerkprogrammierung 3
C FTP storeFileStream Problem Netzwerkprogrammierung 3
N Problem über http eine Datei zu senden Netzwerkprogrammierung 4
D JavaMail - Mailsabrufen Problem (imap) Netzwerkprogrammierung 12
J HTTP Übersetzung yahoo babelfish - Zeichensatz-Problem Netzwerkprogrammierung 6
D Apache CXF, JAX-WS Problem bei Arrays - einfacher Server Netzwerkprogrammierung 2
M Problem beim Datenempfang Netzwerkprogrammierung 2
X Problem mit Server-Client-Kommunikation Netzwerkprogrammierung 14
M Problem mit Socket-Verbindung Netzwerkprogrammierung 2
N NIO Problem beim speziellen Behandeln von einzelnen Benutzern Netzwerkprogrammierung 13
D Thread problem Netzwerkprogrammierung 3
T Servlets JSP: Tomcat Problem Netzwerkprogrammierung 4
K Client - Server Problem Netzwerkprogrammierung 16
T RMI Problem Client-Server Netzwerkprogrammierung 2
P RMI Stub Problem Netzwerkprogrammierung 3
D Socket UDP Übertragungs Problem Netzwerkprogrammierung 7
I HTTP Post aus html in Java einlesen - Problem Netzwerkprogrammierung 2
I HTTP Post aus html in Java einlesen - Problem Netzwerkprogrammierung 6
D Problem mit ObjectInputStreams Netzwerkprogrammierung 10
D Socket Problem mit InputStreamReader Netzwerkprogrammierung 3
N CRC32 CheckSum Problem bei UDP Netzwerkprogrammierung 2
V Java Mail Api - IMAP Problem Netzwerkprogrammierung 6
P RMI Problem Netzwerkprogrammierung 4
I Problem bei Outputstreamerzeugung in anderer Klasse als Socket Netzwerkprogrammierung 5
S Socket Problem mit Objektübertragung Netzwerkprogrammierung 16
Shoox Reader / Writer Problem Netzwerkprogrammierung 2
S Problem mit 2 Serversockets unter Win XP Netzwerkprogrammierung 7
P Socket Problem mit Netzwerkverbindung über TCP Netzwerkprogrammierung 12
M RMI - Connection Problem Netzwerkprogrammierung 7
J Socket Client - Server Problem Netzwerkprogrammierung 4
M Socket Chat-Client-Problem Netzwerkprogrammierung 8
D Client Server Problem, Methode readline() löst SocketException "Connection reset" aus Netzwerkprogrammierung 8
E HTTP Problem beim Auslesen von Websiten Netzwerkprogrammierung 6
T Problem bei Findung der richtigen Architektur Netzwerkprogrammierung 3
AlexSpritze Authentication Problem bei WebServices Netzwerkprogrammierung 4
J Socket Problem -Proxies Netzwerkprogrammierung 2
R HTTP Problem bei Authentifizierung über (Http)UrlConnection Netzwerkprogrammierung 2
F Konzept Problem Netzwerkprogrammierung 12
G NIO Sockets Architektur Problem Netzwerkprogrammierung 2
H Neues NIO problem nachricht an alle clients Netzwerkprogrammierung 3
J Client server problem Netzwerkprogrammierung 3
S Problem bzgl. Sockets / Server-Client-Struktur Netzwerkprogrammierung 3
W RMI Problem Netzwerkprogrammierung 4
M Problem mit Server Netzwerkprogrammierung 5
N Socket Java server c# client problem Netzwerkprogrammierung 7
Y Problem mit ObjectInputStream beim lesen vom Socket Netzwerkprogrammierung 10
R RMI Hibernate Problem Netzwerkprogrammierung 4
Z RMI Problem Netzwerkprogrammierung 11
F RMI problem mit RMI Netzwerkprogrammierung 3
H Object Cast Problem nach Übertragung mit Log4j Netzwerkprogrammierung 5
H Log4j SocketAppender Problem Netzwerkprogrammierung 2
P HTTP Problem beim Download von einer Datei Netzwerkprogrammierung 4
P Problem mit JOscarLib Netzwerkprogrammierung 6
0din SMTP Client - readline problem Netzwerkprogrammierung 4
C komisches Problem / ObjectInputStream erstellt sich nicht Netzwerkprogrammierung 5
W vista / win server 2003 netzlaufwerk problem Netzwerkprogrammierung 4
W Problem mit HTTP-Dateiübertragung Netzwerkprogrammierung 6
E Problem: netbeans 6.5 und webanwendung Netzwerkprogrammierung 6
C Problem mit Übertragung einer Klasse in RMI Netzwerkprogrammierung 10
B UDP Problem Netzwerkprogrammierung 25

Ähnliche Java Themen

Neue Themen


Oben