Klasse mit String-List über PrintWriter schreiben. Meinung/Ideeen?

Kai008

Erfahrenes Mitglied
Ich verwende zum Kommunizieren über Sockets meistens die Object-I/O-Streams. Jetzt versuche ich wieder davon wegzukommen, weil ich gemerkt habe, dass die echt langsam sind. Außerdem fliegen oft (scheinbar grundlos) Exceptions. Selbst ein 2-Byte-langer String kommt erst mit (grob geschätzt) einer viertel Sekunde Verzögerung an, was man natürlich deutlich merkt. Deshalb habe ich die Klasse umgeschrieben, um sie per PrintWriter/BufferedReader zu schreiben/lesen und rekonstruieren. Nichts besonderes, aber kann ich trotzdem eure Meinung darüber haben? Und event. Verbesserungsvorschläge, was mir weiter helfen könnte, oder die Geschwindigkeit event. weiter steigert?

Java:
package core;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

public class Command implements CommanandConstants
{
	private static final int BUFFER_LENGHT = 2048;
	private static final char[] BUFFER = new char[BUFFER_LENGHT];
	
	private static Map<Socket, PrintWriter> writerMap;
	private static Map<Socket, BufferedReader> readerMap;
	
	private String command;
	private String location;
	private List<String> parameterList;
	
	static
	{
		writerMap = new HashMap<Socket, PrintWriter>();
		readerMap = new HashMap<Socket, BufferedReader>();
	}
	public Command(String command, String location)
	{
		super();
		this.command = command;
		this.location = location;
	}
	private Command(String readedData)
	{
		super();
		String[] split = Pattern.compile(";").split(readedData, 0);
		command = split[0];
		location = split[1];
		
		parameterList = new ArrayList<String>();
		for(byte b = 2; b < split.length; b++)
			parameterList.add(split[b]);
	}

