Apache Mina - Hilfestellung

Raziell

Bekanntes Mitglied
Hallo zusammen,

ich arbeite mich momentan in Apache Mina ein. Hauptsächlich orientiere ich mich derzeit am Client-Server Beispiel auf der Mina Webseite. Ich habe jetzt ein paar allgemeine Fragen zur Architektur von Mina.

Ich habe bei herkömmlichen ServerSocket Anwendungen auf Server Seite immer einen Thread pro Client gehabt, der auf einkommende Daten eines Sockets lauscht. So wie ich das jetzt im Beispiel gesehen habe gibt es einen zentralen-Handler, welcher IOHandlerAdapter erweitert oder IOHandler implementiert. Für mich stellt sich nun die Frage ob es Sinn macht, alle einkommenden Daten über den einen Handler zu verarbeiten, oder ob man auch das Multithreadprinzip anwenden kann bzw. evtl. sollte? :)

Erstmal die eine Frage, danach kommt noch mehr :D

Danke und...
 
Zuletzt bearbeitet:

KrokoDiehl

Top Contributor
Hm... ich halte es für sinnvoller das Handler-Prinzip von MINA zu übernehmen. Falls der Server Anfragen bearbeiten muss, die "zu" lange dauern, kann man das immernoch in einen Thread auslagern und danach senden lassen.
Aber ich wüsste nun nicht, warum man den MINA-Aufbau wieder zurückbiegen sollte ... dann kann man doch gleich die normalen Socket und ServerSockets nehmen.
 

Raziell

Bekanntes Mitglied
Hi,
erstmal Danke für deine Antwort. Also ich werde es auf jeden Fall erstmal so belassen wie MINA es meines erachtens auch vorsieht.

Jetzt habe ich allerdings noch eine zweite Frage. Ich sende relativ viele Daten in kurzer Zeit. Momentan sende ich einfach Strings mit
Java:
IOSession.write()
Die Frage ist jetzt, wie ich möglichst perfomant und elegent Daten schnell versenden kann. Bin jetzt gerade dabei mir den ProtocolCodecFilter anzuschauen. Das ganze kommt mir allerdings ziemlich komplex vor. Ist das denn überhaupt der richtige Weg, den ich da mit dem ProtocolCodecFilter gehe, oder soll ich bei den Strings bleiben? Es empfiehlt sich doch nen ByteBuffer oä. zu verwenden oder?

Danke und...
 

Raziell

Bekanntes Mitglied
Hat denn keiner ne Idee oder vllt. ein paar Tipps zu dem Thema :)

Also was am besten wie benutzen: ProtocolCodecFilter, ByteBuffer, oä.?

Danke
 
T

tuxedo

Gast
Kommt drauf an was du machen willst. Wenn du mehrere "Nachrichtenarten" hast, ist der Protocolcodecfilter schon das richtige.

Beim senden ist das recht "schnuppe". Einfach mit write() auf die Session schreiben. MINA queued die zu sendenden Nachrichten. Kriege damit über 10.000 Nachrichten pro Sekunde durchs 100Mbit Netzwerk gesendet (kommt stark auf die Nachricht selbst an).

Für die Empfangsseite: Wie bereits erwähnt wurde: Einen Handler implementieren und je nach Anwendungsgebiet die eingehenden Nachrichten in einen Thread-Pool zur weiteren Verarbeitung geben.
Wenn man sich da nicht allzu doof anstellt, ist auch das super schnell. Hab in einem Performancetest erst diese Woche einen (Empfangs-)Durchsatz von 38.000 Nachrichten pro Sekunde auf der Serverseite erreicht (kommt auch hier auf die Art der Nachricht an). Und da geht sicher noch mehr...

Gruß
Alex
 
Zuletzt bearbeitet von einem Moderator:

Raziell

Bekanntes Mitglied
Hallo,

danke für deine Antwort. Also prinzipiell sind die Nachrichten ziemlich gleich aufgebaut. Nämlich in etwa so: "TYPE;value1;value2;...". Da könnte ich dann überall einfach Strings verwenden und bräuchte ja ansich keinen eigenen Protocol-Codec implementieren.

Dann bau ich das ganze jetzt erstmal einfach mit session.write() und führe die Berechnungen in einem Thread-Pool aus.
Stellt Mina denn schon so eine Art Thread-Pool zur Verfügung oder muss ich den selbst implementieren? Ich meine nämlich schonmal das Wort Thread-Pool im zusammenhang mit Mina gehört zu haben.

Danke
 
T

tuxedo

Gast
Naja, im Prinzip hat mein SIMON auch immer die gleiche Nachricht:

type;id;bodylength;body

Trotzdem übertrage ich keine Strings. Wenn du den String "1234567890" überträgst, dann sind das allein für die Zeichenkette 20 bytes + Overhead des Strings. Wenn du 1234567890 als Integer überträgst, dann sind das 4 bytes.

Von daher: Kommt drauf an was du machen willst.

MINA an sich kann auch mit Threadpools umgehen. Aber die sitzen dann in der Filterkette oder auf der Acceptor-Seite etc... Laut MINA Doku sollte man da nix dran machen wenn man nicht weiß was man tut.

Ergo: Implementiere den IOHandler, und in der messageReceived() Methode liest du die Daten und gibst sie weiter an den Threadpool. Kannst ja mal in meine Implementierung reinschauen:

SIMON - /trunk/src/main/java/de/root1/simon/Dispatcher.java - root1.de - Software Engineering

Java:
public void messageReceived(IoSession session, Object message) throws Exception {

    logger.debug("Received message from session {}", Utils.longToHexString(session.getId()));
    AbstractMessage abstractMessage = (AbstractMessage) message;
    messageProcessorPool.execute(new ProcessMessageRunnable(this, session, abstractMessage));

}

Ich habe für alle Nachrichten eine gemeinsame Oberklasse "AbstractMessage" (siehe MINA Doku zum ProtocolCodecFilter). Also caste ich die Message entsprechend.
Die Message stecke ich dann in eine Klasse die "Runnable" implementiert und die, wenn durch den Threadpool die run() Methode ausgeführt wird, die Message verarbeitet. Das Runnable stecke ich dann zur Verarbeitung in den ThreadPool. Und diesen kann ich beim anlegen konfigurieren: Entweder als "cached" Threadpool (die JVM entscheidet selbst wieviele Threads sie benutzt) oder als "fixed" Threadpool (ich lege fest wieviele Threads arbeiten).

gruß
Alex
 

Raziell

Bekanntes Mitglied
Hi,
danke für deine Tipps, also ich bin jetzt gerade dabei das ganze anhand von dem Beispiel auf der Mina Seite mit nem eigenen Protocol-Codec zu realisieren. Ist doch weniger kompliziert als ich dachte und vor allem sauberer und flexibler.

Ich überlege mir jetzt momentan noch wie ich den Thread-Pool, und das Speichern der User und IOSessions am besten realisieren kann.

Zum Beispiel für einen Threadpool habe ich folgendes gefunden Mit ThreadPoolExecutor Arbeit unter Java effizient parallelisieren | Konstantin Filtschew WebLog. Macht das Sinn das so aufzubauen? Beispielsweise habe ich serverseitig pro User auf jeden Fall schonmal einen Thread, welcher Benutzereingaben vom IOHandler bekommt und daraufhin verschiedenste Berechnungen durchführt.

Danke...
 
Zuletzt bearbeitet:
T

tuxedo

Gast
Wo ist das Problem? Mit dem ExecutorService einen ThreadPool anlegen, die Nachricht in eine Klasse die "Runnable" implementiert stecken (das ausführen von run() muss dann die verarbeitung vornehmen), und dann die Runnable-Klassen-Instanz in den Threadpool werfen.

Dann hast du genau so viel Threads wie du brauchst, bzw. mit dem Threadpool konfigurierst (siehe ExecutorService (Java Platform SE 6)). Fertig.

Dein Link ist eine eigenimplementierung eines ThreadPools. Aber warum das Rad neu erfinden (mit ein paar ecken), wenn es schon eine fertige implementierung in der Java Standardlib gibt (ohne größere Ecken)?!

- Alex
 

Raziell

Bekanntes Mitglied
Danke dir wusste nicht, dass es so einfach geht :)

Ein Problem habe ich noch und zwar überlege ich mir momentan wie ich am besten die Speicherung der Clients realisieren kann.

Folgendes Szenario möchte ich verwenden:

  • Eine synchonisierte HashMap mit IOSession und Client-Objekt.
  • Der Handler löscht Objekte sprich Clients aus der Liste und fügt neue hinzu.
  • Zu jedem Client-Objekt mindestens ein Thread, welcher auf die Map zugreift und die Zustände des Objektes ändert.
  • Ein globaler Thread holt sich in bestimmten Intervallen Informationen von allen Objekte aus der Map und sendet diese an alle Clients, damit die sich synchronisieren können.

Das Problem sehe ich in der synchronisierten Map. Es kann ja dann immer nur ein Thread auf die Map zugreifen, d.h. alle anderen Threads warten auf freigabe. Andererseits bekomme ich ja Probleme wenn der Handler Objekte aus der Map löscht, während ein Thread darauf zugreift.

Hat jmd. Tipps zur realisierung?

Danke...
 
Zuletzt bearbeitet:
T

tuxedo

Gast
Danke dir wusste nicht, dass es so einfach geht :)

Man lernt eben nie aus ;-)

Ein Problem habe ich noch und zwar überlege ich mir momentan wie ich am besten die Speicherung der Clients realisieren kann.

Folgendes Szenario möchte ich verwenden:


* Eine synchonisierte HashMap mit IOSession und Client-Objekt.

klingt soweit mal gut.

* Der Handler löscht Objekte sprich Clients aus der Liste und fügt neue hinzu.

Macht Sinn ...

[/quote]
* Zu jedem Client-Objekt mindestens ein Thread, welcher auf die Map zugreift und die Zustände des Objektes ändert.
Damit machst du dir deine Skalierbarkeit kaputt. Dann kannst du gleich wieder java IO nehmen wo du einen Thread pro Client brauchst.
Was willst du denn da für Zustände ändern?!

