![]() |
Delphi-Version: 2010
Netzwerkprotokoll zum Übertragen von Variant Streams
Hallo zusammen,
nun da ich das Problem mit TVarRec / Variant gelöst habe, presentiere ich hier mal die vorläufige Version des Protokolls. Die Unit besteht aus zwei Klassen: Der Encoder dient zum Erstellen des Pakets. Über die Methode Append() können Daten angehangen werden. Der Decoder kann Datensätze anhand ihrer Indizes aus dem fertigen Paket auslesen. Die Daten werden als Variant zurückgegeben. Praktisch könnte man also sagen, es handelt sich um eine Art Stream für Variants. :arrow: Der Code sieht etwas umständlich aus. Meine Frage daher, ob jemand spontan noch Verbesserungsvorschläge hat. Insbesondere auch zur Performance. :arrow: Achso, vielleicht noch zum generellen Aufbau des Protokolls. Vor jedem Datensatz wird ein Byte geschrieben, welches den Typ der folgenden Daten enthält. Bei Strings folgt diesem Typ Identifier zusätzlich noch ein 4 Byte Cardinal Wert, welcher die Länge des Strings beinhaltet. Danach folgen die eigentlichen Daten.
Delphi-Quellcode:
Hier noch ein kurzes Beispiel, wie die Klassen verwendet werden:
unit dxVariantStream;
interface uses Windows; type TdxVariantEncoder = class(TObject) private FData: Pointer; FSize: Int64; protected function CalculateDataSize(V: Variant): Cardinal; function WriteValue(Offset: Cardinal; V: Variant): Cardinal; public procedure Append(Value: Variant); overload; procedure Append(Values: array of Variant); overload; constructor Create; destructor Destroy; override; property Data: Pointer read FData; property Size: Int64 read FSize; end; TdxVariantDecoder = class(TObject) private FIndexMap: array of Cardinal; FItemCount: Integer; FData: Pointer; protected function GetVariantItem(I: Integer): Variant; public constructor Create(Data: Pointer; Size: Int64); destructor Destroy; override; property Items[Index: Integer]: Variant read GetVariantItem; default; property ItemCount: Integer read FItemCount; end; implementation uses dxException; type ECommandParserException = class(Exception); { TCommandEncoder } procedure TdxVariantEncoder.Append(Value: Variant); var Offset: Cardinal; begin Offset := FSize; FSize := FSize + CalculateDataSize(Value); if Assigned(FData) then begin ReallocMem(FData, FSize); end else begin GetMem(FData, FSize); end; WriteValue(Offset, Value); end; procedure TdxVariantEncoder.Append(Values: array of Variant); var BufferSize, Offset: Cardinal; I: Integer; begin BufferSize := 0; for I := Low(Values) to High(Values) do begin Inc(BufferSize, CalculateDataSize(Values[I])); end; Offset := FSize; FSize := FSize + BufferSize; if Assigned(FData) then begin ReallocMem(FData, FSize); end else begin GetMem(FData, FSize); end; for I := Low(Values) to High(Values) do begin Inc(Offset, WriteValue(Offset, Values[I])); end; end; constructor TdxVariantEncoder.Create; begin inherited Create; FSize := 0; end; destructor TdxVariantEncoder.Destroy; begin if Assigned(FData) then begin FreeMem(FData); end; inherited; end; function TdxVariantEncoder.WriteValue(Offset: Cardinal; V: Variant): Cardinal; var VarData: TVarData; begin Result := 0; VarData := TVarData(V); case VarData.VType of varSmallInt: begin PByte(Cardinal(FData) + Offset)^ := $0; PSmallInt(Cardinal(FData) + Offset + 1)^ := VarData.VSmallInt; Result := 1 + SizeOf(SmallInt); end; varInteger: begin PByte(Cardinal(FData) + Offset)^ := $1; PInteger(Cardinal(FData) + Offset + 1)^ := VarData.VInteger; Result := 1 + SizeOf(Integer); end; varSingle: begin PByte(Cardinal(FData) + Offset)^ := $2; PSingle(Cardinal(FData) + Offset + 1)^ := VarData.VSingle; Result := 1 + SizeOf(Single); end; varDouble: begin PByte(Cardinal(FData) + Offset)^ := $3; PDouble(Cardinal(FData) + Offset + 1)^ := VarData.VDouble; Result := 1 + SizeOf(Double); end; varCurrency: begin PByte(Cardinal(FData) + Offset)^ := $4; PCurrency(Cardinal(FData) + Offset + 1)^ := VarData.VCurrency; Result := 1 + SizeOf(Currency); end; varDate: begin PByte(Cardinal(FData) + Offset)^ := $5; PDateTime(Cardinal(FData) + Offset + 1)^ := VarData.VDate; Result := 1 + SizeOf(TDateTime); end; varBoolean: begin PByte(Cardinal(FData) + Offset)^ := $6; PWordBool(Cardinal(FData) + Offset + 1)^ := VarData.VBoolean; Result := 1 + SizeOf(WordBool); end; varShortInt: begin PByte(Cardinal(FData) + Offset)^ := $7; PShortInt(Cardinal(FData) + Offset + 1)^ := VarData.VShortInt; Result := 1 + SizeOf(ShortInt); end; varByte: begin PByte(Cardinal(FData) + Offset)^ := $8; PByte(Cardinal(FData) + Offset + 1)^ := VarData.VByte; Result := 1 + SizeOf(Byte); end; varWord: begin PByte(Cardinal(FData) + Offset)^ := $9; PWord(Cardinal(FData) + Offset + 1)^ := VarData.VWord; Result := 1 + SizeOf(Word); end; varLongWord: begin PByte(Cardinal(FData) + Offset)^ := $A; PLongWord(Cardinal(FData) + Offset + 1)^ := VarData.VLongWord; Result := 1 + SizeOf(LongWord); end; varInt64: begin PByte(Cardinal(FData) + Offset)^ := $B; PInt64(Cardinal(FData) + Offset + 1)^ := VarData.VInt64; Result := 1 + SizeOf(Int64); end; varUInt64: begin PByte(Cardinal(FData) + Offset)^ := $C; PUInt64(Cardinal(FData) + Offset + 1)^ := VarData.VUInt64; Result := 1 + SizeOf(UInt64); end; varString: begin PByte(Cardinal(FData) + Offset)^ := $D; Result := Length(AnsiString(VarData.VString)) * SizeOf(AnsiChar); PCardinal(Cardinal(FData) + Offset + 1)^ := Result; CopyMemory(Pointer(Cardinal(FData) + Offset + 5), VarData.VString, Result); Inc(Result, 5); end; varUString: begin PByte(Cardinal(FData) + Offset)^ := $E; Result := Length(AnsiString(VarData.VUString)) * SizeOf(WideChar); PCardinal(Cardinal(FData) + Offset + 1)^ := Result; CopyMemory(Pointer(Cardinal(FData) + Offset + 5), VarData.VUString, Result); Inc(Result, 5); end; end; end; function TdxVariantEncoder.CalculateDataSize(V: Variant): Cardinal; var VarData: TVarData; begin Result := 0; VarData := TVarData(V); case VarData.VType of varSmallInt: Result := 1 + SizeOf(SmallInt); varInteger: Result := 1 + SizeOf(Integer); varSingle: Result := 1 + SizeOf(Single); varDouble: Result := 1 + SizeOf(Double); varCurrency: Result := 1 + SizeOf(Currency); varDate: Result := 1 + SizeOf(TDateTime); varBoolean: Result := 1 + SizeOf(WordBool); varShortInt: Result := 1 + SizeOf(ShortInt); varByte: Result := 1 + SizeOf(Byte); varWord: Result := 1 + SizeOf(Word); varLongWord: Result := 1 + SizeOf(LongWord); varInt64: Result := 1 + SizeOf(Int64); varUInt64: Result := 1 + SizeOf(UInt64); varString: Result := 5 + Length(AnsiString(VarData.VString)) * SizeOf(AnsiChar); varUString: Result := 5 + Length(AnsiString(VarData.VUString)) * SizeOf(WideChar); end; end; { TCommandDecoder } constructor TdxVariantDecoder.Create(Data: Pointer; Size: Int64); var Offset: Cardinal; begin inherited Create; FData := Data; FItemCount := 0; Offset := 0; while (Offset < Size) do begin Inc(FItemCount); SetLength(FIndexMap, FItemCount); FIndexMap[FItemCount - 1] := Offset; case PByte(Cardinal(FData) + Offset)^ of $0: Inc(Offset, 1 + SizeOf(SmallInt)); $1: Inc(Offset, 1 + SizeOf(Integer)); $2: Inc(Offset, 1 + SizeOf(Single)); $3: Inc(Offset, 1 + SizeOf(Double)); $4: Inc(Offset, 1 + SizeOf(Currency)); $5: Inc(Offset, 1 + SizeOf(TDateTime)); $6: Inc(Offset, 1 + SizeOf(WordBool)); $7: Inc(Offset, 1 + SizeOf(ShortInt)); $8: Inc(Offset, 1 + SizeOf(Byte)); $9: Inc(Offset, 1 + SizeOf(Word)); $A: Inc(Offset, 1 + SizeOf(LongWord)); $B: Inc(Offset, 1 + SizeOf(Int64)); $C: Inc(Offset, 1 + SizeOf(UInt64)); $D: Inc(Offset, 5 + PCardinal(DWord(FData) + Offset + 1)^); $E: Inc(Offset, 5 + PCardinal(DWord(FData) + Offset + 1)^); end; end; end; destructor TdxVariantDecoder.Destroy; begin inherited; end; function TdxVariantDecoder.GetVariantItem(I: Integer): Variant; var DataType: Byte; VarData: TVarData; A: AnsiString; W: WideString; Size: Cardinal; begin if (not (I in [Low(FIndexMap) .. High(FIndexMap)])) then begin raise ECommandParserException.Create('Data table index out of bounds.'); end; DataType := PByte(DWord(FData) + FIndexMap[I])^; case DataType of $0: Result := PSmallInt(Cardinal(FData) + FIndexMap[I] + 1)^; $1: Result := PInteger(Cardinal(FData) + FIndexMap[I] + 1)^; $2: Result := PSingle(Cardinal(FData) + FIndexMap[I] + 1)^; $3: Result := PDouble(Cardinal(FData) + FIndexMap[I] + 1)^; $4: Result := PCurrency(Cardinal(FData) + FIndexMap[I] + 1)^; $5: Result := PDateTime(Cardinal(FData) + FIndexMap[I] + 1)^; $6: Result := PWordBool(Cardinal(FData) + FIndexMap[I] + 1)^; $7: Result := PShortInt(Cardinal(FData) + FIndexMap[I] + 1)^; $8: Result := PByte(Cardinal(FData) + FIndexMap[I] + 1)^; $9: Result := PWord(Cardinal(FData) + FIndexMap[I] + 1)^; $A: Result := PLongWord(Cardinal(FData) + FIndexMap[I] + 1)^; $B: Result := PInt64(Cardinal(FData) + FIndexMap[I] + 1)^; $C: Result := PUInt64(Cardinal(FData) + FIndexMap[I] + 1)^; $D: begin VarData.VType := varString; Size := PCardinal(Cardinal(FData) + FIndexMap[I] + 1)^; SetLength(A, Size div SizeOf(AnsiChar)); CopyMemory(@A[1], Pointer(Cardinal(FData) + FIndexMap[I] + 5), Size); Result := A; end; $E: begin VarData.VType := varUString; Size := PCardinal(Cardinal(FData) + FIndexMap[I] + 1)^; SetLength(W, Size div SizeOf(WideChar)); CopyMemory(@W[1], Pointer(Cardinal(FData) + FIndexMap[I] + 5), Size); Result := W; end; end; end; end.
Delphi-Quellcode:
Viele Grüße
procedure TForm6.Button1Click(Sender: TObject);
var Encoder: TdxVariantEncoder; Decoder: TdxVariantDecoder; begin Encoder := TdxVariantEncoder.Create; try Encoder.Append([1, -120000, 'testwide', AnsiString('testansi'), false]); Encoder.Append(Word(2)); Encoder.Append([Int64(33), 123.444]); Decoder := TdxVariantDecoder.Create(Encoder.Data, Encoder.Size); try ShowMessage(Decoder[3]); // testansi ShowMessage(Decoder[7]); // 123.444 finally Decoder.Free; end; finally Encoder.Free; end; end; Zacherl |
AW: Netzwerkprotokoll zum Übertragen von Variant Streams
push
|
AW: Netzwerkprotokoll zum Übertragen von Variant Streams
Ohne mir den Source genau anzuschauen kann ich dir schon sagen, dass du dir mit so einem Protokoll keinen Gefallen tust, wenn du mal verschiedene Versionen miteinander kompatibel machen willst. Sobald sich irgendwelche Internas ändern (wie zum Beispiel mit Unicode geschehen, oder wie mit NativeInt unter verschiedenen Systemen geschehen wird) ist es nicht mehr zu gebrauchen, außer du bringst beide Seiten wieder auf einen Stand.
|
AW: Netzwerkprotokoll zum Übertragen von Variant Streams
Das ist kein Problem. Ansi und Unicode Strings werden differenziert. Ebenso bleiben bei den 64 Bit Delphi Versionen die Datentypen wie Integer, etc. ja 4 Byte groß. Die neuen NativeInt Typen kann ich einfach nachrüsten.
Wobei ich diese eh nicht verwenden werde, wenn eine Seite noch für 32 Bit kompiliert wurde. Insofern müsste ich eh alles neu builden. |
AW: Netzwerkprotokoll zum Übertragen von Variant Streams
Sofern du dich auf Delphi auf beiden Seiten festlegen willst, und gegenüber möglichen Änderungen deiner Daten (neues Feld hinzugekommen, Datentyp hat sich geändert) anfällig sein möchtest, ist das sicherlich ganz brauchbar. Ich kann aber aus Erfahrung sagen, dass solch ein Protokoll die schlimmste Krätze sein kann, wenn man mit unterschiedlichen Versionen kompatibel sein möchte.
|
AW: Netzwerkprotokoll zum Übertragen von Variant Streams
Das glaube ich gerne, habe da auch ein wenig Erfahrung mit. Das Protokoll ist allerdings für ein kleines Projekt entstanden, bei dem beide Seiten mit Delphi programmiert sind. Im Prinzip ging es mir nur um einfachen Zugriff (ohne ständige Typkonvertierungen) und möglichst wenig Protokoll Overhead.
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:09 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz