Heiner KückerRemote Protocol |
|
Home Java-Seite Bit Packed Array ASM Improved heterogene Map, HMap Constraint Code Generator JSP WorkFlow PageFlow FlowControl Page Flow Engine Web Flow Engine Control_and_Command JSP_Spreadsheet Code-Generator für Option-Either-Stil in Java verbesserter Comparator Fluent-Interface Code-Generator auf Basis einer Grammatik Visitor mit Multidispatch for-Schleife mit yield-return Kognitions-Maschine semantisches Netz Domain Parser Codegenerator_für hierarchische Datenstrukturen Expression_Engine Formula_Parser Thread Preprocessor State Transition Engine AspectJ Java_Explorer DBF_Library Kalender_Applet SetGetGen BeanSetGet CheckPackage LineNumbers GradDms Excel-Export StringTokenizer JspDoc JspCheck JSP-Schulung Java Server Pages Struts Ascii-Tabellen- Layouter Ascii-Baum- Layouter Ascii-Art-Fluss- Diagramm- Parser AsciiArt AssignmentMatrix Layouter StringSerial Silbentrennung JDBC_Schlüssel- Generierung bidirektional/ unidirektional gelinkte Liste Java_Sitemap Generator XmlBuilder RangeMap StringFormatter VersionSafe XCopy JTextField CommandLine- ParamReader Bitmap-Grafik MultiMarkable- Buffered- InputStream JavaCache JdomUtil CollectionUtil XML Really Pull Parser Log-Filter Remote-Protokoll Sudoku-Generator Delegation statt Mehrfachvererbung Disjunct Interval Set WebCam_Demo Weiterentwicklung_Java Alaska-XBase++-Seite Projekte Philosophien Techniken Konzepte Sudoku Kontakt / Impressum Links SiteMap Letzte Aktualisierung: 10.05.2007 |
Remote Protocol Serialisierung und Deserialisierung von Java-Objekten
Serialisierung und Deserialisierung von Java-Objekten zur Übertragung über Socket mit Streams oder Java-NIO. Das Remote Client-Server-Protokoll ---------------------------------- Ziel ist es, ein performantes Übertragungsprotokoll zwischen dem einem Remote Client und Server festzulegen. Dabei soll auch ein Server, der in PHP geschrieben wurde, unterstützt werden. Allgemeines ----------- Die Standard-Serialisierung/-Deserialsierung in Java erzeugt sehr grosse Datenmengen. So wird zum Beispiel ein java.util.Date in 82 Byte serialsiert, wobei nur 8 Byte (das interne long time) Nutzdaten sind. Durch das hier gezeigte Protokoll soll diese Datenmenge reduziert werden, zum Beispiel soll ein java.util.Date nur zu seinen 8 Byte Nutzlast und zusätzlich einem Byte für die Erkennung des Datentyps serialsiert werden. TODO Ein Datum ohne Zeitangabe könnte sogar in 3 Byte Nutzlast serialsiert werden, 5 Bit für den Tag im Monat (1 bis 31), 4 Bit für den Monat (1 bis 12). Dann bleiben noch 15 Bit für das Jahr, was den Bereich von 0 bis 32768 bzw von -16383 bis -16384 erlaubt. Dies sollte für alle kaufmännischen Probleme reichen. Ich verstehe also das Jahr-2000-Problem nicht, es hätte nicht auftreten müssen. Zum Test des Protokolls habe ich einen Java-NIO-Server (ProtocolServerNonBlocked) und einen Client (ProtocolClient) geschrieben. Dieser Code kam nie produktiv zum Einsatz und wurde nie intensiv getestet. Der Protokoll-Umschlag ---------------------- Da das TCP/IP-Protokoll keine Information liefert, wann ein gesendeter Datenblock vollständig ist, muss eine Längen-Information mitgesendet werden. Die ersten vier Byte der Nachricht stellen die Längen-Information dar. Das höchstwertigste Byte wird als erstes gesendet. Dadurch ist die Grösse eines Datenblockes auf 4 GigaByte begrenzt, was ausreichen sollte. Nach den vier Byte für die Länge werden zwei Byte für die gewünschte Operation gesendet. Diese beiden Bytes gehören schon zur Gesamt-Information und werden bei der Länge mitgezählt. Das erlaubt es 65536 Operationen zu definieren. In den Client- und Server-Programmen werden diese Nummern auf benannte Operationen gemappt. Nach dem Operations-Code folgt ein Byte mit der Anzahl der Parameter. Dadurch ist die Anzahl der Parameter auf 255 beschränkt. Durch die Angabe der Parameter-Anzahl ist es möglich, die gleiche Operation mit unterschiedlichen Parametern aufzurufen. Werden mehr Parameter benötigt, müssen diese in Arrays verpackt werden. Achtung: das höchstwertigste Bit der Parameter-Anzahl wird als Signal benutzt, ob die gesamten Parameter komprimiert sind (GZIP). Negative Parameter-Anzahlen gelten als komprimiert. Dadurch reduziert sich die Anzahl möglicher Parameter auf 127. Diese Lösung ist notwendig, weil sich mit einem GZIP-Umschlag nur ein einzelner Parameter komprimieren lässt. Danach folgen die Parameter der Operation. Beim Response wird der Operations-Code zurückgesendet. Dies ist zwar redundant, wird aber bei evtl. später zu implementierenden asynchronen Antworten benötigt. Die Response enthält ebenfalls ein Byte für die Anzahl der Rückgabe-Werte mit dem höchstwertigen Bit als Markierung für die Komprimierung der Rückgabe-Werte. Request: 4 - 5 6 - 9 10 - 11 12 13 - 0 - 1 2 - 5 6 - 7 8 9 +------------------+-------------------+-------------------+----------------------+------------------------+--------------------+ | Byte 0 - 3 Länge | 2 Byte Request-ID | 4 Byte Session-ID | Byte 10 - 11 OP-Code | Byte 6 Parameter-Anzahl | Parameter-Daten | +------------------+-------------------+-------------------+----------------------+------------------------+--------------------+ |<--------------------------------------------- Länge des Request ------------------------------------------>| Response: 4 - 5 6 - 9 10 - 11 12 13 - 0 - 1 2 - 5 6 - 7 8 9 +------------------+-------------------+-------------------+----------------------+------------------------------+--------------------+ | Byte 0 - 3 Länge | 2 Byte Request-ID | 4 Byte Session-ID | Byte 10 - 11 OP-Code | Byte 6 Rückgabe-Werte-Anzahl | Rückgabe-Daten | +------------------+-------------------+-------------------+----------------------+------------------------------+--------------------+ |<---------------------------------------------- Länge der Response ---------------------------------------------->| 2 Byte für eine Request-ID TODO Nachfrage: Session-Id 4 Byte im Header mitsenden ? (Kann für anonyme Request's ausgenullt werden) Der gesamte Request und Response wird verschlüsselt. Bei jedem Request und Response wird der positionsabhängige Verschlüsseler neu gestartet, um ein aus dem Takt (Synchronisation) laufen der Ver- und Entschlüsseler zu vermeiden. Offene Frage: soll die Längen-Information sowie Request-ID und Session-ID mit verschlüsselt werden ? Bei Bekanntsein dieser Tatsache könnte dadurch das Knacken der Verschlüsselung erleichtert werden. Verwendung der Serialisierungs-Formate für die Persistierung ------------------------------------------------------------ Die Serialisierungs-Formate sind unverändert zum Abspeichern von Konfigurationsdaten oder temporär zu persistierender Daten (persistenter Client-Cache) geeignet. Dabei ist aber immer eine serielle Arbeitsweise erforderlich. Für die Arbeit mit wahlfreiem Zugriff zur RAM- Einsparung müssen alternative Formate mit festen Längen verwendet werden (oder besser eine Embedded-DB wie Derby oder db4o). Zum Absichern der Versions-Migration (zusätzliche Felder bei neuen Programm-Versionen) ist ein Versions-Header erforderlich. Dazu können weitere fachliche Informationen (Kopf- Informationen like-DBASE oder Inhalts-Informationen (Datentyp semantisch) ) kommen. Das Abspeichern der Body-Length ist beim Persistieren im Datei-System nicht nötig. +-------------------------------------------------+-------------+ | Versions-Nummer (2 Byte, höchstwertiges zuerst) | Daten-Bytes | +-------------------------------------------------+-------------+ Datentypen ========== Jeder Parameter- und Rückgabewert wird durch das erste Byte in seinem Type identifiziert. Bei Notwendigkeit wird dieser Type durch weitere Bytes genauer spezifiert. Typen mit implizitem Wert ------------------------- Diese Typen führen keinen Wert mit, da der Wert im Typ schon enthalten ist. n - Null (char 110) t - true (char 116) f - false (char 102) Elementare Datentypen --------------------- -Byte vorzeichenbehaftete 8-Bit-Ganzzahl +-------------+------------+ | b (char 98) | Daten-Byte | +-------------+------------+ -Short vorzeichenbehaftete 16-Bit-Ganzzahl +--------------+---------------------+-----------------------+ | s (char 115) | Höchstwertiges Byte | Niederwertigstes Byte | +--------------+---------------------+-----------------------+ -Integer: 32-bit Integer +--------------+---------------------+--------+--------+-----------------------+ | i (char 105) | Höchstwertiges Byte | Byte 1 | Byte 2 | Niederwertigstes Byte | +--------------+---------------------+--------+--------+-----------------------+ -Long: 64-bit Long +--------------+---------------------+-----------------+-----------------------+ | l (char 108) | Höchstwertiges Byte | Byte 1 - Byte 6 | Niederwertigstes Byte | +--------------+---------------------+-----------------+-----------------------+ -komprimiertes Short, komprimiertes Integer und komprimiertes Long Es ist möglich, dass der Wert eines int in den Wertebereich von byte oder short passt. Es ist möglich, dass der Wert eines long in den Wertebereich von byte, int oder short passt. Für diesen Fall werden nur die jeweils notwendigen Bytes serialisiert. Beim Deserialisieren muss aber wieder der originale Typ bereitgestellt werden, um Probleme mit unterschiedlichen Klassen (ClassCastException, ArrayStoreException, Fehlschlagen von instanceof) zu vermeiden. Typ | Speicher-Typ | Prefix-Code -------+--------------+------------- short | byte (8) | 1 int | byte (8) | 2 int | short (16) | 3 long | byte (8) | 4 long | short (16) | 5 long | int (32) | 6 -Float 4-Byte (32 Bit) Gleitkommazahl: Code 'F', weil 'f' schon für false verwendet wird +--------------+---------------------------------------------------+ | F (char 70) | 4 Byte im IEEE 754 floating-point "single format" | +--------------+---------------------------------------------------+ -Double 8-Byte (64 Bit) Gleitkommazahl: +--------------+------------------------------------------+ | d (char 100) | 8 Byte im IEEE 754 floating-point format | +--------------+------------------------------------------+ Konvertierung in Java Double.longBitsToDouble(long bits) Double.doubleToLongBits(double value) Konvertierung in PHP pack( "d", Download der Quelldateien REMOTE_PROTOCOL.zip Installation: Anlegen eines Projektes in der IDE Ihrer Wahl. Start mit ProtocolServerBlocked#main bzw. ProtocolServerNonBlocked#main bzw. TestServer#main (Server unbedingt immer zuerst starten) und dananch ProtocolClient#main bzw. TestClient#main.
Achtung: Erweiterungen und Fixes stelle ich ohne Historie
und ohne Ankündigung hier bereit. Lizenzbedingungen:
Die Programme, Quelltexte und Dokumentationen können ohne
irgendwelche Bedingungen kostenlos verwendet werden. |