* Ein globaler Thread holt sich in bestimmten Intervallen Informationen von allen Objekte aus der Map und sendet diese an alle Clients, damit die sich synchronisieren können.

Keine Ahnung für das gut ist. Kenne dein genaues Vorhaben ja nicht.

Das Problem sehe ich in der synchronisierten Map. Es kann ja dann immer nur ein Thread auf die Map zugreifen, d.h. alle anderen Threads warten auf freigabe. Andererseits bekomme ich ja Probleme wenn der Handler Objekte aus der Map löscht, während ein Thread darauf zugreift.

Hat jmd. Tipps zur realisierung?

Danke...

Wenn du die Map nur zum Speichern der Session nutzt, dann hast das das synchroisationsproblem doch nur dann, wenn eine Client kommt oder geht?!

- Alex
 

Raziell

Bekanntes Mitglied
Vielleicht erstmal vorne weg zur info: Es geht um einen kleinen Gameserver.

* Zu jedem Client-Objekt mindestens ein Thread, welcher auf die Map zugreift und die Zustände des Objektes ändert.

Damit machst du dir deine Skalierbarkeit kaputt. Dann kannst du gleich wieder java IO nehmen wo du einen Thread pro Client brauchst.
Was willst du denn da für Zustände ändern?!

Die Client-Threads sollen eigtl. die Positionen der Spieler anhand erhaltener Daten vom Client neu berechnen etc..
Das heisst, die Threads müssten dann auf das jeweilige Client Objekt in der Map zugreifen und "Bewegung simulieren".

* Ein globaler Thread holt sich in bestimmten Intervallen Informationen von allen Objekte aus der Map und sendet diese an alle Clients, damit die sich synchronisieren können.

Keine Ahnung für das gut ist. Kenne dein genaues Vorhaben ja nicht.

Der globale Thread sendet dann beispielsweise die Koordinaten aller Clients in bestimmten Intervallen an alle Spieler, damit diese die Positionen der anderen Spieler synchronisieren können.

Vllt. ist meine Denkweise auch momentan iwie. ganz falsch aber das war halt meine erste spontane idee.

Danke und...
 
Zuletzt bearbeitet:
T

tuxedo

Gast
Na bei einem Gameserver hat man doch für gewöhnlich einen einzigen Thread der den Spielablauf koordiniert.

Würde das dann so erledigen:

Im IoHandler kommen Pakete rein. Die werden zum "auslesen/evaluieren" in einen Threadpool gesteckt.

Jedes Paket lässt sich ja anhand der Session (und dem Context der sich aus der Client-Serverkommunikation ergibt (Stichwort Login)) einem Client/Spieler zuordnen.

Ergo kriegt jeder Spieler auf dem Server ein Objekt in dem er seine Daten/Zustände einpflegt. Dann musst du nur den Zugriff auf die Client-Objekte für den Threadpool, der die Datenpakete zur verarbeitung bekommt, synchronisieren.

Ergänzend dazu hast du den MainGameLoop-Thread, der die Spielwelt "bearbeitet" und ggf. Datenpakete mit Positionsupdates oder sonstigen an entsprechende Clients schickt.

Hier musst du natürlich auch die Spieler-Objekte entsprechend synchronisieren.

Kommt halt alles drauf an was du machen willst: Ein Rundenbasiertes Spiel, eine Echtzeit MMORPG, Schach, ...

Eventuell solltest du auch mal das "Killer Game Programming" Buch für Java lesen?! Da gibts einige "Best Practices". Wenn du mit google umgehen kannst, findest du das Buch in mehreren PDFs auch online... (kostenlos und legal)

- Alex
 

Raziell

Bekanntes Mitglied
Hi,
das ganze Spiel soll erstmal möglichst simpel in Echtzeit ablaufen.

Im IoHandler kommen Pakete rein. Die werden zum "auslesen/evaluieren" in einen Threadpool gesteckt.

Jedes Paket lässt sich ja anhand der Session (und dem Context der sich aus der Client-Serverkommunikation ergibt (Stichwort Login)) einem Client/Spieler zuordnen.

Ergo kriegt jeder Spieler auf dem Server ein Objekt in dem er seine Daten/Zustände einpflegt. Dann musst du nur den Zugriff auf die Client-Objekte für den Threadpool, der die Datenpakete zur verarbeitung bekommt, synchronisieren.

Genau so habe ich es bisher. Das Spieler Objekt auf Serverseite enthält beispielsweise x, y Koordinten etc..

Meine ursprüngliche Idee war, dass ein Thread jeweils die Bewegungen eines Spielers simuliert, denn der Server erhält ja lediglich einen Tastendruck des Clients. Somit muss ja die y-Position beispielsweise solange verändert werden, bis der Spieler stehen bleibt oder sich horizontal bewegt, ergo der Server einen anderen Tastendruck empfängt.

Wäre dafür deiner Meinung nach dann der "MainGameLoop-Thread" verantwortlich, oder sollte sich der Threadpool mit den übergebenen Spieler-Objekten darum kümmern?

Danke für den Tipp mit dem Buch
 
Zuletzt bearbeitet:
T

tuxedo

Gast
Lesen bildet... Sonst muss man das Rad halt nochmal neu erfinden.

