BSOne.SFC/Tocsg.Lib/VCL/CS/Tocsg.Packet.pas

601 lines
17 KiB
Plaintext

{*******************************************************}
{ }
{ Tocsg.Packet }
{ }
{ Copyright (C) 2022 kku }
{ }
{*******************************************************}
unit Tocsg.Packet;
interface
uses
Tocsg.Obj, Tocsg.Exception, System.SysUtils, Winapi.Windows,
superobject, System.Classes;
const
SIZE_INTEGER = SizeOf(Integer);
CTX_PACKET_SIGNATURE: AnsiString = '@OC$.G';
LEN_CTX_PACKET_SIGNATURE = 6;
type
// Server -> Client
PCtxPacketHeader = ^TCtxPacketHeader;
TCtxPacketHeader = packed record
sSig: array [0..5] of AnsiChar; // #SPK.$$
wRank: WORD;
dwId,
dwSize: DWORD;
end;
// 종류 추가 되면 패킷 해더의 wRank 수정해야함
TTgPacketKind = (pkNormal, pkFileQueue, pkIgnore, pkCritical);
TTgPacketHeader = record
Cmd,
Result: Integer;
Toss,
Handle,
WndMessage: LONGLONG;
ResultMsg: String;
PacketKind: TTgPacketKind;
end;
TSnTextEncoding = System.SysUtils.TEncoding;
TSnBytes = TBytes;
ETgPacket = class(ETgException);
ITgPacket = interface
['{5F9E8D1B-7371-41D2-AA7A-E8DDAE3788B9}']
function _GetPacketHeader: TTgPacketHeader;
function _GetJsonObject: ISuperObject;
function ToJsonString: String;
function ToBytes(var SnBytes: TSnBytes): Integer;
function ToBytesDataOnly(var SnBytes: TSnBytes): Integer;
function SaveToFile(sPath: String): Boolean;
end;
ISendPacket = interface(ITgPacket)
['{0322E57A-C699-40BE-B0DF-64B9A69A5701}']
function GetCommand: Integer;
procedure SetPacketKind(aPacketKind: TTgPacketKind);
function GetPacketKind: TTgPacketKind;
procedure SetHandle(llHandle: LONGLONG);
procedure SetWndMessage(llWndMessage: LONGLONG);
procedure SetToss(llToss: LONGLONG);
procedure SetResult(nResult: Integer);
procedure SetResultMsg(const sResultMsg: String);
function GetResult: Integer;
function GetResultMsg: String;
function GetSocket: TObject;
procedure SetSocket(aSocket: TObject);
function GetO(const sPath: String): ISuperObject;
procedure PutO(const sPath: String; const Value: ISuperObject);
procedure PutB(const sPath: String; Value: Boolean);
procedure PutI(const sPath: String; Value: SuperInt);
procedure PutC(const sPath: String; Value: Currency);
procedure PutD(const sPath: String; Value: Double);
procedure PutS(const sPath: String; const Value: String);
property Command: Integer read GetCommand;
property Handle: LONGLONG write SetHandle;
property WndMessage: LONGLONG write SetWndMessage;
property Toss: LONGLONG write SetToss;
property Result: Integer read GetResult write SetResult;
property ResultMsg: String read GetResultMsg write SetResultMsg;
property PacketKind: TTgPacketKind read GetPacketKind write SetPacketKind;
property Socket: TObject read GetSocket write SetSocket;
property O[const sPath: String]: ISuperObject read GetO write PutO; default;
property B[const sPath: String]: Boolean write PutB;
property I[const sPath: String]: SuperInt write PutI;
property D[const sPath: String]: Double write PutD;
property C[const sPath: String]: Currency write PutC;
property S[const sPath: String]: String write PutS;
end;
IRcvPacket = interface(ITgPacket)
['{C1BFCE0F-1B34-4FCC-84AC-4F24CA6D72A5}']
function GetCommand: Integer;
function GetPacketKind: TTgPacketKind;
function GetHandle: LONGLONG;
function GetWndMessage: LONGLONG;
function GetToss: LONGLONG;
function GetResult: Integer;
function GetResultMsg: String;
function GetSocket: TObject;
function GetRcvPacketSize: DWORD;
function ToJsonString: String;
function GetO(const sPath: String): ISuperObject;
function GetB(const sPath: String): Boolean;
function GetI(const sPath: String): SuperInt;
function GetD(const sPath: String): Double;
function GetC(const sPath: String): Currency;
function GetS(const sPath: String): String;
function GetA(const sPath: String): TSuperArray;
property Command: Integer read GetCommand;
property PacketKind: TTgPacketKind read GetPacketKind;
property Handle: LONGLONG read GetHandle;
property WndMessage: LONGLONG read GetWndMessage;
property Toss: LONGLONG read GetToss;
property Result: Integer read GetResult;
property ResultMsg: String read GetResultMsg;
property Socket: TObject read GetSocket;
property RcvPacketSize: DWORD read GetRcvPacketSize;
property O[const sPath: String]: ISuperObject read GetO; default;
property B[const sPath: String]: Boolean read GetB;
property I[const sPath: String]: SuperInt read GetI;
property D[const sPath: String]: Double read GetD;
property C[const sPath: String]: Currency read GetC;
property S[const sPath: String]: String read GetS;
property A[const sPath: String]: TSuperArray read GetA;
end;
TTgPacket = class(TInterfacedObject, ITgPacket, ISendPacket, IRcvPacket)
protected
Socket_: TObject;
PacketHeader_: TTgPacketHeader;
SuperObject_: ISuperObject;
dwRcvLen_: DWORD;
function _GetPacketHeader: TTgPacketHeader;
function _GetJsonObject: ISuperObject;
procedure PutO(const sPath: String; const Value: ISuperObject);
procedure PutB(const sPath: String; Value: Boolean);
procedure PutI(const sPath: String; Value: SuperInt);
procedure PutC(const sPath: String; Value: Currency);
procedure PutD(const sPath: String; Value: Double);
procedure PutS(const sPath: String; const Value: String);
function GetO(const sPath: String): ISuperObject;
function GetB(const sPath: String): Boolean;
function GetI(const sPath: String): SuperInt;
function GetD(const sPath: String): Double;
function GetC(const sPath: String): Currency;
function GetS(const sPath: String): String;
function GetA(const sPath: String): TSuperArray;
function GetCommand: Integer;
procedure SetPacketKind(aPacketKind: TTgPacketKind);
function GetPacketKind: TTgPacketKind;
procedure SetHandle(llHandle: LONGLONG);
function GetHandle: LONGLONG;
procedure SetWndMessage(llWndMessage: LONGLONG);
function GetWndMessage: LONGLONG;
procedure SetToss(llToss: LONGLONG);
function GetToss: LONGLONG;
procedure SetResult(nResult: Integer);
function GetResult: Integer;
procedure SetResultMsg(const sResultMsg: String);
function GetResultMsg: String;
function GetSocket: TObject;
procedure SetSocket(aSocket: TObject);
function GetRcvPacketSize: DWORD; // for IRcvPacket
public
Constructor Create; overload;
Constructor Create(const aCmd: Integer; PacketKind: TTgPacketKind = pkNormal); overload;
Constructor Create(aPacket: ITgPacket; PacketKind: TTgPacketKind = pkNormal); overload;
Constructor Create(const aRcvData: Pointer; nRcvLen: Integer); overload;
Constructor Create(const aRcvData: TSnBytes; bIncludeLen: Boolean = false; aSocket: TObject= nil{for ClientContext}); overload;
Constructor Create(aSocket: TObject{for ClientContext}; const aRcvData: Pointer; nRcvLen: Integer); overload;
Constructor Create(const sJsonData: String); overload;
function ToJsonString: String;
function ToBytes(var SnBytes: TSnBytes): Integer;
function ToBytesDataOnly(var SnBytes: TSnBytes): Integer;
function SaveToFile(sPath: String): Boolean;
property Command: Integer read GetCommand;
property PacketKind: TTgPacketKind read GetPacketKind;
property Toss: LONGLONG read GetToss write SetToss;
property Handle: LONGLONG read GetHandle write SetHandle;
property WndMessage: LONGLONG read GetWndMessage write SetWndMessage;
property Result: Integer read GetResult write SetResult;
property ResultMsg: String read GetResultMsg write SetResultMsg;
property Socket: TObject read GetSocket write SetSocket;
property O[const sPath: String]: ISuperObject read GetO write PutO; default;
property B[const sPath: String]: Boolean read GetB write PutB;
property I[const sPath: String]: SuperInt read GetI write PutI;
property D[const sPath: String]: Double read GetD write PutD;
property C[const sPath: String]: Currency read GetC write PutC;
property S[const sPath: String]: String read GetS write PutS;
end;
const
LEN_CTX_PACKET_HEADER = SizeOf(TCtxPacketHeader);
BUFFER_SIZE = 32 * 1024;
MAX_BUF_LEN = BUFFER_SIZE;
implementation
uses
Tocsg.Safe, Tocsg.JSON;
{ TTgPacket }
Constructor TTgPacket.Create;
begin
Inherited Create;
ZeroMemory(@PacketHeader_, SizeOf(PacketHeader_));
dwRcvLen_ := 0;
end;
Constructor TTgPacket.Create(const aCmd: Integer; PacketKind: TTgPacketKind = pkNormal);
begin
Create;
PacketHeader_.Cmd := aCmd;
PacketHeader_.PacketKind := PacketKind;
SuperObject_ := TSuperObject.Create;
end;
Constructor TTgPacket.Create(aPacket: ITgPacket; PacketKind: TTgPacketKind = pkNormal);
begin
Create;
PacketHeader_ := aPacket._GetPacketHeader;
SuperObject_ := aPacket._GetJsonObject;
PacketHeader_.PacketKind := PacketKind;
end;
Constructor TTgPacket.Create(const aRcvData: TSnBytes; bIncludeLen: Boolean = false; aSocket: TObject = nil{for ClientContext});
procedure ExtractData;
var
nDataLen: Integer;
sJsonData: UTF8String;
begin
nDataLen := 0;
if bIncludeLen then
begin
dwRcvLen_ := Length(aRcvData);
if dwRcvLen_ < 4 then
raise ETgPacket.Create('Rcv 버퍼가 손상되었습니다.');
CopyMemory(@nDataLen, @aRcvData[0], SIZE_INTEGER);
if nDataLen <> (dwRcvLen_ - SIZE_INTEGER) then
begin
raise ETgPacket.CreateFmt('Rcv 데이터 크기가 잘못되었습니다. (DataLen = %d, RcvBufLen = %d',
[nDataLen, dwRcvLen_ - SIZE_INTEGER]);
end;
SetLength(sJsonData, nDataLen);
CopyMemory(@sJsonData[1], @aRcvData[4], nDataLen);
end else begin
nDataLen := Length(aRcvData);
dwRcvLen_ := nDataLen;
SetLength(sJsonData, nDataLen);
CopyMemory(@sJsonData[1], @aRcvData[0], nDataLen);
end;
SuperObject_ := SO(sJsonData);
try
with SuperObject_['Header'], PacketHeader_ do
begin
Cmd := I['Cmd'];
Handle := I['Handle'];
WndMessage := I['WndMessage'];
PacketKind := TTgPacketKind(I['PacketKind']);
Result := I['Result'];
Toss := I['Toss'];
ResultMsg := S['ResultMsg'];
end;
except
raise ETgPacket.Create('Invalid packet ..');
end;
end;
begin
Create;
Socket_ := aSocket;
ExtractData;
end;
Constructor TTgPacket.Create(aSocket: TObject{for ClientContext}; const aRcvData: Pointer; nRcvLen: Integer);
begin
Create(aRcvData, nRcvLen);
Socket_ := aSocket;
end;
Constructor TTgPacket.Create(const aRcvData: Pointer; nRcvLen: Integer);
procedure ExtractData;
var
sJsonData: UTF8String;
begin
dwRcvLen_ := nRcvLen;
SetLength(sJsonData, nRcvLen);
CopyMemory(@sJsonData[1], aRcvData, nRcvLen);
SuperObject_ := SO(sJsonData);
try
with SuperObject_['Header'], PacketHeader_ do
begin
Cmd := I['Cmd'];
Handle := I['Handle'];
WndMessage := I['WndMessage'];
PacketKind := TTgPacketKind(I['PacketKind']);
Result := I['Result'];
Toss := I['Toss'];
ResultMsg := S['ResultMsg'];
end;
except
raise ETgPacket.Create('Invalid packet ..');
end;
end;
begin
Create;
ExtractData;
end;
Constructor TTgPacket.Create(const sJsonData: String);
begin
Inherited Create;
SuperObject_ := SO(sJsonData);
with SuperObject_['Header'], PacketHeader_ do
begin
Cmd := I['Cmd'];
Handle := I['Handle'];
WndMessage := I['WndMessage'];
PacketKind := TTgPacketKind(I['PacketKind']);
Result := I['Result'];
Toss := I['Toss'];
ResultMsg := S['ResultMsg'];
end;
end;
function TTgPacket._GetPacketHeader: TTgPacketHeader;
begin
Result := PacketHeader_;
end;
function TTgPacket._GetJsonObject: ISuperObject;
begin
Result := SuperObject_;
end;
function TTgPacket.ToJsonString: String;
var
O: ISuperObject;
begin
Result := '';
if SuperObject_ <> nil then
begin
O := SO;
with PacketHeader_, O do
begin
I['Cmd'] := Cmd;
if Handle <> 0 then
I['Handle'] := Handle;
if WndMessage <> 0 then
I['WndMessage'] := WndMessage;
if PacketKind <> pkNormal then
I['PacketKind'] := Integer(PacketKind);
if Result <> 0 then
I['Result'] := Result;
if Toss <> 0 then
I['Toss'] := Toss;
if ResultMsg <> '' then
S['ResultMsg'] := ResultMsg;
end;
SuperObject_.O['Header'] := O;
Result := SuperObject_.AsJSon;
end;
end;
function TTgPacket.ToBytes(var SnBytes: TSnBytes): Integer;
var
sJsonData: UTF8String;
nDataLen: Integer;
begin
Result := 0;
sJsonData := UTF8Encode(ToJsonString);
if sJsonData <> '' then
begin
nDataLen := Length(sJsonData);
Result := SIZE_INTEGER + Length(sJsonData);
SetLength(SnBytes, Result);
CopyMemory(@SnBytes[0], @nDataLen, SIZE_INTEGER);
CopyMemory(@SnBytes[4], @sJsonData[1], nDataLen);
// SetLength(sJsonData, 0);
end;
end;
function TTgPacket.ToBytesDataOnly(var SnBytes: TSnBytes): Integer;
var
sJsonData: UTF8String;
begin
Result := 0;
sJsonData := UTF8Encode(ToJsonString);
if sJsonData <> '' then
begin
Result := Length(sJsonData);
SetLength(SnBytes, Result);
CopyMemory(@SnBytes[0], @sJsonData[1], Result);
end;
end;
function TTgPacket.SaveToFile(sPath: String): Boolean;
var
ss: TStringStream;
begin
Result := false;
try
Guard(ss, TStringStream.Create(ToJsonString, TEncoding.UTF8));
ss.SaveToFile(sPath);
Result := true;
except
//
end;
end;
procedure TTgPacket.PutO(const sPath: String; const Value: ISuperObject);
begin
SuperObject_.O[sPath] := Value;
end;
procedure TTgPacket.PutB(const sPath: String; Value: Boolean);
begin
SuperObject_.B[sPath] := Value;
end;
procedure TTgPacket.PutI(const sPath: String; Value: SuperInt);
begin
SuperObject_.I[sPath] := Value;
end;
procedure TTgPacket.PutC(const sPath: String; Value: Currency);
begin
SuperObject_.C[sPath] := Value;
end;
procedure TTgPacket.PutD(const sPath: String; Value: Double);
begin
SuperObject_.D[sPath] := Value;
end;
procedure TTgPacket.PutS(const sPath: String; const Value: String);
begin
SuperObject_.S[sPath] := Value;
end;
function TTgPacket.GetO(const sPath: String): ISuperObject;
begin
Result := SuperObject_.O[sPath];
end;
function TTgPacket.GetB(const sPath: String): Boolean;
begin
Result := SuperObject_.B[sPath];
end;
function TTgPacket.GetI(const sPath: String): SuperInt;
begin
Result := SuperObject_.I[sPath];
end;
function TTgPacket.GetD(const sPath: String): Double;
begin
Result := SuperObject_.D[sPath];
end;
function TTgPacket.GetC(const sPath: String): Currency;
begin
Result := SuperObject_.C[sPath];
end;
function TTgPacket.GetS(const sPath: String): String;
begin
Result := SuperObject_.S[sPath];
end;
function TTgPacket.GetA(const sPath: String): TSuperArray;
begin
Result := SuperObject_.A[sPath];
end;
function TTgPacket.GetCommand: Integer;
begin
Result := PacketHeader_.Cmd;
end;
procedure TTgPacket.SetPacketKind(aPacketKind: TTgPacketKind);
begin
if PacketHeader_.PacketKind <> aPacketKind then
PacketHeader_.PacketKind := aPacketKind;
end;
function TTgPacket.GetPacketKind: TTgPacketKind;
begin
Result := PacketHeader_.PacketKind;
end;
procedure TTgPacket.SetHandle(llHandle: LONGLONG);
begin
if PacketHeader_.Handle <> llHandle then
PacketHeader_.Handle := llHandle;
end;
function TTgPacket.GetHandle: LONGLONG;
begin
Result := PacketHeader_.Handle;
end;
procedure TTgPacket.SetWndMessage(llWndMessage: LONGLONG);
begin
if PacketHeader_.WndMessage <> llWndMessage then
PacketHeader_.WndMessage := llWndMessage;
end;
function TTgPacket.GetWndMessage: LONGLONG;
begin
Result := PacketHeader_.WndMessage;
end;
procedure TTgPacket.SetToss(llToss: LONGLONG);
begin
if PacketHeader_.Toss <> llToss then
PacketHeader_.Toss := llToss;
end;
function TTgPacket.GetToss: LONGLONG;
begin
Result := PacketHeader_.Toss;
end;
procedure TTgPacket.SetResult(nResult: Integer);
begin
if PacketHeader_.Result <> nResult then
PacketHeader_.Result := nResult;
end;
function TTgPacket.GetResult: Integer;
begin
Result := PacketHeader_.Result;
end;
procedure TTgPacket.SetResultMsg(const sResultMsg: String);
begin
if PacketHeader_.ResultMsg <> sResultMsg then
PacketHeader_.ResultMsg := sResultMsg;
end;
function TTgPacket.GetResultMsg: String;
begin
Result := PacketHeader_.ResultMsg;
end;
function TTgPacket.GetSocket: TObject;
begin
Result := Socket_;
{$IFDEF DEBUG}
if Result = nil then
raise ETgPacket.Create('소켓이 지정 되지있지 않습니다.');
{$ENDIF}
end;
procedure TTgPacket.SetSocket(aSocket: TObject);
begin
if Socket_ <> aSocket then
Socket_ := aSocket;
end;
function TTgPacket.GetRcvPacketSize: DWORD;
begin
Result := dwRcvLen_;
end;
end.