	public void sendAboutSocket(Socket socket)
	{
		PrintWriter printWriter = writerMap.get(socket);
		if(printWriter == null)
		{
			try
			{
				printWriter = new PrintWriter(socket.getOutputStream());
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
			writerMap.put(socket, printWriter);
		}
		printWriter.write(toString());
		printWriter.flush();
	}
	public boolean hasCommand(String s)
	{
		return(command.equals(s));
	}
	public boolean hasLocation(String s)
	{
		return(location.equals(s));
	}
	public String toString()
	{
		String asString = command + ";" + location + ";";
		if(parameterList != null)
			for(byte b = 0; b < parameterList.size(); b++)
				asString+= parameterList.get(b) + ";";
		return(asString);
	}
	public String getParameter(int i)
	{
		String parameter = null;
		if(parameterList == null)
			parameter = parameterList.get(i);
		return(parameter);
	}
	public static Command readFromSocket(Socket socket)
	{
		BufferedReader bufferedReader = readerMap.get(socket);
		if(bufferedReader == null)
		{
			try
			{
				bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
			readerMap.put(socket, bufferedReader);
		}
		
		Command command = null;
		try
		{
			int charCount = bufferedReader.read(BUFFER, 0, BUFFER.length);
			String commandAsString = String.valueOf(BUFFER, 0, charCount);
			command = new Command(commandAsString);
		}
		catch (SocketException e) {}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		return(command);
	}
	public synchronized void addParameter(String parameter)
	{
		if(parameterList == null)
			parameterList = new ArrayList<String>();
		parameterList.add(parameter);
	}
}
 
nur mal eine zwischenfrage ... ist das java-zu-java-kommunikation?

wenn ja, warum machst du nicht rmi/jrmp?

grüße
gore
 
Performancetechnisch fällt mir folgendes auf :

Java:
	private Command(String readedData)
	{
		super();
		String[] split = Pattern.compile(";").split(readedData, 0);
		command = split[0];
		location = split[1];
		
		parameterList = new ArrayList<String>();
		for(byte b = 2; b < split.length; b++)
			parameterList.add(split[b]);
	}

sowie 

	public String toString()
	{
		String asString = command + ";" + location + ";";
		if(parameterList != null)
			for(byte b = 0; b < parameterList.size(); b++)
				asString+= parameterList.get(b) + ";";
		return(asString);
	}

1. mach nicht jedesmal einen patterncompile -> kostet zeit
1a) mach ein new arraylist(split.length), sonst wird immer wieder neu alloziiert
2. nimm einen stringbuilder statt "+" konkatenation bei strings, dito
 
Danke, aber ich wollte hauptsächlich die Verbindunggeschwindigkeit verbessern, Prozessormäßig hatte ich nur selten Probleme.
Ich nehme an, der StringBuilder ist so richtig verwendet, oder?

Java:
public String toString()
{
	StringBuilder builder = new StringBuilder();
	builder.append(command);
	builder.append(";");
	builder.append(location);
	builder.append(";");
	
	if(parameterList != null)
		for(byte b = 0; b < parameterList.size(); b++)
		{
			builder.append(parameterList.get(b));
			builder.append(";");
		}
	return(builder.toString());
}

Ist Java-2-Java, aber bei einen Remote Methoden-Aufruf per RMI müssen die Klassen der Parameter wieder serialisiert werden, was das selbe Geschwindigkeitsproblem mit sich bringen würde, oder? So habe ich einen Server, der in einem Thread ließt, die Änderungen prüft/durchführt, eventuell eine Antwort sendet und wieder am Schleifenanfang zurückkehrt.
 
mmm

machst Du Java NIO ?

Wenn ja, solltest Du mal die Sleeptime Deines Acceptorthreads prüfen, sprich der Thjread deines Servers, der beim Eingang einer Verbindung reagiert. Alles was wir hier je mit RMI gemacht haben, war im übrigen sauschnell.

Grüße
gore
 
Nein, NIO habe ich nur einmal mit FileChannel für ein Backup-Programm benutzt. Um die Verbindung anzunehmen verwende ich ServerSocket.accept(), und dieser und der BufferedReader blockieren den Thread, bis was reinkommt. Deshalb schläft der Server (von mir aus) nie.

Wenn RMI wirklich so schnell ist, werde ich es mir mal anschauen. Vielen Dank für deine Hilfe.
 
stimmt, das socket.accept() habe ich übersehen.

Ich könnte mir vorstellen, dass Dein StreamWriter lahm ist. Da du ja Strings hast, kannst Du die doch direkt mittels byte[] transporieren. Sollte auf jeden Fall en. Du kannst afaik auch einen ObjectStream nehen - schau mal unter

java.io_ObjectInputStream;
java.io_ObjectOutputStream;
java.io_OutputStream;

Dann kannst Du das Zeug auch direkt in Objectform streamen, was RMI letztendlich auch macht.
 
Ja, aber dann müsste ich dem String zu bytes und wieder zurückwandeln.
Die ObjectStreams waren der Grund, warum ich die Klasse so versenden will.
Ich habe das Projekt mal hochgeladen, falls du irgendwann zwischendurch mal kurz Zeit findest.

Beim Applet ist eigendlich nur core.loginscreen.game.surfaceGame.SurfaceKeyPanel wichtig, das macht in der keyPressed/Released aus dem momentanen Status und der gedrückten Taste den neuen Status und sendet ihm serialisiert am Server.

Dort wird es von core.client.Client empfangen, er führt die core.client.ParseGame.parse(Command, Client) aus, und verteilt in der sendClientStat(Command, Client) das Objekt an alle Clients auf der Karte.

core.loginscreen.game.surfaceGame.WaitForIncomingCommandsThread empfängt dieses am Applet und setzt dem jeweiligen Spieler auf dem Status, damit er sich bewegt

Wenn man den localhost oder eine IP, die auf ihm zeigt verwendet, läuft alles ziemlich gut. Wenn man die Internet-IP verwendet (in core.Applet in einer Konstanten) merkt man schon beim ersten Tastendruck eine deutliche Verzögerung.
 

Anhänge