Ohne das Buch komplett gelesen zu haben (hab nur ein paar Kapitel gelesen), würde ich darauf tippen, dass so ein Server i.d.R. nicht jede Spielerposition mittels einem eigenen, dedizierten Thread berechnet, sondern anhand von:

* Geschwindigkeit
* Richtung
* Zeit

Ergo würde der MainLoopThread, wenn er wieder an der Stelle angelangt ist an der die Spieler Positionsupdates geschickt bekommen, eine Berechnung durchführen die ermittelt, wo der Spieler gerade ist. Mit Geschwindigkeit, Richtung und Zeit lässt sich das ja recht easy erreichnen. Dann wiird die neue Koordinate wieder im Spieler/Session-Objekt gesichert und an entsprechende Clients verteilt.

- Alex
 

Raziell

Bekanntes Mitglied
Lesen bildet... Sonst muss man das Rad halt nochmal neu erfinden.

Natürlich, das ist mir durchaus bewusst. Allerdings denke ich auch nicht, dass es falsch ist, sich auch mal selbst ein paar Gedanken vorweg zum Thema zu machen :)

* Geschwindigkeit
* Richtung
* Zeit

Ergo würde der MainLoopThread, wenn er wieder an der Stelle angelangt ist an der die Spieler Positionsupdates geschickt bekommen, eine Berechnung durchführen die ermittelt, wo der Spieler gerade ist. Mit Geschwindigkeit, Richtung und Zeit lässt sich das ja recht easy erreichnen. Dann wiird die neue Koordinate wieder im Spieler/Session-Objekt gesichert und an entsprechende Clients verteilt.

Seltsam, genau diese Idee kam mir eben beim Autofahren :D

Also ich merke mir praktisch die vergangene Zeit zwischen den Aktionen und rechne mit Hilfe der Laufrichtung die neue Position der Spieler aus. Beschleunigung kann ich ignorieren, da es keine gibt :D

Dann werde ich mal versuchen, dass ganze umzusetzen.

Danke
 

Raziell

Bekanntes Mitglied
Hallo,
ich habe nochmal ne Frage und zwar habe ich Momentan einen Message Typen und das ganze mit Hilfe der ProtocolCodecFactory umgesetzt.

Meine doDecode Methode sieht folgendermaßen aus:

Java:
	@Override
	protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
		int type = in.getInt();
		int bodyLength = in.getInt();
		String body = in.getString(decoder);
		Message request = new Message(type, bodyLength, body);
		out.write(request);
		return true;
	}

Und so sieht meine encode aus:

Java:
	@Override
	public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
		Message request = (Message) message;
		IoBuffer buffer = IoBuffer.allocate(20, false);
		buffer.setAutoExpand(true);
		buffer.putInt(request.getType());
		buffer.putInt(request.getBodyLength());
		buffer.putString(request.getBody(), encoder);
		buffer.flip();
		out.write(buffer);
	}

Es funktioniert zwar alles einwandfrei aber meine Frage ist jetzt, wo ich ansetzen muss, wenn ich mehrere Message-Typen habe? Ich müsste ja quasi in den beiden Methoden prüfen, welcher Typ-Message es ist und den Buffer dementsprechend füllen oder?

Da gibt es doch bestimmt ne bessere Lösung.

Danke
 
Zuletzt bearbeitet:
T

tuxedo

Gast
Entweder du schaust das im doch recht ausführlichen MINA Beispiel an, oder du schaust in den SIMON Source, oder du bemühst google. Der ProtocolCodecFilter setzt eine Klasse ein die anfangs den Typen ausliest, und gemäß Typ dann den entsprechenden Encoder oder Decoder benutzt/lädt.

Mal davon abgesehen: Du hast eine mögliche Paketfragmentierung noch nicht berücksichtigt. Es ist nicht immer sichergestellt, dass ein Puffer schon die kompletten Daten für die Nachricht enthält. Ergo musst du schauen ob genug Bytes im Puffer liegen um die Nachricht komplett raustzulesen.
Ansonsten läuft du Gefahr eine BufferUnderFlowException zu erhalten.

- Alex
 

Raziell

Bekanntes Mitglied
Mal davon abgesehen: Du hast eine mögliche Paketfragmentierung noch nicht berücksichtigt. Es ist nicht immer sichergestellt, dass ein Puffer schon die kompletten Daten für die Nachricht enthält. Ergo musst du schauen ob genug Bytes im Puffer liegen um die Nachricht komplett raustzulesen.
Ansonsten läuft du Gefahr eine BufferUnderFlowException zu erhalten.

Ok das heisst ich muss quasi prüfen ob IOBuffer.remaining() >= Anzahl der Bytes meiner Nachricht wenn ich das richtig verstehe?

Entweder du schaust das im doch recht ausführlichen MINA Beispiel an, oder du schaust in den SIMON Source, oder du bemühst google. Der ProtocolCodecFilter setzt eine Klasse ein die anfangs den Typen ausliest, und gemäß Typ dann den entsprechenden Encoder oder Decoder benutzt/lädt.

Ich habe mir deine SIMON Implementierung bereits angeschaut allerdings ist dort noch vieles unklar und meiner Meinung nach einfach noch zu komplex für mich.

Über Google konnte ich leider nichts Hilfreiches zu meinem Problem finden.

Das Mina Beispiel habe ich ja durchgearbeitet, allerdings wird dort ja auch nur ein Nachrichtentyp verwendet.

Ich lege ja zu Beginn des Programmes einen ProtocolCodecFilter für Server und Client fest.

die encode und doDecode Methoden werden ja automatisch aufgerufen, sobald Nachrichten verschickt bzw. empfangen werden, um den jeweilgen Encoder/Decoder auch zu nutzen. Nur weiß ich nicht wie ich dort die Typen unterscheiden kann, bzw. ob das der richtige Weg ist.

Danke...
 
T

tuxedo

Gast
Ok das heisst ich muss quasi prüfen ob IOBuffer.remaining() >= Anzahl der Bytes meiner Nachricht wenn ich das richtig verstehe?

Korrekt.

Ich habe mir deine SIMON Implementierung bereits angeschaut allerdings ist dort noch vieles unklar und meiner Meinung nach einfach noch zu komplex für mich.

Über Google konnte ich leider nichts Hilfreiches zu meinem Problem finden.

Das Mina Beispiel habe ich ja durchgearbeitet, allerdings wird dort ja auch nur ein Nachrichtentyp verwendet.

Ich lege ja zu Beginn des Programmes einen ProtocolCodecFilter für Server und Client fest.

die encode und doDecode Methoden werden ja automatisch aufgerufen, sobald Nachrichten verschickt bzw. empfangen werden, um den jeweilgen Encoder/Decoder auch zu nutzen. Nur weiß ich nicht wie ich dort die Typen unterscheiden kann, bzw. ob das der richtige Weg ist.

Danke...

Okay, dann will ich mal nicht so sein... :D

Das hier ist erstmal die richtige Doku die du komplett gelesen und verstanden haben solltest:

Apache MINA - Tutorial on ProtocolCodecFilter (for Mina 2.x)

Gleich danach solltest du dir dieses MINA Sample anschauen (ist über die MINA Doku verlinkt):

Index of /report/trunk/xref/org/apache/mina/example/sumup

Dort findest du zwei Packages:

* Codec
* Messages

Geh zuerst in Messages und schau dir AddMessageDecoder an. Du wirst sehen, dass diese Decoder-Klasse von "AbstractMessageDecoder" erbt. Im Konstruktur siehst du auch, dass ein Message-Typ deklariert wird. Dazu wird der super-Konstruktur, also der von AbstractMessageDecoder aufgerufen.

Die Dekoder-Klasse an sich hat als einzige weitere sinnvolle implementierung die Dekodierung der Nachricht.

Gehen wir also mal in die Superklasse "AbstractMessageDecoder"....

Dort finden wir den Konstruktor der den Message-Typ entgegen nimmt und ganz wichtig, eine Methode namens "decodable" ...

Diese Methode prüft ob diese Klasse diese Nachricht dekodieren kann.

Dazu wird der "Header" der Nachricht angeschaut. Passt der Message-Typ im Puffer zu dem eigenen, wird ein "OK" zurück gegeben...

MINA geht also he rund klappert beim Empfang von Daten alle Decoder-Klassen ab und sucht den passenden Dekoder.

Das geht aber nur, wenn MINA weiß welche Decoder-Klassen es gibt. Und das geschieht in der CodecFactoryKlasse namens "SumUpProtocolCodecFactory".

Wenn du diese Klasse ins leben rufen willst, musst du angeben ob du Server oder Client bist. Im Fall von SIMON macht das nicht komplett Sinn, da bei SIMON prinzipiell jede Message in jede Richtung geschickt werden kann. Aber bei dir wird das schon Sinn machen. Denn es wird sicher Pakete geben die nur der Server verarbeiten kann, und welche die nur der Client verarbeiten kann. Von daher wäre es in der Tat blödsinn immer alle vorhandenen Decoder abzuklappern, wenn man auf Serverseite (oder Clientseite) eh nur einen Teil davon braucht.

Wenn du jetzt also mehrere Message-Typen brauchst/anlegen willst, dann ist folgendes zu tun:

1) Message-Klasse anlegen und eine ID für diese Message festlegen
2) Encoder und Decoder-Klasse für diese Message-Klasse anlegen
3) In der ProtocolCodecFactory die Encoder- und Decoder-Klasse bekannt machen.

Fertig.
 

Raziell

Bekanntes Mitglied
Erstmal vielen Dank für deine ausführliche Antwort :) Mir ist jetzt schon einiges klarer.

Allerdings hatte ich bis jetzt leider noch keine Zeit weiter zu programmieren. Ich melde mich dann allerdings nochmal wenn ich wieder ein bisschen Zeit finde.

Danke
 
T

tuxedo

Gast
Die MINA Mailingliste (zu finden über die Webseite), ist übrigens auch ganz hilfreich. Da antworten die Entwickler von MINA direkt auf deine Fragen... Kompetentere Antworten als da wirst du anderswo nicht bekommen.

- Alex
 

Raziell