  • Anhang.zip
    41,9 KB · Aufrufe: 10
Hast Du mal versucht, die einzelnen Schritte mit millisekunden zu messen?

Du kannst das indem Du Dir über new Date().getTimeInMillis() eine aktuelle Uhrzeit holst. Dann kannst Du messen, wo innerhalb Deiner Versendefunktion der Engpass ist.

Die Verbindung zu localhost ist selbstverständlich deutlisch schneller, da sie keinen Umweg über die Netzwerkkarte nimmt.

Grüße
gore
 
Ich weiß, dass die "äußere IP" langsamer ist, das Internet ist vergleichsweiße (zumindest in unserem Raum) ja auch ein echt langsames Netzwerk
Argh, auf die Zeitmessung wäre ich echt nicht so schnell gekommen. x_x"

Code:
Keypanel Start: 1269436619906
Keypanel sende Command: 1269436619906
Keypanel sende Command beendet: 1269436619906
Keypanel Start: 1269436620406
Keypanel sende Command: 1269436620406
Keypanel sende Command beendet: 1269436620406
Keypanel Start: 1269436620937
Keypanel Start: 1269436620984
Keypanel Start: 1269436621015
Keypanel Start: 1269436621046
Keypanel Start: 1269436621093
Keypanel Start: 1269436621124
Keypanel Start: 1269436621156
Keypanel Start: 1269436621203
Keypanel Start: 1269436622062
Keypanel sende Command: 1269436622062
Keypanel sende Command beendet: 1269436622062
Keypanel Start: 1269436622937
Keypanel sende Command: 1269436622937
Keypanel sende Command beendet: 1269436622937
Keypanel Start: 1269436623906
Keypanel sende Command: 1269436623906
Keypanel sende Command beendet: 1269436623906
Code:
Applet status empfangen: 1269436620234
Status gesetzt: 1269436620234
Applet status empfangen: 1269436620437
Status gesetzt: 1269436620437
Applet status empfangen: 1269436620640
Status gesetzt: 1269436620640
Applet status empfangen: 1269436621640
Status gesetzt: 1269436621640
Applet status empfangen: 1269436622453
Status gesetzt: 1269436622453
Applet status empfangen: 1269436622656
Status gesetzt: 1269436622656
Applet status empfangen: 1269436623249
Status gesetzt: 1269436623249
Applet status empfangen: 1269436623656
Status gesetzt: 1269436623656
Applet status empfangen: 1269436624359
Status gesetzt: 1269436624359
Applet status empfangen: 1269436624859
Status gesetzt: 1269436624859
Code:
Server: Beginne verteilen 1269436620031
Verteilen beendet: 1269436620031
Server: Beginne verteilen 1269436620234
Verteilen beendet: 1269436620234
Server: Beginne verteilen 1269436620437
Verteilen beendet: 1269436620437
Server: Beginne verteilen 1269436621437
Verteilen beendet: 1269436621437
Server: Beginne verteilen 1269436622249
Verteilen beendet: 1269436622249
Server: Beginne verteilen 1269436622453
Verteilen beendet: 1269436622453
Server: Beginne verteilen 1269436623046
Verteilen beendet: 1269436623046
Server: Beginne verteilen 1269436623453
Verteilen beendet: 1269436623453
Server: Beginne verteilen 1269436624062
Verteilen beendet: 1269436624062
Server: Beginne verteilen 1269436624562
Verteilen beendet: 1269436624562

Code:
Errechne neuen Status: 0 ms.
Senden/Empfangen des Status: 125 ms. (Ziemlich verschätzt, ist nur eine achtel Sekunde Verzögerung.)
An alle User senden: 0 ms. (Das eigendliche senden rennt wohl in einen anderen Thread weiter.)
Zeit zwischen senden des Servers und empfangen: 203 ms.

Es kostet eigendlich nichts außer der O-I/O-S Zeit.
Und das merkt man schon recht deutlich: http://www.imagebanana.com/view/le497wud/Video.gif
 

Neue Beiträge

Zurück