Bekanntes Mitglied
Guten Abend,
ich hätte da nochmal ne ganz dumme Frage und zwar, wie kann kann ich die De- und Encoder in der Factory registrieren?

Bei folgender implementierung:

Server:

Java:
acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new CodecFactory(true)));

Factory:
Java:
public class CodecFactory extends DemuxingProtocolCodecFactory {

	public CodecFactory(boolean server) {
		if (server) {
			super.addMessageDecoder(SystemMessageDecoder.class);
		}
	}

}

bekomme ich folgenden Fehler:

Java:
Exception in thread "main" java.lang.IllegalArgumentException: The specified class doesn't have a public default constructor.
	at org.apache.mina.filter.codec.demux.DemuxingProtocolDecoder.addMessageDecoder(DemuxingProtocolDecoder.java:92)
	at org.apache.mina.filter.codec.demux.DemuxingProtocolCodecFactory.addMessageDecoder(DemuxingProtocolCodecFactory.java:93)
	at de.server.protocol.CodecFactory.<init>(CodecFactory.java:11)
	at de.server.Server.main(Server.java:39)

Woran kann das liegen? Meine implementierung ist genauso wie im Beispiel von Mina.

Danke
 
T

tuxedo

Gast
Hmm, die Exception sagt ja schon alles: Der Konstruktor muss public sein und darf keine Parameter erwarten. Warum da das Sample nicht passt weiß ich jetzt auch grad nicht.

Hab die Sache bei mir mit dem setzen des Typs (client/server) über eine Setup-Methode realisiert:

http://svn.root1.de/svn/simon/trunk...mon/codec/base/SimonProtocolCodecFactory.java

In die FilterChain wird das ganze dann so eingebaut:

Java:
        protocolFactory.setup(true);
        acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(protocolFactory));
 
Zuletzt bearbeitet von einem Moderator:

Raziell

Bekanntes Mitglied
Hi,
mich wundert das ganze, weil Sie im Beispiel ja dann anscheind den gleichen Fehler haben müssten. Hat das denn bei denen keiner gemerkt :D

Wenn ich das so mache:

Java:
CodecFactory codecFactory = new CodecFactory();
codecFactory.setup(true);
acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(codecFactory));

Java:
public class CodecFactory extends DemuxingProtocolCodecFactory {
 
    public CodecFactory() {

    }
    
    public void setup(boolean server){
       if (server) {
            super.addMessageDecoder(SystemMessageDecoder.class);
       }
   }
 
}

Bekomme ich sobald ich setup() aufrufe, wieder genau den gleichen Fehler.

Hmmm ???:L iwas mache ich wohl falsch.
 
T

tuxedo

Gast
Ah, sorry. Hab die Exception nicht genau genug gelesen... Vergiss was ich oben geschrieben hab...

Deine Klasse SystemMessageDecoder hat keinen parameterlosen Default-Konstruktor. Wie schaut denn diese Klasse aus? Und was machst du da kom Konstruktor?
 

Raziell

Bekanntes Mitglied
Genau das dachte ich mir auch allerdings habe ich auch den Konstruktor exakt wie im Mina-Beispiel aufgbaut.

Java:
public SystemMessageDecoder() {
    super(Constants.SYSTEM_MESSAGE);
}
 
T

tuxedo

Gast
Hmm, *seltsam* Welche MINA Version verwendest du? Passt dein Classpath? Ist da die richtige Version dieser klasse drin?

Wenn's schon die aktuelle (2.0.2) ist und der Classpath stimmt, dann kannst du das Problem ja mal in der MINA Mailingliste posten...
 

Raziell

Bekanntes Mitglied
Kann ich momentan leider nicht sagen, müsste ich heute Abend mal schauen, wenn ich zu Hause bin.

Aber eigtl. sollte es die 2.0.2 sein, zumindest habe ich die runtergeladen :D

Wenn alles nix hilft werde ich wohl mal ne Mail schreiben müssen...

Danke
 
Zuletzt bearbeitet:

Raziell

Bekanntes Mitglied
Oh man als ich mir den Code nochmal angeschaut habe, sehe ich das da fälschlicherweise ein Parameter im Konstruktor vom MessageDecoder übergeben wird. Welcher Trottel war das nur :D

Danke
 

Raziell

Bekanntes Mitglied
Hallo,
ich habe nochmal ein paar Fragen, da es noch ein paar Unklarheiten meinerseits gibt.

AbstractMessage:
Zu welchem Zweck dient die Sequence hat das irgend einen tieferen Sinn?

AddMessage:
Der value ist jetz einfach irgendein Wert, den diese AddMessage hat. In meiner eigenen Implementierung könnte ich dann später die Variablen die ich für diese Message brauche definieren oder?

AbstractMessageDecoder:

Dort wird folgendes geprüft:

Java:
46      public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
47          // Return NEED_DATA if the whole header is not read yet.
48          if (in.remaining() < Constants.HEADER_LEN) {
49              return MessageDecoderResult.NEED_DATA;
50          }
51  
52          // Return OK if type and bodyLength matches.
53          if (type == in.getShort()) {
54              return MessageDecoderResult.OK;
55          }
56  
57          // Return NOT_OK if not matches.
58          return MessageDecoderResult.NOT_OK;
59      }

Die sequence ist ein Int und 4 Byte groß. Der type ist doch allerdings auch ein Int und müsste 4 Byte groß sein. Warum nehmen sie dort eine größe von 2?

Java:
28      public static final int TYPE_LEN = 2;
29  
30      public static final int SEQUENCE_LEN = 4;
31  
32      public static final int HEADER_LEN = TYPE_LEN + SEQUENCE_LEN;

AddMessageDecoder:

Java:
42          if (in.remaining() < Constants.ADD_BODY_LEN) {
43              return null;
44          }

Generell verstehe ich das mit header und body nicht so ganz, also wie definiere ich meinen body und meinen header? So wie ich Luist habe? Also beispielsweise 2 int's sind mein Header und ein String ist der body?

Hier ein ähnliches Problem, ich verstehe nicht wie sie auf die ADD_BODY_LEN von 4 kommen.
Ist das weil der value (Body?) ein Int mit der Größe von 4 Byte ist?

Was mache ich dann, wenn mein Body ein variable Länge hat, also wenn ich beispielsweise einen String nehme?

Danke schonmal an euch
 
Zuletzt bearbeitet:
T

tuxedo

Gast
Die Sequence ist nur nötig wenn du sie brauchst....

Vereinfacht gesagt:

Wenn du keine Sequence verwendest, und der Server dem Client auf eine Anfrage hin eine Antwort schickt, weißt du nicht ob die Antwort zur Frage passt. Denn der Client könnte ja schon weitere Fragen gestellt haben.

Im Fall von SIMON kann ich mit der Sequence-ID die Methodenaufrufe zuordnen. Der Server nimmt vom Client den Methodenaufruf entgegen, und antwortet, asynchron, mit dem result. Das Result-Paket hat die gleiche Sequence wie der Methodenaufruf. So kann der Client alles passend zuordnen.

Wenn du so eine Zuordnung nicht brauchst, kannst du die Sequence weglassen.

Die sequence ist ein Int und 4 Byte groß. Der type ist doch allerdings auch ein Int und müsste 4 Byte groß sein. Warum nehmen sie dort eine größe von 2?

Das ist so nicht richtig. Type sind 2 bytes, und sequence sind 4 bytes:
Java:
          // Return OK if type and bodyLength matches.
          if (type == in.getShort()) {
              return MessageDecoderResult.OK;
          }

byte = 1 byte
short = 2 bytes
integer = 4 bytes
long = 8 bytes

Da der Header aus dem Type und der Sequence besteht, macht das 2+4 bytes = 6 bytes für den Header.

Wenn du unbedingt noch traffik sparen willst (was nur bei langsamen Handyverbindungen noch Sinn macht), dann kannst du den Type auf ein byte runterkürzen (=256 Type-Möglichkeiten) und die Sequence von int auf short. Dann bist du bei 3 bytes im Header. Und wenn du die Sequence gar nicht brauchst, dann hast du nur 1 byte Header.
Generell verstehe ich das mit header und body nicht so ganz, also wie definiere ich meinen body und meinen header? So wie ich Luist habe? Also beispielsweise 2 int's sind mein Header und ein String ist der body?

Jepp, so wie du lust hast.

Du überlegst dir zuerst: Was brauch ich für Nachrichten? Dann definierst du für jeden Nachricht einen Typ (als byte, short, int, egal. Hauptsache du hast IDs festgelegt). Wenn du dann für jede Nachricht einen Encoder/Decoder anlegst, dann bist du eigentlich schon fertig.

Jeder Encoder weiß, wie er die zu transportierenden Information in bytes umformt (das musst du implementieren). Und jeder Dekoder weiß, wie er bytes in einem IoBuffer zu interpretieren hat und kann so aus einem Stall voll Bytes wieder Informationen herauslesen.

Was also im Body steht, und wie man es encodiert/dekodiert, hängt von der Nachrichtenart ab. Die eine Nachricht kann im Body Strings haben, die nächste braucht 32 Long-Werte. Eine "Login" Nachricht würde man wohl mit 2 Strings im Body versehen: Einmal Benutzername und einmal Passwort. Was im Body steht bestimmst du.

Ich hab das in SIMON noch etwas anders gelöst. Bei mir sind die Nachrichten nicht immer von gleicher Länge. Eine "Methodenaufrufnachricht" kann mal mehr oder weniger Daten transportieren (je nachdem was die Methode für Argumente hat). Für eine Nachrichtenart kann der Body also in der Länge variieren. Deshalb hab ich meinen Header noch um eine weitere Information erweitert: Wielange ist der Body?
Somit weiß ich, wenn ich den Header lese:

1 byte für die Nachrichtenart -> 1 byte
1 integer für die Sequence -> 4 byte
1 integer für die Länge des Bodys -> 4 byte

Ich muss also immer 9 bytes lesen. Dann weiß ich welchen Decoder ich brauche und wieviele Bytes ich noch lesen muss um die Nachricht komplett verarbeiten zu können.

Beim Encodieren weiß ich ja welche Informationen ich senden will. Ergo weiß ich schon die ID für die Nachrichtenart. Dann forme ich die Daten in bytes um und hab somit zum einen den Body, sowie seine Länge. Die Sequence verwalte ich über eine zentrale Instanz die einfach einen Integer immer weiter inkrementiert. Alles zusammen wird dann in passender Reihenfolge gesendet und gut ist.
Hier ein ähnliches Problem, ich verstehe nicht wie sie auf die ADD_BODY_LEN von 4 nehmen.
Ist das weil der value (Body?) Int mit der Größe von 4 Byte ist?

Jepp. Wie ich oben schon schrieb: Ein Integer ist 4 byte lang.

Ein String ist ggf. länger da der intern mit einem char abgebildet wird und char halt 2 bytes braucht. Bei Strings musst du etwas aufpassen. Da musst du in IoBuffer die "prefixedString..." Methode benutzen. Die schickt die Länge des String gleich mit. Kannst du aber auch in der Doku nachlesen.

Was mache ich dann, wenn mein Body ein variable Länge hat, also wenn ich beispielsweise einen String nehme?

Vor dem einschieben des Bodys in den zu sendenen IoBuffer messen/nachrechnen/nachschauen wieviele bytes es nun wirklich sind... Oder im Spezialfall String:

IoBuffer (Apache MINA 2.0.0-M3 API Documentation)
IoBuffer (Apache MINA 2.0.0-M3 API Documentation)

Gruß
Alex
 

Raziell

Bekanntes Mitglied
Vielen Dank für deine ausführliche Antwort.
Das hat bei mir jetzt schonmal sehr zum Verständnis beigetragen :)

Ich hoffe das ich im laufe des WEs mal ein bisschen Zeit finde das alles umzusetzen.

Danke
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
D Apache Mina Serial: Error Netzwerkprogrammierung 2
D Apache Mina und GWT Servlet Netzwerkprogrammierung 4
dayaftereh Fragen zu Apache Mina? Netzwerkprogrammierung 5
Kr0e Apache Mina -> await() Netzwerkprogrammierung 30
Kr0e Apache Mina Problem Netzwerkprogrammierung 2
D HTTP Apache-HttpClient/UNAVAILABLE (java 1.4) Netzwerkprogrammierung 18
Nuiton FTP Apache Commons: FTPClient und Sicherheit Netzwerkprogrammierung 9
N FTP FTP Client invalid IPv6 address (Apache Commons Net API) Netzwerkprogrammierung 6
G apache httpClient Problem. Netzwerkprogrammierung 5
M Apache HTTPClient Server log ausgeben ?! Netzwerkprogrammierung 3
N HTTP Apache 4.2.1 HttpClient 302 nach Login und auf den weiteren Seiten. Netzwerkprogrammierung 5
R Apache HttpClient File Download? Netzwerkprogrammierung 3
0 Apache Commons File Object bekommen Netzwerkprogrammierung 4
W HTTP Apache HttpComponents und GZIP Netzwerkprogrammierung 2
F Apache commons net SFTPClient Netzwerkprogrammierung 5
D Apache CXF, JAX-WS Problem bei Arrays - einfacher Server Netzwerkprogrammierung 2
M Apache Solr doc & pdf Upload Netzwerkprogrammierung 8
M need org.apache.commons.httpclient.* Netzwerkprogrammierung 8
C apache commons net ftp bei upload unvollständig Netzwerkprogrammierung 3
R HTTP Apache HTTP Client: Request mit angehängter Datei Netzwerkprogrammierung 2
N SFTP apache keine Verbindungaufbau möglich Netzwerkprogrammierung 6
K Login via apache httpclient Netzwerkprogrammierung 4
T HTTP Apache Commons HttpClient Bibliothek Netzwerkprogrammierung 2
B Tomcat Apache Server Netzwerkprogrammierung 6
lordcarlos HTTP Apache HttpClient, post und login. Netzwerkprogrammierung 2
J org.apache.http.auth.NTCredentials Netzwerkprogrammierung 2
A org.apache.commons http client in Netbeans einbinden Netzwerkprogrammierung 3
T Apache HttpClient & Default Headers Netzwerkprogrammierung 9
T apache HTTPClient einloggen Netzwerkprogrammierung 2
G apache von außen zugänglich machen Netzwerkprogrammierung 5
1 Upload problem! org.apache.commons.net.ftp Netzwerkprogrammierung 3
Q HTTPS mit Apache HttpClient Netzwerkprogrammierung 4
S Google Search Webservice mit Apache Axis realisieren? Netzwerkprogrammierung 2
I Apache http-client: Problem beim Proxyaufruf Netzwerkprogrammierung 2
S Applet und JWS auf Apache-Axis (SOAP) Netzwerkprogrammierung 8
C HTTPS mit Apache HTTPClient Netzwerkprogrammierung 1
M org.apache.commons.httpclient.HttpClient Netzwerkprogrammierung 3
J Antwort eines Soaprequests parsen mittels org.apache.soap Netzwerkprogrammierung 2
B Via Java Datei zu PHP-Script auf Apache hochladen Netzwerkprogrammierung 4
A http request per socket an apache server Netzwerkprogrammierung 5

Ähnliche Java Themen

Neue Themen


Oben