2873 lines
72 KiB
Plaintext
2873 lines
72 KiB
Plaintext
{*******************************************************}
|
|
{ }
|
|
{ Tocsg.ServerBase }
|
|
{ }
|
|
{ Copyright (C) 2022 kku }
|
|
{ }
|
|
{*******************************************************}
|
|
|
|
unit Tocsg.ServerBase;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.Classes, System.SysUtils, Winapi.Messages, Winapi.Windows,
|
|
Winapi.Winsock2, System.SyncObjs, Tocsg.Packet, Tocsg.Obj, Tocsg.Thread,
|
|
Tocsg.Exception, Tocsg.Encrypt, System.Generics.Collections;
|
|
|
|
const
|
|
DEFAULT_BUF_COUNT = 1000;
|
|
STOP_WORK = $FFFFFFFF;
|
|
PACKET_VERSION = 1;
|
|
|
|
MAX_SEND_COUNT = DWORD(-1);
|
|
|
|
MAX_CTX_SEND_BUFCOUNT = 30000;
|
|
|
|
FILE_CUT_IPS = 'CutIpLst.#info';
|
|
|
|
type
|
|
// Server -> Client
|
|
PCtxPacketHeader = ^TCtxPacketHeader;
|
|
TCtxPacketHeader = packed record
|
|
sSig: array [0..5] of AnsiChar; // @OC$.G
|
|
wRank: WORD;
|
|
dwId,
|
|
dwSize: DWORD;
|
|
end;
|
|
|
|
ECrmSocket = class(ETgException);
|
|
|
|
TTgSocketBase = class(TTgObject)
|
|
private
|
|
CS_: TCriticalSection;
|
|
|
|
sRemoteHost_,
|
|
sRemoteAddr_: String;
|
|
function GetRemoteAddr: String;
|
|
function GetRemoteHost: String;
|
|
protected
|
|
hSocket_: TSocket;
|
|
bActive_: Boolean;
|
|
DefEncoding_: TSnTextEncoding;
|
|
|
|
PktEncKind_: TTgEncKind;
|
|
PktEncPass_: String;
|
|
Enc_: TTgEncrypt;
|
|
|
|
procedure Lock;
|
|
procedure Unlock;
|
|
|
|
function GetActive: Boolean; virtual;
|
|
procedure SetActive(bVal: Boolean); virtual; abstract;
|
|
procedure _Check_WSAGetLastError(nResult: Integer; const sMsg: String);
|
|
|
|
function GetPktEncKind: TTgEncKind;
|
|
function GetPktEncPass: String;
|
|
function GetPktEncObj: TTgEncrypt;
|
|
public
|
|
Constructor Create(aSocket: TSocket);
|
|
Destructor Destroy; override;
|
|
|
|
procedure SetPacketEncInfo(aPktEncKind: TTgEncKind; const sPktEncPass: String); virtual;
|
|
|
|
property Handle: TSocket read hSocket_;
|
|
property Active: Boolean read GetActive write SetActive;
|
|
property RemoteAddr: String read GetRemoteAddr;
|
|
property RemoteHost: String read GetRemoteHost;
|
|
end;
|
|
|
|
TSocketDataType = (dtRcv, dtSend);
|
|
PSocketData = ^TSocketData;
|
|
TSocketData = packed record
|
|
Overlapped: OVERLAPPED;
|
|
WSABuf: WSABuf;
|
|
SocketDataType: TSocketDataType;
|
|
AllocBuf: Pointer;
|
|
// arrBuf: array [0..MAX_BUF_LEN-1] of Byte;
|
|
end;
|
|
|
|
PSocketBuffer = ^TSocketBuffer;
|
|
TSocketBuffer = packed record
|
|
Data: TSocketData;
|
|
bUseWSA: Boolean;
|
|
end;
|
|
|
|
TTgServerBase = class;
|
|
TThdClientClose = class;
|
|
|
|
TClientInfo = record
|
|
nType: Integer;
|
|
sVer,
|
|
sWinVer,
|
|
sIpAddr,
|
|
sIpRAddr,
|
|
sAccount,
|
|
sComName,
|
|
sMacAddr,
|
|
sMacAddrs: String;
|
|
BootDT,
|
|
ConnDT: TDateTime;
|
|
end;
|
|
|
|
TTgClientCtxBase = class(TTgSocketBase)
|
|
private
|
|
_RcvBuffers: TSnBytes;
|
|
_PieceRcvBuffers: array [0..2] of Byte; // 4바이트 미만 조각 데이터
|
|
_nTotalRcvLen,
|
|
_nRemainRcvLen,
|
|
_PieceRcvLen: Integer; // 4바이트 미만 조각 패킷
|
|
|
|
bRcvDec_,
|
|
bSendEnc_: Boolean;
|
|
|
|
// 전송 실패 카운트
|
|
nSendFailCnt_: Integer;
|
|
// 각 클라이언트에 대한 패킷 처리방식을 다르게 하기위해 버전정보를 추가 한다.
|
|
wPktVer_: WORD;
|
|
|
|
dtLastRcv_: TDateTime;
|
|
|
|
qSendWaitBuf_: TQueue<PSocketBuffer>;
|
|
nIoRefCnt_: Integer;
|
|
|
|
pRcvBuf_: PSocketBuffer;
|
|
SendBufList_: TList<PSocketBuffer>;
|
|
|
|
// SendPacket() 할때마다 카운트 되며 CtxPacketHeader의 dwId 값이 된다.
|
|
dwSendCount_: DWORD;
|
|
|
|
procedure OnSendQNotify(Sender: TObject; const Item: PSocketBuffer;
|
|
Action: TCollectionNotification);
|
|
procedure SetActive(bVal: Boolean); override;
|
|
function GetLastRcvTime: TDateTime;
|
|
procedure UpdateLastRcvTime;
|
|
procedure SetPacketVersion(const wVal: WORD);
|
|
function GetPacketVersion: WORD;
|
|
procedure MakeRcvPacket(nBufferLen: Integer);
|
|
|
|
procedure SetRcvDec(bVal: Boolean);
|
|
function GetRcvDec: Boolean;
|
|
procedure SetSendEnc(bVal: Boolean);
|
|
function GetSendEnc: Boolean;
|
|
|
|
function PushSendBuf(pBuf: PSocketBuffer): Boolean;
|
|
function AddSendBuf(pBuf: PSocketBuffer): Integer;
|
|
procedure RemoveSendBuf(pBuf: PSocketBuffer);
|
|
function GetSendBufCount: Integer;
|
|
|
|
// function Old_SendPacket_Until_1_2_3(Send: ISendPacket): Boolean;
|
|
protected
|
|
Server_: TTgServerBase;
|
|
AssocObj_: TTgObject;
|
|
ClientInfo_: TClientInfo;
|
|
// 계속 물려있는 현상 보완을 위해 추가
|
|
dtCreate_: TDateTime;
|
|
bSendPktEncInfo_: Boolean;
|
|
public
|
|
Constructor Create(aServer: TTgServerBase; aSocket: TSocket);
|
|
Destructor Destroy; override;
|
|
procedure ClearSendBuffer;
|
|
|
|
procedure SetPacketEncInfo(aPktEncKind: TTgEncKind; const sPktEncPass: String); override;
|
|
|
|
procedure Close;
|
|
function ReadyRecv: Boolean;
|
|
// function ProcessSendBuf(pSendBuf: PSocketBuffer = nil): Boolean;
|
|
function ProcessSendBuf: Boolean;
|
|
|
|
function SendPacket(Send: ISendPacket): Boolean;
|
|
|
|
property LastRcvTime: TDateTime read GetLastRcvTime;
|
|
property AbleRcvDec: Boolean read GetRcvDec write SetRcvDec;
|
|
property AbleSendEnc: Boolean read GetSendEnc write SetSendEnc;
|
|
property OwnerSVr: TTgServerBase read Server_;
|
|
property ClientInfo: TClientInfo read ClientInfo_;
|
|
end;
|
|
|
|
PRcvPktData = ^TRcvPktData;
|
|
TRcvPktData = record
|
|
Ctx: TTgClientCtxBase;
|
|
PacketKind: TTgPacketKind;
|
|
pData: Pointer;
|
|
nLen: Integer;
|
|
end;
|
|
|
|
TClientEnumerator = TEnumerator<TTgClientCtxBase>;
|
|
TClientContextClass = class of TTgClientCtxBase;
|
|
|
|
TClientNotifyEvent = procedure(Sender: TTgServerBase; aClient: TTgClientCtxBase) of object;
|
|
|
|
TThdSocket = class;
|
|
TThdPingWorker = class;
|
|
|
|
ECrmServer = class(ETgException);
|
|
TTgServerBase = class(TTgSocketBase)
|
|
private
|
|
dtActive_: TDateTime;
|
|
hCompPort_: THandle;
|
|
|
|
sIPAddr_: String;
|
|
nPort_: Integer;
|
|
SockAddr_: TSockAddr;
|
|
|
|
SenderThreadList_,
|
|
WorkThreadList_: TList<TThdSocket>;
|
|
ThdSvrAccept_,
|
|
ThdSvrLogic_: TThdSocket;
|
|
ThdClientClose_: TThdClientClose;
|
|
ThdPingWorker_: TThdPingWorker;
|
|
|
|
evClientConnected_,
|
|
evClientDisconnected_: TClientNotifyEvent;
|
|
|
|
nRcvBufSize_,
|
|
nSendBufSize_: Integer;
|
|
wWorkThdCnt_: WORD;
|
|
|
|
CtxSendBufList_: TList<PSocketBuffer>;
|
|
|
|
procedure _Accept;
|
|
procedure _Open;
|
|
procedure _Close;
|
|
|
|
procedure _RegisterClient(aClient: TTgClientCtxBase);
|
|
procedure _RemoveClient(aClient: TTgClientCtxBase);
|
|
|
|
procedure _ProcessTossPacket(aCtx: TTgClientCtxBase; RcvPacket: IRcvPacket);
|
|
|
|
procedure OnCtxSendBufNotify(Sender: TObject; const Item: PSocketBuffer;
|
|
Action: TCollectionNotification);
|
|
procedure AddCtxSendBuf(pBuf: PSocketBuffer);
|
|
procedure RemoveCtxSendBuf(pBuf: PSocketBuffer);
|
|
procedure ClearCtxSendBuf;
|
|
protected
|
|
QueueRcvPacket_: TQUeue<PRcvPktData>;
|
|
QueueSendPacket_: TQueue<ISendPacket>;
|
|
|
|
llRcvWaitSize_: LONGLONG;
|
|
// llSendWaitSize_: LONGLONG;
|
|
|
|
DcClient_: TDictionary<Pointer,TTgClientCtxBase>;
|
|
|
|
CutIpList_: TStringList;
|
|
sCutIpLogDir_,
|
|
sCutIpLogFName_: String;
|
|
sMakedPacketEncPass4Send_: String; // 클라이언트에 전송할 패킷 암호화 패스워드를 미리 만들어서 저장해 둔다.
|
|
|
|
procedure _QueueRcvPacket(pRcvData: PRcvPktData); virtual;
|
|
function _DequeueRcvPacket: PRcvPktData; virtual;
|
|
procedure OnRcvDataNotify(Sender: TObject; const Item: PRcvPktData; Action: TCollectionNotification);
|
|
|
|
function _DequeueSendPacket: ISendPacket; virtual;
|
|
|
|
function CreateClientContext(aSocket: TSocket): TTgClientCtxBase; virtual;
|
|
procedure OnClientNotify(Sender: TObject; const Item: TTgClientCtxBase;
|
|
Action: TCollectionNotification);
|
|
procedure OnWorkThreadNotify(Sender: TObject; const Item: TThdSocket;
|
|
Action: TCollectionNotification);
|
|
|
|
procedure SetActive(bVal: Boolean); override;
|
|
procedure SetPort(const nPort: Integer);
|
|
|
|
procedure ClientConnectedEvent(aClient: TTgClientCtxBase); virtual;
|
|
procedure ClientDisconnectedEvent(aClient: TTgClientCtxBase); virtual;
|
|
|
|
function GetClientEnumerator: TClientEnumerator;
|
|
function GetClientCount: Integer;
|
|
|
|
function GetRcvWaitSize: LONGLONG;
|
|
// function GetSendWaitSize: LONGLONG;
|
|
// function IsValidBuffer(pBuf: PSocketBuffer): Boolean;
|
|
|
|
procedure SendPacketEncConfirm(aCtx: TTgClientCtxBase);
|
|
procedure SendPacketEncInfo(aCtx: TTgClientCtxBase);
|
|
procedure ProcessTossFail(aCtx: TTgClientCtxBase; aRcv: IRcvPacket); virtual;
|
|
procedure ProcessRcvPacket(aCtx: TTgClientCtxBase; aRcv: IRcvPacket); virtual;
|
|
procedure ProcessFileQueuePacket(aCtx: TTgClientCtxBase; aRcv: IRcvPacket); overload; virtual;
|
|
procedure ProcessFileQueuePacket(aCtx: TTgClientCtxBase; pRcvBuf: Pointer; pRcvLen: Integer); overload; virtual;
|
|
|
|
procedure ProcessClientConnection(aCtx: TTgClientCtxBase); virtual; abstract;
|
|
|
|
procedure PushClientClose(aCtx: TTgClientCtxBase); virtual;
|
|
procedure LoadCutIpList;
|
|
public
|
|
Constructor Create(const nPort: Integer);
|
|
Destructor Destroy; override;
|
|
|
|
procedure SetPacketEncInfo(aPktEncKind: TTgEncKind; const sPktEncPass: String); override;
|
|
procedure QueueSendPacket(SendPacket: ISendPacket); virtual; // protected에서 아래로 옮김 19_0830 16:03:47 sunk
|
|
// procedure IncSendWaitSize(llSize: LONGLONG);
|
|
// procedure DecSendWaitSize(llSize: LONGLONG);
|
|
|
|
function IsValidClient(aClient: TTgClientCtxBase): Boolean;
|
|
procedure CloseClientCtx(aClientCtx: TTgClientCtxBase); virtual;
|
|
function CheckCutIp(sIp: String): Boolean;
|
|
// function CheckAcceptBan(sIp: String): Boolean;
|
|
|
|
property CompletionPort: THandle read hCompPort_;
|
|
property IPAddr: String read sIPAddr_;
|
|
property Port: Integer read nPort_ write SetPort;
|
|
property CountClient: Integer read GetClientCount;
|
|
property OnClientConnected: TClientNotifyEvent write evClientConnected_;
|
|
property OnClientDisconnected: TClientNotifyEvent write evClientDisconnected_;
|
|
property ActiveDateTime: TDateTime read dtActive_;
|
|
property RcvWaitSize: LONGLONG read GetRcvWaitSize;
|
|
// property SendWaitSize: LONGLONG read GetSendWaitSize;
|
|
end;
|
|
|
|
TThdSocket = class(TTgThread)
|
|
private
|
|
Server_: TTgServerBase;
|
|
public
|
|
Constructor Create(aServer: TTgServerBase);
|
|
end;
|
|
|
|
TThdServerAccept = class(TThdSocket)
|
|
protected
|
|
procedure Execute; override;
|
|
end;
|
|
|
|
TThdServerWork = class(TThdSocket)
|
|
protected
|
|
procedure Execute; override;
|
|
end;
|
|
|
|
TThdClientClose = class(TThdSocket)
|
|
private
|
|
qCloseCtx_: TQueue<TTgClientCtxBase>;
|
|
DcCloseCtx_: TDictionary<Pointer,TTgClientCtxBase>;
|
|
procedure OnQCtxNotify(Sender: TObject; const Item: TTgClientCtxBase;
|
|
Action: TCollectionNotification);
|
|
procedure OnDCtxNotify(Sender: TObject; const Item: TTgClientCtxBase;
|
|
Action: TCollectionNotification);
|
|
protected
|
|
procedure Execute; override;
|
|
public
|
|
Constructor Create(aServer: TTgServerBase);
|
|
Destructor Destroy; override;
|
|
|
|
procedure PushCloseCtx(aCtx: TTgClientCtxBase);
|
|
end;
|
|
|
|
TThdServerLogic = class(TThdSocket)
|
|
protected
|
|
procedure Execute; override;
|
|
public
|
|
Constructor Create(aServer: TTgServerBase);
|
|
end;
|
|
|
|
TThdPingWorker = class(TTgThread)
|
|
private
|
|
Server_: TTgServerBase;
|
|
protected
|
|
procedure Execute; override;
|
|
public
|
|
Constructor Create(aServer: TTgServerBase);
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Tocsg.Safe, System.DateUtils, Tocsg.Path, Tocsg.Trace, Tocsg.Network, Tocsg.PacketDefine, Tocsg.Hex;
|
|
|
|
const
|
|
LEN_SOCKET_BUF = SizeOf(TSocketBuffer);
|
|
LEN_CTX_PACKET_HEADER = SizeOf(TCtxPacketHeader);
|
|
BUFFER_SIZE = 32 * 1024;
|
|
MAX_BUF_LEN = BUFFER_SIZE;
|
|
|
|
var
|
|
_CS: TCriticalSection = nil;
|
|
_WSAData: TWSAData;
|
|
_WSACount: Integer = 0;
|
|
|
|
{ TTgSocketBase }
|
|
|
|
Constructor TTgSocketBase.Create(aSocket: TSocket);
|
|
begin
|
|
CS_ := TCriticalSection.Create;
|
|
Inherited Create;
|
|
|
|
// nLockCnt_ := 0;
|
|
|
|
sRemoteHost_ := '';
|
|
sRemoteAddr_ := '';
|
|
|
|
Enc_ := nil;
|
|
PktEncPass_ := '';
|
|
PktEncKind_ := ekNone;
|
|
|
|
hSocket_ := aSocket;
|
|
bActive_ := hSocket_ <> INVALID_SOCKET;
|
|
|
|
_CS.Acquire;
|
|
try
|
|
if _WSACount = 0 then
|
|
begin
|
|
// WinSock 2.2 요청 MAKEWORD(2, 2) = $0202
|
|
if WSAStartup($0202, _WSAData) <> 0 then
|
|
begin
|
|
// WSASYSNOTREADY 네트워크 서브 시스템이 네트워크에 접속을 준비할 수 없음
|
|
// WSAVERNOTSUPPORTED 요구한 윈속의 버전이 서포트 안됨
|
|
// WSAEINPROGRESS 블로킹 윈도우 소켓이 실행중
|
|
// WSAEPROCLIM 동시에 실행 가능한 최대 윈속수에 달했음
|
|
// WSAEFAULT lpWSAData가 무효한 포인터임
|
|
nLastError_ := GetLastError;
|
|
_Trace('TSunkSocket.Create >> %s, LastError = %d', [SysErrorMessage(nLastError_), nLastError_]);
|
|
raise ECrmSocket.CreateFmt('%s, Error = %d', [SysErrorMessage(nLastError_), nLastError_]);
|
|
end else _WSACount := 1;;
|
|
end else Inc(_WSACount);
|
|
finally
|
|
_CS.Release;
|
|
end;
|
|
end;
|
|
|
|
Destructor TTgSocketBase.Destroy;
|
|
begin
|
|
_CS.Acquire;
|
|
try
|
|
if _WSACount > 0 then
|
|
Dec(_WSACount);
|
|
|
|
if _WSACount = 0 then
|
|
WSACleanup;
|
|
finally
|
|
_CS.Release;
|
|
end;
|
|
|
|
Inherited;
|
|
if Assigned(Enc_) then
|
|
FreeAndNil(Enc_);
|
|
FreeAndNil(CS_);
|
|
end;
|
|
|
|
procedure TTgSocketBase.Lock;
|
|
begin
|
|
if Assigned(CS_) then
|
|
CS_.Acquire;
|
|
end;
|
|
|
|
procedure TTgSocketBase.Unlock;
|
|
begin
|
|
if Assigned(CS_) then
|
|
CS_.Release;
|
|
end;
|
|
|
|
function TTgSocketBase.GetActive: Boolean;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := bActive_;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgSocketBase._Check_WSAGetLastError(nResult: Integer; const sMsg: String);
|
|
var
|
|
nErr: Integer;
|
|
begin
|
|
if nResult <> 0 then
|
|
begin
|
|
nErr := WSAGetLastError;
|
|
case nErr of
|
|
WSAEWOULDBLOCK,
|
|
ERROR_IO_PENDING : ;
|
|
else
|
|
begin
|
|
nLastError_ := nErr;
|
|
_Trace('_Check_WSAGetLastError("%s"), ErrorCode = %d', [sMsg, nErr]);
|
|
raise ECrmSocket.CreateFmt('Windows socket error: %s (%d), on API ''%s''',
|
|
[SysErrorMessage(nErr), nErr, sMsg]);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TTgSocketBase.GetPktEncKind: TTgEncKind;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := PktEncKind_;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
function TTgSocketBase.GetPktEncPass: String;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := PktEncPass_;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
function TTgSocketBase.GetPktEncObj: TTgEncrypt;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := Enc_;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
function TTgSocketBase.GetRemoteAddr: String;
|
|
var
|
|
SockAddrIn: TSockAddr;
|
|
nSize: Integer;
|
|
begin
|
|
if sRemoteAddr_ <> '' then
|
|
begin
|
|
Result := sRemoteAddr_;
|
|
exit;
|
|
end;
|
|
|
|
Result := '';
|
|
|
|
if not bActive_ or (hSocket_ = INVALID_SOCKET) then
|
|
exit;
|
|
|
|
nSize := SizeOf(SockAddrIn);
|
|
_Check_WSAGetLastError(getpeername(hSocket_, SockAddrIn, nSize), 'getpeername');
|
|
Result := String(inet_ntoa(TSockAddrIn(SockAddrIn).sin_addr));
|
|
sRemoteAddr_ := Result;
|
|
end;
|
|
|
|
function TTgSocketBase.GetRemoteHost: String;
|
|
var
|
|
SockAddrIn: TSockAddr;
|
|
nSize: Integer;
|
|
pHost: PHostEnt;
|
|
begin
|
|
if sRemoteHost_ <> '' then
|
|
begin
|
|
Result := sRemoteHost_;
|
|
exit;
|
|
end;
|
|
|
|
Result := '';
|
|
if not bActive_ or (hSocket_ = INVALID_SOCKET) then
|
|
exit;
|
|
|
|
nSize := SizeOf(SockAddrIn);
|
|
_Check_WSAGetLastError(getpeername(hSocket_, SockAddrIn, nSize), 'getpeername');
|
|
pHost := gethostbyaddr(@TSockAddrIn(SockAddrIn).sin_addr.s_addr, 4, PF_INET);
|
|
if pHost <> nil then
|
|
begin
|
|
Result := String(pHost.h_name);
|
|
sRemoteHost_ := Result;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgSocketBase.SetPacketEncInfo(aPktEncKind: TTgEncKind; const sPktEncPass: String);
|
|
begin
|
|
Lock;
|
|
try
|
|
PktEncKind_ := aPktEncKind;
|
|
PktEncPass_ := sPktEncPass;
|
|
|
|
if Assigned(Enc_) then
|
|
FreeAndNil(Enc_);
|
|
|
|
if PktEncKind_ <> ekNone then
|
|
Enc_ := TTgEncrypt.Create(PktEncPass_, PktEncKind_);
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
{ TTgClientCtxBase }
|
|
|
|
Constructor TTgClientCtxBase.Create(aServer: TTgServerBase; aSocket: TSocket);
|
|
begin
|
|
New(pRcvBuf_);
|
|
ZeroMemory(pRcvBuf_, LEN_SOCKET_BUF);
|
|
pRcvBuf_.Data.AllocBuf := AllocMem(MAX_BUF_LEN);
|
|
// SetLength(pRcvBuf_.Data.AllocBuf, MAX_BUF_LEN);
|
|
|
|
// New(pSendBuf_);
|
|
// ZeroMemory(pSendBuf_, LEN_SOCKET_BUF);
|
|
|
|
Inherited Create(aSocket);
|
|
{$IFDEF DEBUG2} _Trace('Create() .. '); {$ENDIF}
|
|
|
|
SendBufList_ := TList<PSocketBuffer>.Create;
|
|
SendBufList_.OnNotify := OnSendQNotify;
|
|
|
|
nIoRefCnt_ := 0;
|
|
wPktVer_ := 0;
|
|
|
|
bRcvDec_ := false;
|
|
bSendEnc_ := false;
|
|
|
|
Server_ := aServer;
|
|
Server_._RegisterClient(Self);
|
|
ASSERT(Server_.wWorkThdCnt_ > 0);
|
|
// SetLength(arrSendBuf_, Server_.wWorkThdCnt_);
|
|
// ZeroMemory(arrSendBuf_, LEN_SOCKET_BUF * Server_.wWorkThdCnt_);
|
|
|
|
qSendWaitBuf_ := TQueue<PSocketBuffer>.Create;
|
|
|
|
nSendFailCnt_ := 0;
|
|
_PieceRcvLen := 0;
|
|
_nTotalRcvLen := 0;
|
|
_nRemainRcvLen := 0;
|
|
dwSendCount_ := 0;
|
|
|
|
AssocObj_ := nil;
|
|
ZeroMemory(@ClientInfo_, SizeOf(ClientInfo_));
|
|
dtCreate_ := Now;
|
|
bSendPktEncInfo_ := false;
|
|
|
|
UpdateLastRcvTime;
|
|
{$IFDEF DEBUG2} _Trace('Create() .. 1'); {$ENDIF}
|
|
|
|
if not ReadyRecv then
|
|
Server_.CloseClientCtx(Self);
|
|
{$IFDEF DEBUG2} _Trace('Create() .. 2'); {$ENDIF}
|
|
end;
|
|
|
|
Destructor TTgClientCtxBase.Destroy;
|
|
begin
|
|
SetActive(false);
|
|
SetLength(_RcvBuffers, 0);
|
|
qSendWaitBuf_.OnNotify := OnSendQNotify;
|
|
FreeAndNil(qSendWaitBuf_);
|
|
FreeAndNil(SendBufList_);
|
|
Inherited;
|
|
// Dispose(pSendBuf_);
|
|
// SetLength(pRcvBuf_.Data.AllocBuf, 0);
|
|
FreeMem(pRcvBuf_.Data.AllocBuf, MAX_BUF_LEN);
|
|
Dispose(pRcvBuf_);
|
|
end;
|
|
|
|
procedure TTgClientCtxBase.OnSendQNotify(Sender: TObject; const Item: PSocketBuffer;
|
|
Action: TCollectionNotification);
|
|
begin
|
|
try
|
|
case Action of
|
|
cnAdded : ;
|
|
cnRemoved :
|
|
begin
|
|
// if not Item.bUseWSA then
|
|
// begin
|
|
//// SetLength(Item.Data.AllocBuf, 0);
|
|
// if (Item.Data.AllocBuf <> nil) and (Item.Data.WSABuf.len > 0) then
|
|
// FreeMem(Item.Data.AllocBuf, Item.Data.WSABuf.len);
|
|
// Dispose(Item);
|
|
// end else
|
|
// Server_.AddCtxSendBuf(Item);
|
|
|
|
// SetLength(Item.Data.AllocBuf, 0);
|
|
if (Item.Data.AllocBuf <> nil) and (Item.Data.WSABuf.len > 0) then
|
|
FreeMem(Item.Data.AllocBuf, Item.Data.WSABuf.len);
|
|
Dispose(Item);
|
|
end;
|
|
cnExtracted : ;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(Self, E, 'Fail .. OnSendQNotify()');
|
|
end;
|
|
end;
|
|
|
|
procedure TTgClientCtxBase.ClearSendBuffer;
|
|
begin
|
|
Lock;
|
|
qSendWaitBuf_.OnNotify := OnSendQNotify;
|
|
try
|
|
qSendWaitBuf_.Clear;
|
|
SendBufList_.Clear;
|
|
finally
|
|
qSendWaitBuf_.OnNotify := nil;
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgClientCtxBase.SetPacketEncInfo(aPktEncKind: TTgEncKind; const sPktEncPass: String);
|
|
begin
|
|
// 서버가 활성화중이 아니면 무시 14_0704 09:56:49 sunk
|
|
if not Server_.Active then
|
|
exit;
|
|
|
|
Inherited;
|
|
|
|
// Lock;
|
|
// try
|
|
// bRcvDec_ := false;
|
|
// bSendEnc_ := false;
|
|
// finally
|
|
// Unlock;
|
|
// end;
|
|
end;
|
|
|
|
procedure TTgClientCtxBase.Close;
|
|
begin
|
|
Active := false;
|
|
end;
|
|
|
|
procedure TTgClientCtxBase.SetActive(bVal: Boolean);
|
|
var
|
|
li: TLinger;
|
|
begin
|
|
if bActive_ <> bVal then
|
|
begin
|
|
Lock;
|
|
try
|
|
bActive_ := bVal;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
|
|
if not bVal then
|
|
begin
|
|
ClearSendBuffer;
|
|
Lock;
|
|
try
|
|
if hSocket_ <> INVALID_SOCKET then
|
|
begin
|
|
ZeroMemory(@li, SizeOf(li));
|
|
setsockopt(hSocket_, SOL_SOCKET, SO_LINGER, @li, SizeOf(li));
|
|
closesocket(hSocket_);
|
|
hSocket_ := INVALID_SOCKET;
|
|
end;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end else
|
|
raise ECrmSocket.Create('클라이언트는 임의적으로 활성화 시킬 수 없습니다.');
|
|
end;
|
|
end;
|
|
|
|
function TTgClientCtxBase.GetLastRcvTime: TDateTime;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := dtLastRcv_;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgClientCtxBase.UpdateLastRcvTime;
|
|
begin
|
|
Lock;
|
|
try
|
|
dtLastRcv_ := Now;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgClientCtxBase.SetPacketVersion(const wVal: WORD);
|
|
begin
|
|
Lock;
|
|
try
|
|
if wPktVer_ <> wVal then
|
|
wPktVer_ := wVal;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
function TTgClientCtxBase.GetPacketVersion: WORD;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := wPktVer_;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgClientCtxBase.SetRcvDec(bVal: Boolean);
|
|
begin
|
|
Lock;
|
|
try
|
|
bRcvDec_ := bVal;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
function TTgClientCtxBase.GetRcvDec: Boolean;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := bRcvDec_;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgClientCtxBase.SetSendEnc(bVal: Boolean);
|
|
begin
|
|
Lock;
|
|
try
|
|
bSendEnc_ := bVal;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
function TTgClientCtxBase.GetSendEnc: Boolean;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := bSendEnc_;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
function TTgClientCtxBase.PushSendBuf(pBuf: PSocketBuffer): Boolean;
|
|
var
|
|
bDoProcSend: Boolean;
|
|
begin
|
|
try
|
|
Result := false;
|
|
if not Active then
|
|
exit;
|
|
|
|
Lock;
|
|
try
|
|
bDoProcSend := qSendWaitBuf_.Count = 0;
|
|
qSendWaitBuf_.Enqueue(pBuf);
|
|
finally
|
|
Unlock;
|
|
end;
|
|
|
|
if bDoProcSend then
|
|
begin
|
|
Result := ProcessSendBuf;
|
|
if not Result then
|
|
Close;
|
|
// Server_.CloseClientCtx(Self);
|
|
end;
|
|
|
|
Result := true;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := false;
|
|
ETgException.TraceException(Self, E, 'Fail .. PushSendBuf()');
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TTgClientCtxBase.AddSendBuf(pBuf: PSocketBuffer): Integer;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := SendBufList_.Add(pBuf);
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgClientCtxBase.RemoveSendBuf(pBuf: PSocketBuffer);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
try
|
|
Lock;
|
|
try
|
|
i := SendBufList_.IndexOf(pBuf);
|
|
if i <> -1 then
|
|
begin
|
|
pBuf.bUseWSA := false;
|
|
SendBufList_.Delete(i);
|
|
end;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(Self, E, 'Fail .. RemoveSendBuf()');
|
|
end;
|
|
end;
|
|
|
|
function TTgClientCtxBase.GetSendBufCount: Integer;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := SendBufList_.Count;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgClientCtxBase.MakeRcvPacket(nBufferLen: Integer);
|
|
const
|
|
OVER_SIZE_PACKET = 52428800;{50MB}
|
|
var
|
|
nReaded,
|
|
nReadLen: Integer;
|
|
pBuf: TBytes;
|
|
i: Integer;
|
|
|
|
procedure ProcessRcvPacket;
|
|
var
|
|
Rcv: IRcvPacket;
|
|
pDecBuf: TBytes;
|
|
nDataPos: Integer;
|
|
pRcvData: PRcvPktData;
|
|
nPktVer: Integer;
|
|
begin
|
|
try
|
|
nPktVer := GetPacketVersion;
|
|
|
|
New(pRcvData);
|
|
pRcvData.Ctx := Self;
|
|
pRcvData.PacketKind := pkNormal;
|
|
pRcvData.nLen := Length(_RcvBuffers);
|
|
|
|
nDataPos := 0;
|
|
// 헤더 확인 및 처리
|
|
if CompareMem(@PCtxPacketHeader(_RcvBuffers).sSig[0], @CTX_PACKET_SIGNATURE[1], 6) then
|
|
begin
|
|
// 패킷을 j-son 오브젝트로 파싱 하기 전에 파일큐에 넣기 위해
|
|
pRcvData.PacketKind := TTgPacketKind(PCtxPacketHeader(_RcvBuffers).wRank);
|
|
nDataPos := LEN_CTX_PACKET_HEADER;
|
|
pRcvData.nLen := PCtxPacketHeader(_RcvBuffers).dwSize;
|
|
end;
|
|
|
|
pRcvData.pData := AllocMem(pRcvData.nLen);
|
|
|
|
// 복호화 확인 및 처리
|
|
if AbleRcvDec then
|
|
begin
|
|
if Enc_ = nil then
|
|
begin
|
|
// {$IFDEF DEBUG}
|
|
// ASSERT(Enc_ <> nil, 'none.. encrypt obj ..');
|
|
// {$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
Lock;
|
|
try
|
|
pDecBuf := Enc_.DecryptBufferEx(@_RcvBuffers[nDataPos], pRcvData.nLen);
|
|
|
|
case Enc_.EncKind of
|
|
ekAes256cbc,
|
|
ekAes192cbc,
|
|
ekAes128cbc:
|
|
begin
|
|
// 언패딩이 필요한 알고리즘의 경우,
|
|
// 언패딩 후 길이를 다시 계산
|
|
pRcvData.nLen := Length(pDecBuf);
|
|
end;
|
|
end;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
|
|
CopyMemory(pRcvData.pData, @pDecBuf[0], pRcvData.nLen);
|
|
end else
|
|
CopyMemory(pRcvData.pData, @_RcvBuffers[nDataPos], pRcvData.nLen);
|
|
|
|
if pRcvData.PacketKind = pkCritical then
|
|
begin
|
|
// 패킷 버전 2부터 헤더를 확인해서 랭크를 j-son 파싱 전에 랭크 확인하고 처리 시켜주게 한다.
|
|
// 그리고 로직 스레드에서 리시브 패킷 처리 하는걸로 변경
|
|
Rcv := TTgPacket.Create(Self, pRcvData.pData, pRcvData.nLen);
|
|
if Rcv.Toss = 0 then
|
|
Server_.ProcessRcvPacket(Self, Rcv)
|
|
else
|
|
Server_._ProcessTossPacket(Self, Rcv);
|
|
FreeMem(pRcvData.pData);
|
|
Dispose(pRcvData);
|
|
end else
|
|
Server_._QueueRcvPacket(pRcvData);
|
|
except
|
|
// on E: Exception do
|
|
// ETgException.TraceException(E, 'TTgClientCtxBase >> MakeRcvPacket()::MakeRcvPacket() .. error');
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
try
|
|
if not Active then
|
|
exit;
|
|
|
|
if _PieceRcvLen > 0 then
|
|
begin
|
|
SetLength(pBuf, _PieceRcvLen + nBufferLen);
|
|
CopyMemory(@pBuf[0], @_PieceRcvBuffers[0], _PieceRcvLen);
|
|
CopyMemory(@pBuf[_PieceRcvLen], pRcvBuf_.Data.AllocBuf, nBufferLen);
|
|
Inc(nBufferLen, _PieceRcvLen);
|
|
_PieceRcvLen := 0;
|
|
end else begin
|
|
// pBuf := @pRcvBuf_.Data.arrBuf;
|
|
SetLength(pBuf, MAX_BUF_LEN);
|
|
CopyMemory(@pBuf[0], pRcvBuf_.Data.AllocBuf, MAX_BUF_LEN);
|
|
// pBuf := TBytes(pRcvBuf_.Data.AllocBuf);
|
|
end;
|
|
|
|
nReaded := 0;
|
|
if _nRemainRcvLen = 0 then
|
|
begin
|
|
if nBufferLen < 10 then
|
|
begin
|
|
// 10바이트 미만 미루기
|
|
_PieceRcvLen := nBufferLen;
|
|
CopyMemory(@_PieceRcvBuffers[0], @pBuf[nReaded], _PieceRcvLen);
|
|
exit;
|
|
end;
|
|
|
|
// 먼저 밀린 패킷 마구 들어온건지 검증..15_0313 18:38:37 sunk
|
|
for i := 4 to nBufferLen - 7 do
|
|
begin
|
|
if AnsiChar(pBuf[i]) = CTX_PACKET_SIGNATURE[1] then
|
|
if AnsiChar(pBuf[i+1]) = CTX_PACKET_SIGNATURE[2] then
|
|
if AnsiChar(pBuf[i+2]) = CTX_PACKET_SIGNATURE[3] then
|
|
if AnsiChar(pBuf[i+3]) = CTX_PACKET_SIGNATURE[4] then
|
|
if AnsiChar(pBuf[i+4]) = CTX_PACKET_SIGNATURE[5] then
|
|
if AnsiChar(pBuf[i+5]) = CTX_PACKET_SIGNATURE[6] then
|
|
begin
|
|
nReaded := i - 4;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
if nBufferLen <= (i + 7) then
|
|
begin
|
|
_PieceRcvLen := 0;
|
|
_nTotalRcvLen := 0;
|
|
_nRemainRcvLen := 0;
|
|
exit;
|
|
end;
|
|
|
|
CopyMemory(@_nTotalRcvLen, @pBuf[nReaded], SIZE_INTEGER);
|
|
|
|
if _nTotalRcvLen + SIZE_INTEGER < nBufferLen then
|
|
begin
|
|
while nReaded < nBufferLen do
|
|
begin
|
|
if nBufferLen - nReaded < 4 then
|
|
begin
|
|
// 4바이트 미만은 패킷사이즈도 알수 없기때문에,
|
|
// 4바이트 미만은 다음 패킷까지 미룬다
|
|
_PieceRcvLen := nBufferLen - nReaded;
|
|
CopyMemory(@_PieceRcvBuffers[0], @pBuf[nReaded], _PieceRcvLen);
|
|
exit;
|
|
end;
|
|
CopyMemory(@_nTotalRcvLen, @pBuf[nReaded], SIZE_INTEGER);
|
|
Inc(nReaded, SIZE_INTEGER);
|
|
if _nTotalRcvLen > OVER_SIZE_PACKET then
|
|
begin
|
|
// 용량이 넘 크면 문제가 있다고 판단하고 넘김
|
|
_PieceRcvLen := 0;
|
|
_nTotalRcvLen := 0;
|
|
_nRemainRcvLen := 0;
|
|
exit;
|
|
end;
|
|
|
|
SetLength(_RcvBuffers, _nTotalRcvLen);
|
|
|
|
if _nTotalRcvLen > (nBufferLen - nReaded) then
|
|
begin
|
|
CopyMemory(@_RcvBuffers[0], @pBuf[nReaded], nBufferLen - nReaded);
|
|
_nRemainRcvLen := _nTotalRcvLen - (nBufferLen - nReaded);
|
|
break;
|
|
end else begin
|
|
CopyMemory(@_RcvBuffers[0], @pBuf[nReaded], _nTotalRcvLen);
|
|
|
|
ProcessRcvPacket;
|
|
|
|
Inc(nReaded, _nTotalRcvLen);
|
|
end;
|
|
end;
|
|
end else begin
|
|
if (_nTotalRcvLen + SIZE_INTEGER) > nBufferLen then
|
|
nReadLen := nBufferLen - SIZE_INTEGER
|
|
else
|
|
nReadLen := _nTotalRcvLen;
|
|
|
|
if _nTotalRcvLen > OVER_SIZE_PACKET then
|
|
begin
|
|
// 용량이 넘 크면 문제가 있다고 판단하고 넘김
|
|
_PieceRcvLen := 0;
|
|
_nTotalRcvLen := 0;
|
|
_nRemainRcvLen := 0;
|
|
exit;
|
|
end;
|
|
|
|
SetLength(_RcvBuffers, _nTotalRcvLen);
|
|
CopyMemory(@_RcvBuffers[0], @pBuf[SIZE_INTEGER], nReadLen);
|
|
_nRemainRcvLen := _nTotalRcvLen - nReadLen;
|
|
|
|
if _nRemainRcvLen = 0 then
|
|
ProcessRcvPacket;
|
|
end;
|
|
end else begin
|
|
if _nRemainRcvLen < nBufferLen then
|
|
begin
|
|
CopyMemory(@_RcvBuffers[_nTotalRcvLen-_nRemainRcvLen], @pBuf[0], _nRemainRcvLen);
|
|
ProcessRcvPacket;
|
|
|
|
nReaded := _nRemainRcvLen;
|
|
_nTotalRcvLen := 0;
|
|
_nRemainRcvLen := 0;
|
|
while nReaded < nBufferLen do
|
|
begin
|
|
if nBufferLen - nReaded < 4 then
|
|
begin
|
|
// 4바이트 미만은 패킷사이즈도 알수 없기때문에,
|
|
// 4바이트 미만은 다음 패킷까지 미룬다
|
|
_PieceRcvLen := nBufferLen - nReaded;
|
|
CopyMemory(@_PieceRcvBuffers[0], @pBuf[nReaded], _PieceRcvLen);
|
|
exit;
|
|
end;
|
|
CopyMemory(@_nTotalRcvLen, @pBuf[nReaded], SIZE_INTEGER);
|
|
Inc(nReaded, SIZE_INTEGER);
|
|
|
|
{$IFDEF DEBUG}
|
|
if _nTotalRcvLen > OVER_SIZE_PACKET then
|
|
begin
|
|
_nTotalRcvLen := _nTotalRcvLen + 0;
|
|
end;
|
|
{$ENDIF}
|
|
if _nTotalRcvLen > OVER_SIZE_PACKET then
|
|
begin
|
|
// 용량이 넘 크면 문제가 있다고 판단하고 넘김
|
|
_PieceRcvLen := 0;
|
|
_nTotalRcvLen := 0;
|
|
_nRemainRcvLen := 0;
|
|
exit;
|
|
end;
|
|
|
|
SetLength(_RcvBuffers, _nTotalRcvLen);
|
|
|
|
if _nTotalRcvLen > (nBufferLen - nReaded) then
|
|
begin
|
|
CopyMemory(@_RcvBuffers[0], @pBuf[nReaded], nBufferLen - nReaded);
|
|
_nRemainRcvLen := _nTotalRcvLen - (nBufferLen - nReaded);
|
|
break;
|
|
end else begin
|
|
CopyMemory(@_RcvBuffers[0], @pBuf[nReaded], _nTotalRcvLen);
|
|
|
|
ProcessRcvPacket;
|
|
|
|
Inc(nReaded, _nTotalRcvLen);
|
|
end;
|
|
end;
|
|
end else begin
|
|
CopyMemory(@_RcvBuffers[_nTotalRcvLen-_nRemainRcvLen], @pBuf[0], nBufferLen);
|
|
Dec(_nRemainRcvLen, nBufferLen);
|
|
|
|
if _nRemainRcvLen = 0 then
|
|
ProcessRcvPacket;
|
|
end;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(Self, E, 'Fail .. MakeRcvPacket()');
|
|
end;
|
|
end;
|
|
|
|
function TTgClientCtxBase.ReadyRecv: Boolean;
|
|
var
|
|
dwFlags, dwRecvLen: DWORD;
|
|
begin
|
|
Result := false;
|
|
|
|
try
|
|
if Active then
|
|
begin
|
|
ZeroMemory(pRcvBuf_.Data.AllocBuf, MAX_BUF_LEN);
|
|
pRcvBuf_.Data.WSABuf.buf := pRcvBuf_.Data.AllocBuf;
|
|
pRcvBuf_.Data.WSABuf.len := MAX_BUF_LEN;
|
|
|
|
pRcvBuf_.Data.SocketDataType := dtRcv;
|
|
|
|
Lock;
|
|
try
|
|
if hSocket_ <> INVALID_SOCKET then
|
|
begin
|
|
dwFlags := 0;
|
|
Result := WSARecv(hSocket_,
|
|
@pRcvBuf_.Data.WSABuf,
|
|
1,
|
|
dwRecvLen,
|
|
dwFlags,
|
|
@pRcvBuf_.Data.Overlapped,
|
|
nil) <> SOCKET_ERROR;
|
|
end else begin
|
|
Result := false;
|
|
exit;
|
|
end;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
|
|
if not Result then
|
|
Result := (WSAGetLastError = ERROR_IO_PENDING);
|
|
end;
|
|
except
|
|
// ..
|
|
end;
|
|
end;
|
|
|
|
//function TTgClientCtxBase.ProcessSendBuf(pSendBuf: PSocketBuffer = nil): Boolean;
|
|
function TTgClientCtxBase.ProcessSendBuf: Boolean;
|
|
var
|
|
pBuf: PSocketBuffer;
|
|
bResult: Boolean;
|
|
nError: Integer;
|
|
dwFlags,
|
|
dwSendLen: DWORD;
|
|
nRemain: Integer;
|
|
begin
|
|
Result := false;
|
|
if not Active then
|
|
exit;
|
|
|
|
Result := true;
|
|
|
|
Lock;
|
|
try
|
|
nRemain := qSendWaitBuf_.Count;
|
|
if nRemain > 0 then
|
|
pBuf := qSendWaitBuf_.Dequeue
|
|
else
|
|
pBuf := nil;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
|
|
if pBuf <> nil then
|
|
begin
|
|
// _Trace('ProcessSendBuf() .. Do Send.. nRemain=%d, SendLen=%d', [nRemain, pBuf.Data.WSABuf.len]);
|
|
try
|
|
pBuf.bUseWSA := true;
|
|
AddSendBuf(pBuf);
|
|
|
|
dwFlags := 0;
|
|
Lock;
|
|
try
|
|
if hSocket_ <> INVALID_SOCKET then
|
|
begin
|
|
bResult := WSASend(hSocket_,
|
|
@pBuf.Data.WSABuf,
|
|
1,
|
|
dwSendLen,
|
|
dwFlags,
|
|
@pBuf.Data.Overlapped,
|
|
nil) <> SOCKET_ERROR;
|
|
end else exit;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
|
|
if not bResult then
|
|
begin
|
|
nError := WSAGetLastError;
|
|
case nError of
|
|
WSA_IO_PENDING : ;
|
|
WSAENOTSOCK,
|
|
WSAENETRESET,
|
|
WSAEDISCON,
|
|
WSAEBADF,
|
|
WSAEWOULDBLOCK,
|
|
WSAECONNRESET,
|
|
WSAECONNABORTED :
|
|
begin
|
|
RemoveSendBuf(pBuf);
|
|
Result := false;
|
|
|
|
_Trace('TTgClientCtxBase >> Fail!! ProcessSendBuf().. WSASend() ... Close .. Error=%d, IP = %s', [nError, RemoteAddr]);
|
|
// Close;
|
|
exit;
|
|
end;
|
|
else
|
|
begin
|
|
RemoveSendBuf(pBuf);
|
|
Result := false;
|
|
if nSendFailCnt_ < 5 then
|
|
begin
|
|
// 5번까지 시도해준다.
|
|
_Trace('TTgClientCtxBase >> Fail!! ProcessSendBuf().. WSASend() >> Error=%d, IP = %s, FC=%d', [nError, RemoteAddr, nSendFailCnt_]);
|
|
Inc(nSendFailCnt_);
|
|
end else begin
|
|
_Trace('TTgClientCtxBase >> Fail!! ProcessSendBuf().. WSASend() ... Close- >> IP = %s', [RemoteAddr]);
|
|
// Close;
|
|
end;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if nSendFailCnt_ > 0 then
|
|
nSendFailCnt_ := 0;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(Self, E, 'Fail .. ProcessSendBuf()');
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TTgClientCtxBase.SendPacket(Send: ISendPacket): Boolean;
|
|
var
|
|
nLen: Integer;
|
|
pBuf: PSocketBuffer;
|
|
pEncBuf,
|
|
pSendBuf: TSnBytes;
|
|
nPacketVer, nSendPieceDataPos: Integer;
|
|
CtxPacketHeader: TCtxPacketHeader;
|
|
bAbleSendEnc: Boolean;
|
|
begin
|
|
Result := false;
|
|
try
|
|
if Active then
|
|
begin
|
|
bAbleSendEnc := AbleSendEnc;
|
|
nPacketVer := wPktVer_;
|
|
|
|
nLen := Send.ToBytesDataOnly(pSendBuf);
|
|
if nLen = 0 then
|
|
begin
|
|
{$IFDEF DEBUG}
|
|
ASSERT(false);
|
|
{$ELSE}
|
|
_Trace('TTgClientCtxBase >> Fail!! SendPacket().. no init buffer >> IP = %s', [RemoteAddr]);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
New(pBuf);
|
|
ZeroMemory(pBuf, LEN_SOCKET_BUF);
|
|
try
|
|
|
|
// if nPacketVer > 0 then
|
|
// begin
|
|
nSendPieceDataPos := LEN_CTX_PACKET_HEADER;
|
|
// end else begin
|
|
// bAbleSendEnc := false;
|
|
// nSendPieceDataPos := 0;
|
|
// end;
|
|
|
|
try
|
|
if not Active then
|
|
exit;
|
|
|
|
// 암호화 처리
|
|
if bAbleSendEnc then
|
|
begin
|
|
if Enc_ = nil then
|
|
begin
|
|
// {$IFDEF DEBUG}
|
|
// ASSERT(Enc_ <> nil, 'none.. encrypt obj ..');
|
|
// {$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
Lock;
|
|
try
|
|
pEncBuf := Enc_.EncryptBufferEx(pSendBuf, nLen);
|
|
case Enc_.EncKind of
|
|
ekAes256cbc,
|
|
ekAes192cbc,
|
|
ekAes128cbc:
|
|
begin
|
|
// 패딩이 필요한 알고리즘의 경우,
|
|
// 패딩 길이도 들어가야해서 다시 계산. 18_0411 10:08:16 sunk
|
|
nLen := Length(pEncBuf);
|
|
end;
|
|
end;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
|
|
pBuf.Data.AllocBuf := AllocMem(LEN_CTX_PACKET_HEADER + nLen);
|
|
CopyMemory(Pointer(NativeUInt(pBuf.Data.AllocBuf) + nSendPieceDataPos), pEncBuf, nLen);
|
|
end else begin
|
|
pBuf.Data.AllocBuf := AllocMem(LEN_CTX_PACKET_HEADER + nLen);
|
|
CopyMemory(Pointer(NativeUInt(pBuf.Data.AllocBuf) + nSendPieceDataPos), pSendBuf, nLen);
|
|
end;
|
|
|
|
// 패킷 헤더는 암호화 후 넣는걸로 변경 18_0411 11:38:23 sunk
|
|
// if nPacketVer > 0 then
|
|
// begin
|
|
// 패킷 버전별 헤더 도임 14_0610 11:18:13 sunk
|
|
// 패킷 버전 1 추가 - 서버 -> 클라이언트 헤더 도입
|
|
// 패킷 버전 2 추가 - 클라이언트 -> 서버 헤더 도입
|
|
ZeroMemory(@CtxPacketHeader, SizeOf(CtxPacketHeader));
|
|
CopyMemory(@CtxPacketHeader.sSig[0], @CTX_PACKET_SIGNATURE[1], Length(CtxPacketHeader.sSig));
|
|
CtxPacketHeader.dwId := dwSendCount_;
|
|
Inc(dwSendCount_);
|
|
if dwSendCount_ >= 400000000 then
|
|
dwSendCount_ := 0;
|
|
CtxPacketHeader.dwSize := nLen;
|
|
CopyMemory(pBuf.Data.AllocBuf, @CtxPacketHeader, LEN_CTX_PACKET_HEADER);
|
|
// end;
|
|
|
|
pBuf.Data.WSABuf.buf := pBuf.Data.AllocBuf;
|
|
pBuf.Data.WSABuf.len := nLen + nSendPieceDataPos;
|
|
pBuf.Data.SocketDataType := dtSend;
|
|
|
|
if not PushSendBuf(pBuf) then
|
|
begin
|
|
Result := false;
|
|
exit;
|
|
end;
|
|
finally
|
|
SetLength(pSendBuf, 0);
|
|
end;
|
|
Result := true;
|
|
finally
|
|
if not Result then
|
|
begin
|
|
FreeMem(pBuf.Data.AllocBuf);
|
|
Dispose(pBuf);
|
|
end;
|
|
end;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(E, 'TTgClientCtxBase >> SendPacket() .. exception');
|
|
// ETgException.TraceException(Self, ...) 이런식으로 쓰면 안된다 자신이 짤려서 AV로 여기 올때 다시 자신을 참조하면 아에 뻗음 16_1013 16:22:45 sunk
|
|
end;
|
|
end;
|
|
|
|
// AES 알고리즘을 추가 하면서 문제가 생겼다.
|
|
// 패딩 때문인데 이 패딩으로 추가되는 크기는 헤더 생성 후에 나오기 때문에
|
|
// 32k씩 잘라서 최초 헤더를 포함한 데이터가 쏘아지게 되면 뒤늦게 변경된 패딩 추가 크기를
|
|
// 알릴 방법이 애매하다.
|
|
// 2013년 당시에 왜 이렇게 나눠서 보내게 했는지 기억나지 않지만 일단 변경후 상황을 지켜보기로 한다 18_0411 11:26:30 sunk
|
|
|
|
// 이전 버전의 에이전트 업데이트를 위해 남겨둔다 18_0411 15:51:52 sunk
|
|
(*
|
|
function TTgClientCtxBase.Old_SendPacket_Until_1_2_3(Send: ISendPacket): Boolean;
|
|
var
|
|
nLen,
|
|
nPieceLen: Integer;
|
|
pBuf: PSocketBuffer;
|
|
pEncBuf,
|
|
pSendBuf, pSendBufPiece: TSnBytes;
|
|
nSend, nSended,
|
|
nPacketVer, nSendPieceDataPos: Integer;
|
|
CtxPacketHeader: TCtxPacketHeader;
|
|
bAbleSendEnc: Boolean;
|
|
begin
|
|
Result := false;
|
|
try
|
|
if Active then
|
|
begin
|
|
nPacketVer := GetPacketVersion;
|
|
case nPacketVer of
|
|
// 패킷 버전 1 추가 - 서버 -> 클라이언트 헤더 도입 14_0610 11:18:13 sunk
|
|
// 패킷 버전 2 추가 - 클라이언트 -> 서버 헤더 도입 14_0708 17:08:26 sunk
|
|
1, 2, 3 : nLen := Send.ToBytesDataOnly(pSendBuf);
|
|
else
|
|
nLen := Send.ToBytes(pSendBuf);
|
|
end;
|
|
|
|
if nLen = 0 then
|
|
begin
|
|
{$IFDEF DEBUG}
|
|
ASSERT(false);
|
|
{$ELSE}
|
|
_Trace('TTgClientCtxBase >> Fail!! Old_SendPacket_Until_1_2_3().. no init buffer >> IP = %s', [RemoteAddr]);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
if nPacketVer > 0 then
|
|
begin
|
|
// 패킷 버전별 헤더 도임 14_0610 11:18:13 sunk
|
|
// 패킷 버전 1 추가 - 서버 -> 클라이언트 헤더 도입 14_0610 11:18:13 sunk
|
|
// 패킷 버전 2 추가 - 클라이언트 -> 서버 헤더 도입 14_0708 17:08:26 sunk
|
|
ZeroMemory(@CtxPacketHeader, SizeOf(CtxPacketHeader));
|
|
CopyMemory(@CtxPacketHeader.sSig[0], @CTX_PACKET_SIGNATURE[1], Length(CtxPacketHeader.sSig));
|
|
CtxPacketHeader.dwId := dwSendCount_;
|
|
Inc(dwSendCount_);
|
|
if dwSendCount_ >= 400000000 then
|
|
dwSendCount_ := 0;
|
|
CtxPacketHeader.dwSize := nLen;
|
|
|
|
SetLength(pSendBufPiece, BUFFER_SIZE + LEN_CTX_PACKET_HEADER);
|
|
CopyMemory(@pSendBufPiece[0], @CtxPacketHeader, LEN_CTX_PACKET_HEADER);
|
|
nSendPieceDataPos := LEN_CTX_PACKET_HEADER;
|
|
|
|
// 암호화 정보 추가 14_0704 09:35:01 sunk
|
|
bAbleSendEnc := AbleSendEnc;
|
|
end else begin
|
|
bAbleSendEnc := false;
|
|
SetLength(pSendBufPiece, BUFFER_SIZE);
|
|
nSendPieceDataPos := 0;
|
|
end;
|
|
|
|
try
|
|
// 32kb씩 잘라서 처리해준다. 13_0516 17:21 sunk
|
|
nSended := 0;
|
|
while nSended < nLen do
|
|
begin
|
|
if not Active then
|
|
break;
|
|
|
|
nSend := nLen - nSended;
|
|
if nSend > BUFFER_SIZE then
|
|
nSend := BUFFER_SIZE;
|
|
|
|
// 암호화 처리 추가 14_0704 11:23:48 sunk
|
|
if bAbleSendEnc then
|
|
begin
|
|
if Enc_ = nil then // 추가 16_0712 13:08:53 sunk
|
|
begin
|
|
// {$IFDEF DEBUG}
|
|
// ASSERT(Enc_ <> nil, 'none.. encrypt obj ..');
|
|
// {$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
Lock;
|
|
try
|
|
case Enc_.EncKind of
|
|
ekAes256cbc,
|
|
ekAes192cbc,
|
|
ekAes128cbc:
|
|
begin
|
|
ASSERT(false, '패킷 버전 2 이하는 올수 없는 위치 입니다.');
|
|
end;
|
|
end;
|
|
|
|
pEncBuf := Enc_.EncryptBufferEx(@pSendBuf[nSended], nSend); // CryptBuffer() 리셋 옵션 사용 32kb 씩 암호화 리셋 한다.. 14_0704 18:31:12 sunk
|
|
finally
|
|
Unlock;
|
|
end;
|
|
|
|
CopyMemory(@pSendBufPiece[nSendPieceDataPos], @pEncBuf[0], nSend);
|
|
end else
|
|
CopyMemory(@pSendBufPiece[nSendPieceDataPos], @pSendBuf[nSended], nSend);
|
|
|
|
nPieceLen := nSend + nSendPieceDataPos; //Length(pSendBufPiece);
|
|
New(pBuf);
|
|
ZeroMemory(pBuf, LEN_SOCKET_BUF);
|
|
// SetLength(pBuf.Data.AllocBuf, nPieceLen);
|
|
pBuf.Data.AllocBuf := AllocMem(nPieceLen);
|
|
CopyMemory(pBuf.Data.AllocBuf, @pSendBufPiece[0], nPieceLen);
|
|
|
|
// pBuf.Data.WSABuf.buf := @pSendBufPiece[0];
|
|
pBuf.Data.WSABuf.buf := pBuf.Data.AllocBuf;
|
|
pBuf.Data.WSABuf.len := nPieceLen; //nSend + nSendPieceDataPos;
|
|
pBuf.Data.SocketDataType := dtSend;
|
|
|
|
if not PushSendBuf(pBuf) then
|
|
begin
|
|
// SetLength(pBuf.Data.AllocBuf, 0);
|
|
FreeMem(pBuf, nPieceLen);
|
|
Dispose(pBuf);
|
|
Result := false;
|
|
exit;
|
|
end;
|
|
|
|
Inc(nSended, nSend);
|
|
end;
|
|
finally
|
|
SetLength(pSendBufPiece, 0);
|
|
SetLength(pSendBuf, 0);
|
|
end;
|
|
Result := true;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(E, 'TTgClientCtxBase >> Old_SendPacket_Until_1_2_3() .. exception');
|
|
|
|
// ETgException.TraceException(Self, ...) 이런식으로 쓰면 안된다 자신이 짤려서 AV로 여기 올때 다시 자신을 참조하면 아에 뻗음 16_1013 16:22:45 sunk
|
|
end;
|
|
end;
|
|
*)
|
|
|
|
{ TTgServerBase }
|
|
|
|
Constructor TTgServerBase.Create(const nPort: Integer);
|
|
begin
|
|
Inherited Create(INVALID_SOCKET);
|
|
|
|
CtxSendBufList_ := TList<PSocketBuffer>.Create;
|
|
CtxSendBufList_.OnNotify := OnCtxSendBufNotify;
|
|
|
|
// llRcvWaitSize_ := 0;
|
|
// llSendWaitSize_ := 0;
|
|
|
|
sIPAddr_ := '';
|
|
SetPort(nPort);
|
|
hCompPort_ := 0;
|
|
wWorkThdCnt_ := 0;
|
|
sMakedPacketEncPass4Send_ := '';
|
|
|
|
evClientConnected_ := nil;
|
|
evClientDisconnected_ := nil;
|
|
|
|
// QueueRcvPacket_ := TQueue<IRcvPacket>.Create;
|
|
QueueRcvPacket_ := TQUeue<PRcvPktData>.Create;
|
|
QueueSendPacket_ := TQueue<ISendPacket>.Create;
|
|
|
|
DcClient_ := TDictionary<Pointer,TTgClientCtxBase>.Create;
|
|
|
|
ThdSvrAccept_ := nil;
|
|
ThdSvrLogic_ := nil;
|
|
ThdSvrLogic_ := nil;
|
|
ThdPingWorker_ := nil;
|
|
|
|
WorkThreadList_ := TList<TThdSocket>.Create;
|
|
WorkThreadList_.OnNotify := OnWorkThreadNotify;
|
|
|
|
SenderThreadList_ := TList<TThdSocket>.Create;
|
|
SenderThreadList_.OnNotify := OnWorkThreadNotify;
|
|
|
|
sCutIpLogDir_ := '';
|
|
sCutIpLogFName_ := '';
|
|
CutIpList_ := TStringList.Create;
|
|
end;
|
|
|
|
Destructor TTgServerBase.Destroy;
|
|
begin
|
|
SetActive(false);
|
|
|
|
FreeAndNil(CutIpList_);
|
|
FreeAndNil(SenderThreadList_);
|
|
FreeAndNil(WorkThreadList_);
|
|
FreeAndNil(DcClient_);
|
|
FreeAndNil(QueueSendPacket_);
|
|
QueueRcvPacket_.OnNotify := OnRcvDataNotify;
|
|
FreeAndNil(QueueRcvPacket_);
|
|
|
|
FreeAndNil(CtxSendBufList_);
|
|
|
|
Inherited;
|
|
end;
|
|
|
|
procedure TTgServerBase.OnCtxSendBufNotify(Sender: TObject; const Item: PSocketBuffer;
|
|
Action: TCollectionNotification);
|
|
begin
|
|
case Action of
|
|
cnAdded: ;
|
|
cnRemoved:
|
|
begin
|
|
// SetLength(Item.Data.AllocBuf, 0);
|
|
if (Item.Data.AllocBuf <> nil) and (Item.Data.WSABuf.len > 0) then
|
|
begin
|
|
FreeMem(Item.Data.AllocBuf, Item.Data.WSABuf.len);
|
|
Dispose(Item);
|
|
end;
|
|
end;
|
|
cnExtracted: ;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase.AddCtxSendBuf(pBuf: PSocketBuffer);
|
|
begin
|
|
Lock;
|
|
try
|
|
CtxSendBufList_.Add(pBuf);
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase.RemoveCtxSendBuf(pBuf: PSocketBuffer);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Lock;
|
|
try
|
|
i := CtxSendBufList_.IndexOf(pBuf);
|
|
if i <> -1 then
|
|
CtxSendBufList_.Delete(i);
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase.ClearCtxSendBuf;
|
|
begin
|
|
Lock;
|
|
try
|
|
CtxSendBufList_.Clear;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase.OnClientNotify(Sender: TObject; const Item: TTgClientCtxBase;
|
|
Action: TCollectionNotification);
|
|
begin
|
|
case Action of
|
|
cnAdded: ;
|
|
cnRemoved: Item.Free;
|
|
cnExtracted: ;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase.OnWorkThreadNotify(Sender: TObject; const Item: TThdSocket;
|
|
Action: TCollectionNotification);
|
|
begin
|
|
case Action of
|
|
cnAdded: ;
|
|
cnRemoved: Item.Free;
|
|
cnExtracted: ;
|
|
end;
|
|
end;
|
|
|
|
//procedure TTgServerBase.IncSendWaitSize(llSize: LONGLONG);
|
|
//begin
|
|
// Lock;
|
|
// try
|
|
// Inc(llSendWaitSize_, llSize);
|
|
// finally
|
|
// Unlock;
|
|
// end;
|
|
//end;
|
|
//
|
|
//procedure TTgServerBase.DecSendWaitSize(llSize: LONGLONG);
|
|
//begin
|
|
// Lock;
|
|
// try
|
|
// Dec(llSendWaitSize_, llSize);
|
|
// if llSendWaitSize_ < 0 then
|
|
// llSendWaitSize_ := 0;
|
|
// finally
|
|
// Unlock;
|
|
// end;
|
|
//end;
|
|
|
|
procedure TTgServerBase.CloseClientCtx(aClientCtx: TTgClientCtxBase);
|
|
begin
|
|
if IsValidClient(aClientCtx) then
|
|
begin
|
|
try
|
|
PostQueuedCompletionStatus(hCompPort_, 0, NativeInt(aClientCtx), Pointer(STOP_WORK));
|
|
// _RemoveClient(aClientCtx);
|
|
except
|
|
//
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TTgServerBase.CreateClientContext(aSocket: TSocket): TTgClientCtxBase;
|
|
begin
|
|
Result := TTgClientCtxBase.Create(Self, aSocket);
|
|
end;
|
|
|
|
//function TTgServerBase.CheckAcceptBan(sIp: String): Boolean;
|
|
//begin
|
|
//
|
|
//end;
|
|
|
|
function TTgServerBase.CheckCutIp(sIp: String): Boolean;
|
|
var
|
|
sLog,
|
|
sLogPath: String;
|
|
dtNow: TDateTime;
|
|
begin
|
|
Result := false;
|
|
|
|
if (CutIpList_ <> nil) and (CutIpList_.Count > 0) then
|
|
begin
|
|
Result := CutIpList_.IndexOf(sIp) <> -1;
|
|
|
|
try
|
|
if Result and (sCutIpLogDir_ <> '') and (sCutIpLogFName_ <> '') then
|
|
begin
|
|
dtNow := Now;
|
|
sLogPath := sCutIpLogDir_ + FormatDateTime('yyyy\mm\', dtNow);
|
|
if not DirectoryExists(sLogPath) then
|
|
if not ForceDirectories(sLogPath) then exit;
|
|
sLogPath := sLogPath + FormatDateTime('yy_mmdd ', dtNow) + sCutIpLogFName_;
|
|
sLog := FormatDateTime('[yyyy-mm-dd hh:nn:ss] ', Now) + Format('Cut - %s', [sIp]);
|
|
WriteLnFileEndUTF8(sLogPath, sLog);
|
|
end;
|
|
except
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase.LoadCutIpList;
|
|
var
|
|
sPath: String;
|
|
begin
|
|
sCutIpLogDir_ := '';
|
|
sCutIpLogFName_ := '';
|
|
CutIpList_.Clear;
|
|
|
|
sPath := GetRunExePathDir + FILE_CUT_IPS;
|
|
if FileExists(sPath) then
|
|
begin
|
|
try
|
|
CutIpList_.LoadFromFile(sPath, TEncoding.UTF8);
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(Self, E, 'Fail .. LoadCutIpList()');
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase.SetActive(bVal: Boolean);
|
|
begin
|
|
if bActive_ <> bVal then
|
|
begin
|
|
bActive_ := bVal;
|
|
if not bActive_ then
|
|
begin
|
|
_Close;
|
|
|
|
QueueSendPacket_.Clear;
|
|
QueueRcvPacket_.OnNotify := OnRcvDataNotify;
|
|
try
|
|
QueueRcvPacket_.Clear;
|
|
finally
|
|
QueueRcvPacket_.OnNotify := nil;
|
|
end;
|
|
dtActive_ := 0;
|
|
sIPAddr_ := '';
|
|
|
|
// Lock;
|
|
// try
|
|
// llRcvWaitSize_ := 0;
|
|
// llSendWaitSize_ := 0;
|
|
// finally
|
|
// Unlock;
|
|
// end;
|
|
|
|
sCutIpLogDir_ := '';
|
|
sCutIpLogFName_ := '';
|
|
CutIpList_.Clear;
|
|
|
|
_Trace('Server Active Off.');
|
|
end else begin
|
|
LoadCutIpList;
|
|
|
|
sIPAddr_ := GetHostIP;
|
|
_Open;
|
|
dtActive_ := now;
|
|
|
|
_Trace('Server Active On.');
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase.SetPort(const nPort: Integer);
|
|
begin
|
|
if bActive_ then
|
|
raise ECrmServer.Create('서버가 활성화 중입니다. 포트 설정을 할 수 없습니다.');
|
|
|
|
if nPort_ <> nPort then
|
|
nPort_ := nPort;
|
|
end;
|
|
|
|
procedure TTgServerBase.ClientConnectedEvent(aClient: TTgClientCtxBase);
|
|
begin
|
|
if Assigned(evClientConnected_) then
|
|
evClientConnected_(Self, aClient);
|
|
end;
|
|
|
|
procedure TTgServerBase.ClientDisconnectedEvent(aClient: TTgClientCtxBase);
|
|
begin
|
|
if Assigned(evClientDisconnected_) then
|
|
evClientDisconnected_(Self, aClient);
|
|
end;
|
|
|
|
function TTgServerBase.IsValidClient(aClient: TTgClientCtxBase): Boolean;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := DcClient_.ContainsKey(aClient);
|
|
finally
|
|
UnLock;
|
|
end;
|
|
end;
|
|
|
|
function TTgServerBase.GetClientEnumerator: TClientEnumerator;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := DcClient_.Values.GetEnumerator;
|
|
finally
|
|
Unlock
|
|
end;
|
|
end;
|
|
|
|
function TTgServerBase.GetClientCount: Integer;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := DcClient_.Count;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
function TTgServerBase.GetRcvWaitSize: LONGLONG;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := llRcvWaitSize_;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
//function TTgServerBase.GetSendWaitSize: LONGLONG;
|
|
//begin
|
|
// Lock;
|
|
// try
|
|
// Result := llSendWaitSize_;
|
|
// finally
|
|
// Unlock;
|
|
// end;
|
|
//end;
|
|
|
|
procedure TTgServerBase.SetPacketEncInfo(aPktEncKind: TTgEncKind; const sPktEncPass: String);
|
|
var
|
|
enc: TTgEncrypt;
|
|
sMakePass: String;
|
|
pBuf: TBytes;
|
|
begin
|
|
try
|
|
if Active then
|
|
raise ECrmServer.Create('서버가 활성화되어 있는 상태에서는 할 수 없습니다.');
|
|
|
|
Inherited;
|
|
|
|
case aPktEncKind of
|
|
ekAes256cbc,
|
|
ekAes192cbc,
|
|
ekAes128cbc :
|
|
begin
|
|
sMakePass := 'kwa' + sPktEncPass + '!7v*' + #0;
|
|
Guard(enc, TTgEncrypt.Create(ENC_PASSPASS, aPktEncKind));
|
|
pBuf := enc.EncryptBufferEx(@sMakePass[1], Length(sMakePass) * 2);
|
|
sMakedPacketEncPass4Send_ := ConvBinToStr(@pBuf[0], Length(pBuf));
|
|
end
|
|
else
|
|
sMakedPacketEncPass4Send_ := '';
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ECrmServer.TraceException(Self, E, 'Fail .. SetPacketEncInfo');
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase.SendPacketEncConfirm(aCtx: TTgClientCtxBase);
|
|
var
|
|
Send: ISendPacket;
|
|
PktEncKind: TTgEncKind;
|
|
begin
|
|
try
|
|
if IsValidClient(aCtx) then
|
|
begin
|
|
PktEncKind := GetPktEncKind;
|
|
// if PktEncKind <> ekNone then // none 이어도 해당 패킷을 보내야 전송이 활성화 된다 18_0117 14:52:51 sunk
|
|
begin
|
|
Send := TTgPacket.Create(TOC_CONFIRM_PACKET_ENCRYPT);
|
|
Send.I['K'] := NativeInt(PktEncKind);
|
|
Send.I['SPV'] := PACKET_VERSION;
|
|
|
|
aCtx.SendPacket(Send);
|
|
end;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ECrmServer.TraceException(Self, E, 'Fail .. SendPacketEncConfirm()');
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase.SendPacketEncInfo(aCtx: TTgClientCtxBase);
|
|
var
|
|
Send: ISendPacket;
|
|
PktEncKind: TTgEncKind;
|
|
begin
|
|
try
|
|
if IsValidClient(aCtx) then
|
|
begin
|
|
PktEncKind := GetPktEncKind;
|
|
// if PktEncKind <> ekNone then
|
|
begin
|
|
Send := TTgPacket.Create(TOC_UPDATE_PACKET_ENCRYPT);
|
|
Send.I['K'] := NativeInt(PktEncKind);
|
|
Send.S['J'] := sMakedPacketEncPass4Send_;
|
|
aCtx.SendPacket(Send);
|
|
end;
|
|
aCtx.bSendPktEncInfo_ := true;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ECrmServer.TraceException(Self, E, 'Fail .. SendPacketEncInfo');
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase.ProcessTossFail(aCtx: TTgClientCtxBase; aRcv: IRcvPacket);
|
|
begin
|
|
// 토스 실패..
|
|
end;
|
|
|
|
procedure TTgServerBase.ProcessRcvPacket(aCtx: TTgClientCtxBase; aRcv: IRcvPacket);
|
|
|
|
procedure process_TOC_CLIENT_INFO;
|
|
begin
|
|
with aCtx.ClientInfo_, aRcv do
|
|
begin
|
|
nType := I['Type'];
|
|
sVer := S['Ver'];
|
|
sComName := S['ComName'];
|
|
sAccount := S['Account'];
|
|
sWinVer := S['WinVer'];
|
|
BootDT := D['BootDT'];
|
|
sIpRAddr := S['rIP'];
|
|
sIpAddr := aCtx.RemoteAddr;
|
|
sMacAddr := S['MacAddr'];
|
|
sMacAddrs := S['MacAddrs'];
|
|
ConnDT := Now;
|
|
end;
|
|
SendPacketEncConfirm(aCtx);
|
|
ProcessClientConnection(aCtx);
|
|
end;
|
|
|
|
procedure process_TOC_CONFIRM_PACKET_ENCRYPT;
|
|
var
|
|
EncKind: TTgEncKind;
|
|
begin
|
|
EncKind := TTgEncKind(aRcv.I['K']);
|
|
aCtx.wPktVer_ := aRcv.I['CPV'];
|
|
SendPacketEncInfo(aCtx);
|
|
if EncKind <> ekNone then
|
|
begin
|
|
// send 암호화 설정
|
|
aCtx.AbleSendEnc := true;
|
|
aCtx.SetPacketEncInfo(EncKind, GetPktEncPass);
|
|
end;
|
|
end;
|
|
|
|
procedure process_TOC_UPDATE_PACKET_ENCRYPT;
|
|
begin
|
|
Lock;
|
|
try
|
|
{$IFDEF DEBUG}
|
|
ASSERT(Enc_ <> nil, 'no enc obj...');
|
|
{$ELSE}
|
|
if Enc_ = nil then
|
|
begin
|
|
// 이렇게 되면 정상이 아닌데...
|
|
// 테스트 중에 이런 현상이 있어서 아래처럼 보완 처리 14_0708 09:26:40 sunk
|
|
aCtx.AbleSendEnc := true;
|
|
aCtx.SetPacketEncInfo(GetPktEncKind, GetPktEncPass);
|
|
end;
|
|
{$ENDIF}
|
|
finally
|
|
Unlock;
|
|
end;
|
|
// rcv 복호화 설정
|
|
aCtx.AbleRcvDec := true;
|
|
end;
|
|
|
|
procedure process_TOC_REQUEST_CONFIRM_PACKET_ENCRYPT;
|
|
begin
|
|
SendPacketEncConfirm(aCtx);
|
|
end;
|
|
|
|
begin
|
|
try
|
|
if not IsValidClient(aCtx) then
|
|
exit;
|
|
|
|
case aRcv.Command of
|
|
TOC_CLIENT_INFO : process_TOC_CLIENT_INFO;
|
|
TOC_CONFIRM_PACKET_ENCRYPT : process_TOC_CONFIRM_PACKET_ENCRYPT;
|
|
TOC_UPDATE_PACKET_ENCRYPT : process_TOC_UPDATE_PACKET_ENCRYPT;
|
|
TOC_REQUEST_CONFIRM_PACKET_ENCRYPT : process_TOC_REQUEST_CONFIRM_PACKET_ENCRYPT;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ECrmServer.TraceException(Self, E, Format('ProcessRcvPacket() .. Cmd = %d', [aRcv.Command]));
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase.ProcessFileQueuePacket(aCtx: TTgClientCtxBase; aRcv: IRcvPacket);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TTgServerBase.ProcessFileQueuePacket(aCtx: TTgClientCtxBase; pRcvBuf: Pointer; pRcvLen: Integer);
|
|
begin
|
|
//
|
|
end;
|
|
|
|
procedure TTgServerBase.PushClientClose(aCtx: TTgClientCtxBase);
|
|
begin
|
|
ThdClientClose_.PushCloseCtx(aCtx);
|
|
end;
|
|
|
|
procedure TTgServerBase._RegisterClient(aClient: TTgClientCtxBase);
|
|
begin
|
|
{$IFDEF DEBUG2} _Trace('_RegisterClient() .. '); {$ENDIF}
|
|
if IsValidClient(aClient) then
|
|
exit;
|
|
|
|
{$IFDEF DEBUG2} _Trace('_RegisterClient() .. 1'); {$ENDIF}
|
|
Lock;
|
|
try
|
|
DcClient_.Add(aClient, aClient);
|
|
|
|
// 소켓을 위한 윈도우 메세지와 처리 할 네트워크 이벤트 등록 2011-09-09 sunk
|
|
|
|
// FD_ACCEPT 클라이언트가 접속하면 윈도우 메시지를 발생시킨다.
|
|
// FD_READ 데이터 수신이 가능하면 윈도우 메시지를 발생시킨다.
|
|
// FD_WRITE 데이터 송신이 가능하면 윈도우 메시지를 발생시킨다.
|
|
// FS_CLOSE 상대가 접속을 종료하면 윈도우 메시지를 발생시킨다.
|
|
// FS_CONNECT 접속이 완료되면 윈도우 메시지를 발생시킨다.
|
|
// WSAAsyncSelect(aSocket.SocketHandle, hSocket_, WM_SOCKET_CLOSE, FD_CLOSE);
|
|
finally
|
|
Unlock;
|
|
end;
|
|
{$IFDEF DEBUG2} _Trace('_RegisterClient() .. 2'); {$ENDIF}
|
|
|
|
ClientConnectedEvent(aClient);
|
|
{$IFDEF DEBUG2} _Trace('_RegisterClient() .. 3'); {$ENDIF}
|
|
end;
|
|
|
|
procedure TTgServerBase._RemoveClient(aClient: TTgClientCtxBase);
|
|
begin
|
|
if not IsValidClient(aClient) then
|
|
exit;
|
|
|
|
Lock;
|
|
try
|
|
DcClient_.Remove(aClient);
|
|
finally
|
|
UnLock;
|
|
end;
|
|
|
|
if not Assigned(DcClient_.OnValueNotify) then
|
|
begin
|
|
ClientDisconnectedEvent(aClient);
|
|
// FreeAndNil(aClient); ThdFreeCloseCtx 에서 처리 19_0905 08:51:10 sunk
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase._Open;
|
|
|
|
procedure CreateWorkThread;
|
|
var
|
|
i: Integer;
|
|
SystemInfo: TSystemInfo;
|
|
begin
|
|
GetSystemInfo(SystemInfo);
|
|
wWorkThdCnt_ := SystemInfo.dwNumberOfProcessors * 2;
|
|
ASSERT(wWorkThdCnt_ > 0);
|
|
|
|
ThdSvrLogic_ := TThdServerLogic.Create(Self);
|
|
|
|
for i := 0 to wWorkThdCnt_ - 1 do
|
|
WorkThreadList_.Add(TThdServerWork.Create(Self));
|
|
|
|
// for i := 0 to (SystemInfo.dwNumberOfProcessors * 2) - 1 do
|
|
// SenderThreadList_.Add(TThdServerSender.Create(Self));
|
|
|
|
ThdPingWorker_ := TThdPingWorker.Create(Self);
|
|
end;
|
|
|
|
var
|
|
nLen: Integer;
|
|
|
|
begin
|
|
try
|
|
ClearCtxSendBuf;
|
|
hCompPort_ := CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
|
|
if hCompPort_ = 0 then
|
|
begin
|
|
{$IFDEF DEBUG}
|
|
ASSERT(false);
|
|
{$ENDIF}
|
|
nLastError_ := GetLastError;
|
|
_Trace('TTgServerBase._Open() >> CreateIoCompletionPort() > %s, ErrorCode = %d',
|
|
[SysErrorMessage(GetLastError), nLastError_]);
|
|
raise ECrmSocket.CreateFmt('%s, Error = %d', [SysErrorMessage(GetLastError), nLastError_]);
|
|
end;
|
|
|
|
CreateWorkThread;
|
|
|
|
hSocket_ := WSASocket(PF_INET, SOCK_STREAM, 0, nil, 0, WSA_FLAG_OVERLAPPED);
|
|
if hSocket_ = INVALID_SOCKET then
|
|
begin
|
|
{$IFDEF DEBUG}
|
|
ASSERT(false);
|
|
{$ENDIF}
|
|
nLastError_ := GetLastError;
|
|
_Trace('TTgServerBase._Open() >> WSASocket() > %s, ErrorCode = %d',
|
|
[SysErrorMessage(GetLastError), nLastError_]);
|
|
raise ECrmSocket.CreateFmt('%s, Error = %d', [SysErrorMessage(GetLastError), nLastError_]);
|
|
end;
|
|
|
|
nRcvBufSize_ := -1;
|
|
nSendBufSize_ := -1;
|
|
nLen := SizeOf(nRcvBufSize_);
|
|
|
|
// nRcvBufSize_ := 32768;
|
|
// nSendBufSize_ := 32768;
|
|
// setsockopt(hSocket_, SOL_SOCKET, nSendBufSize_, @nRcvBufSize_, nLen);
|
|
|
|
getsockopt(hSocket_, SOL_SOCKET, SO_RCVBUF, @nRcvBufSize_, nLen);
|
|
getsockopt(hSocket_, SOL_SOCKET, SO_SNDBUF, @nSendBufSize_, nLen);
|
|
|
|
ZeroMemory(@SockAddr_, SizeOf(SockAddr_));
|
|
with TSockAddrIn(SockAddr_) do
|
|
begin
|
|
sin_family := AF_INET;
|
|
sin_port := htons(nPort_);
|
|
sin_addr.S_addr := INADDR_ANY;
|
|
end;
|
|
_Check_WSAGetLastError(bind(hSocket_, SockAddr_, SizeOf(SockAddr_)), 'bind');
|
|
_Check_WSAGetLastError(listen(hSocket_, SOMAXCONN), 'listen');
|
|
|
|
ThdClientClose_ := TThdClientClose.Create(Self);
|
|
ThdSvrAccept_ := TThdServerAccept.Create(Self);
|
|
except
|
|
_Close;
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase._Close;
|
|
begin
|
|
if hCompPort_ <> 0 then
|
|
begin
|
|
PostQueuedCompletionStatus(hCompPort_, 0, 0, Pointer(STOP_WORK));
|
|
CloseHandle(hCompPort_);
|
|
hCompPort_ := 0;
|
|
Sleep(2000);
|
|
end;
|
|
|
|
if hSocket_ <> INVALID_SOCKET then
|
|
begin
|
|
closesocket(hSocket_);
|
|
hSocket_ := INVALID_SOCKET;
|
|
end;
|
|
|
|
if Assigned(ThdPingWorker_) then
|
|
begin
|
|
ThdPingWorker_.StopThread;
|
|
FreeAndNil(ThdPingWorker_);
|
|
end;
|
|
|
|
WorkThreadList_.Clear;
|
|
|
|
if Assigned(ThdSvrLogic_) then
|
|
begin
|
|
ThdSvrLogic_.StopThread;
|
|
FreeAndNil(ThdSvrLogic_);
|
|
end;
|
|
|
|
// if Assigned(ThdServerSendWork_) then
|
|
// begin
|
|
// ThdServerSendWork_.StopThread;
|
|
// FreeAndNil(ThdServerSendWork_);
|
|
// end;
|
|
|
|
if Assigned(ThdSvrAccept_) then
|
|
begin
|
|
ThdSvrAccept_.StopThread;
|
|
FreeAndNil(ThdSvrAccept_);
|
|
end;
|
|
|
|
if Assigned(ThdClientClose_) then
|
|
begin
|
|
ThdClientClose_.StopThread;
|
|
FreeAndNil(ThdClientClose_);
|
|
end;
|
|
|
|
DcClient_.OnValueNotify := OnClientNotify;
|
|
try
|
|
DcClient_.Clear;
|
|
finally
|
|
DcClient_.OnValueNotify := nil;
|
|
end;
|
|
|
|
ClearCtxSendBuf;
|
|
end;
|
|
|
|
procedure TTgServerBase._Accept;
|
|
var
|
|
hClientSocket: TSocket;
|
|
SockAddr: TSockAddr;
|
|
// hClientWinSocket: TSocket;
|
|
aCtx: TTgClientCtxBase;
|
|
nSockAddrLen: Integer;
|
|
|
|
|
|
function CheckCut: Boolean;
|
|
var
|
|
SockAddrIn: TSockAddr;
|
|
nSize: Integer;
|
|
begin
|
|
Result := false;
|
|
|
|
try
|
|
nSize := SizeOf(SockAddrIn);
|
|
_Check_WSAGetLastError(getpeername(hClientSocket, SockAddrIn, nSize), 'getpeername');
|
|
// sIpAddr := String(inet_ntoa(TSockAddrIn(SockAddrIn).sin_addr));
|
|
// if sIpAddr = '192.168.99.99' then
|
|
// Result := true;
|
|
|
|
Result := CheckCutIp(inet_ntoa(TSockAddrIn(SockAddrIn).sin_addr));
|
|
except
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
procedure CloseClientContext;
|
|
begin
|
|
if Assigned(aCtx) then
|
|
begin
|
|
{$IFDEF DEBUG2} _Trace('_Accept() .. CloseClientContext()'); {$ENDIF}
|
|
// ThdServerDisconnWork_.EnqDisconnCtx(aCtx);
|
|
_RemoveClient(aCtx);
|
|
end else
|
|
if hClientSocket <> INVALID_SOCKET then
|
|
closesocket(hClientSocket);
|
|
end;
|
|
|
|
begin
|
|
if (Handle = INVALID_SOCKET) or (hCompPort_ = 0) then
|
|
exit;
|
|
|
|
nSockAddrLen := SizeOf(SockAddr);
|
|
hClientSocket := Winapi.Winsock2.accept(Handle, @SockAddr, @nSockAddrLen);
|
|
if hClientSocket <> INVALID_SOCKET then
|
|
begin
|
|
if not bActive_ or CheckCut then
|
|
begin
|
|
closesocket(hClientSocket);
|
|
exit;
|
|
end;
|
|
aCtx := nil;
|
|
try
|
|
{$IFDEF DEBUG2} _Trace('_Accept() .. 1'); {$ENDIF}
|
|
aCtx := CreateClientContext(hClientSocket);
|
|
{$IFDEF DEBUG2} _Trace('_Accept() .. 2'); {$ENDIF}
|
|
if CreateIoCompletionPort(hClientSocket, hCompPort_, DWORD(aCtx), 0) = 0 then
|
|
begin
|
|
CloseClientContext;
|
|
exit;
|
|
end;
|
|
{$IFDEF DEBUG2} _Trace('_Accept() .. 3'); {$ENDIF}
|
|
except
|
|
CloseClientContext;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase._QueueRcvPacket(pRcvData: PRcvPktData);
|
|
begin
|
|
Lock;
|
|
try
|
|
Inc(llRcvWaitSize_, pRcvData.nLen);
|
|
QueueRcvPacket_.Enqueue(pRcvData);
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
function TTgServerBase._DequeueRcvPacket: PRcvPktData;
|
|
begin
|
|
Result := nil;
|
|
Lock;
|
|
try
|
|
if QueueRcvPacket_.Count > 0 then
|
|
Result := QueueRcvPacket_.Dequeue;
|
|
|
|
if Result <> nil then
|
|
begin
|
|
if llRcvWaitSize_ >= Result.nLen then
|
|
Dec(llRcvWaitSize_, Result.nLen)
|
|
else
|
|
llRcvWaitSize_ := 0;
|
|
end else
|
|
llRcvWaitSize_ := 0;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase.OnRcvDataNotify(Sender: TObject; const Item: PRcvPktData; Action: TCollectionNotification);
|
|
begin
|
|
case Action of
|
|
cnAdded: ;
|
|
cnRemoved:
|
|
begin
|
|
FreeMem(Item.pData);
|
|
Dispose(Item);
|
|
end;
|
|
cnExtracted: ;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase.QueueSendPacket(SendPacket: ISendPacket);
|
|
begin
|
|
Lock;
|
|
try
|
|
QueueSendPacket_.Enqueue(SendPacket);
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
function TTgServerBase._DequeueSendPacket: ISendPacket;
|
|
begin
|
|
Result := nil;
|
|
Lock;
|
|
try
|
|
if QueueSendPacket_.Count > 0 then
|
|
begin
|
|
Result := QueueSendPacket_.Dequeue;
|
|
end;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TTgServerBase._ProcessTossPacket(aCtx: TTgClientCtxBase; RcvPacket: IRcvPacket);
|
|
var
|
|
SendCtx: TTgClientCtxBase;
|
|
Send: ISendPacket;
|
|
begin
|
|
SendCtx := TTgClientCtxBase(RcvPacket.Toss);
|
|
if IsValidClient(SendCtx) then
|
|
begin
|
|
Send := TTgPacket.Create(RcvPacket);
|
|
Send.Toss := LONGLONG(aCtx);
|
|
// Send.Socket := ClientContext;
|
|
// _QueueSendPacket(Send);
|
|
SendCtx.SendPacket(Send);
|
|
end else
|
|
ProcessTossFail(aCtx, RcvPacket);
|
|
end;
|
|
|
|
{ TThdSocket }
|
|
|
|
Constructor TThdSocket.Create(aServer: TTgServerBase);
|
|
begin
|
|
Server_ := aServer;
|
|
Inherited Create;
|
|
StartThread;
|
|
end;
|
|
|
|
{ TThdAccept }
|
|
|
|
procedure TThdServerAccept.Execute;
|
|
begin
|
|
while not Terminated and not bWorkStop_ do
|
|
begin
|
|
if Server_.Active then
|
|
begin
|
|
Server_._Accept;
|
|
end else
|
|
Sleep(50);
|
|
end;
|
|
end;
|
|
|
|
{ TThdServerWork }
|
|
|
|
procedure TThdServerWork.Execute;
|
|
var
|
|
nvClient: NativeUInt;
|
|
dwTransfered: DWORD;
|
|
pSocketBuf: PSocketBuffer;
|
|
aCtx: TTgClientCtxBase;
|
|
// RcvPacket: IRcvPacket;
|
|
bProcDisconnect: Boolean;
|
|
|
|
procedure ProcessDisconnected(n: Integer);
|
|
begin
|
|
try
|
|
if Server_.IsValidClient(aCtx) then
|
|
Server_.PushClientClose(aCtx);
|
|
except
|
|
//..
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
while not Terminated and not bWorkStop_ and Server_.Active do
|
|
if Server_.CompletionPort <> 0 then
|
|
begin
|
|
nvClient := 0;
|
|
if not GetQueuedCompletionStatus(Server_.CompletionPort,
|
|
dwTransfered,
|
|
nvClient,
|
|
POVERLAPPED(pSocketBuf),
|
|
INFINITE) then
|
|
begin
|
|
if (GetLastError <> $40) or (nvClient <> 0) then
|
|
begin
|
|
// aCtx := TTgClientCtxBase(nvClient);
|
|
ProcessDisconnected(1);
|
|
Server_.RemoveCtxSendBuf(pSocketBuf);
|
|
end;
|
|
continue;
|
|
end;
|
|
|
|
aCtx := TTgClientCtxBase(nvClient);
|
|
|
|
if DWORD(pSocketBuf) = STOP_WORK then
|
|
begin
|
|
if nvClient = 0 then
|
|
begin
|
|
// 서버 active off
|
|
break;
|
|
end else begin
|
|
// 클라이언트 접속 종료
|
|
ProcessDisconnected(2);
|
|
continue;
|
|
end;
|
|
end;
|
|
|
|
if not Server_.Active then
|
|
break;
|
|
|
|
if dwTransfered = 0 then
|
|
begin
|
|
ProcessDisconnected(3);
|
|
continue;
|
|
end;
|
|
|
|
if pSocketBuf = nil then
|
|
begin
|
|
{$IFDEF DEBUG}
|
|
if GetLastError <> $40 then
|
|
ASSERT(false);
|
|
{$ENDIF}
|
|
ProcessDisconnected(4);
|
|
continue;
|
|
end;
|
|
|
|
bProcDisconnect := false;
|
|
try
|
|
if not Server_.IsValidClient(aCtx) then
|
|
continue;
|
|
|
|
try
|
|
try
|
|
case pSocketBuf.Data.SocketDataType of
|
|
dtRcv :
|
|
begin
|
|
aCtx.UpdateLastRcvTime;
|
|
aCtx.MakeRcvPacket(dwTransfered);
|
|
|
|
if not aCtx.ReadyRecv then
|
|
begin
|
|
{$IFDEF DEBUG2} _Trace('Fail .. aCtx.ReadyRecv()'); {$ENDIF}
|
|
bProcDisconnect := true;
|
|
continue;
|
|
end;
|
|
end;
|
|
dtSend :
|
|
begin
|
|
aCtx.RemoveSendBuf(pSocketBuf);
|
|
if not aCtx.ProcessSendBuf then
|
|
begin
|
|
{$IFDEF DEBUG2} _Trace('Fail .. aCtx.ProcessSendBuf()'); {$ENDIF}
|
|
bProcDisconnect := true;
|
|
continue;
|
|
end;
|
|
end;
|
|
else ASSERT(false);
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
bProcDisconnect := true;
|
|
continue;
|
|
end;
|
|
end;
|
|
finally
|
|
if bProcDisconnect then
|
|
begin
|
|
ProcessDisconnected(5);
|
|
end;
|
|
end;
|
|
except
|
|
continue;
|
|
end;
|
|
end else Sleep(500);
|
|
end;
|
|
|
|
{ TThdClientClose }
|
|
|
|
Constructor TThdClientClose.Create(aServer: TTgServerBase);
|
|
begin
|
|
Inherited Create(aServer);
|
|
DcCloseCtx_ := TDictionary<Pointer,TTgClientCtxBase>.Create;
|
|
qCloseCtx_ := TQueue<TTgClientCtxBase>.Create;
|
|
qCloseCtx_.OnNotify := OnQCtxNotify;
|
|
end;
|
|
|
|
Destructor TThdClientClose.Destroy;
|
|
begin
|
|
FreeAndNIl(qCloseCtx_);
|
|
DcCloseCtx_.OnValueNotify := OnDCtxNotify;
|
|
FreeAndNil(DcCloseCtx_);
|
|
Inherited;
|
|
end;
|
|
|
|
procedure TThdClientClose.OnQCtxNotify(Sender: TObject; const Item: TTgClientCtxBase;
|
|
Action: TCollectionNotification);
|
|
begin
|
|
case Action of
|
|
cnAdded :
|
|
begin
|
|
if not DcCloseCtx_.ContainsKey(Item) then
|
|
DcCloseCtx_.Add(Item, Item);
|
|
end;
|
|
cnRemoved : ;
|
|
cnExtracted : ;
|
|
end;
|
|
end;
|
|
|
|
procedure TThdClientClose.OnDCtxNotify(Sender: TObject; const Item: TTgClientCtxBase;
|
|
Action: TCollectionNotification);
|
|
begin
|
|
case Action of
|
|
cnAdded : ;
|
|
cnRemoved : Item.Free;
|
|
cnExtracted : ;
|
|
end;
|
|
end;
|
|
|
|
procedure TThdClientClose.PushCloseCtx(aCtx: TTgClientCtxBase);
|
|
var
|
|
bAdd: Boolean;
|
|
begin
|
|
try
|
|
bAdd := false;
|
|
Lock;
|
|
try
|
|
if not DcCloseCtx_.ContainsKey(aCtx) then
|
|
begin
|
|
bAdd := true;
|
|
qCloseCtx_.Enqueue(aCtx);
|
|
end;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
|
|
if bAdd then
|
|
begin
|
|
{$IFDEF DEBUG2} _Trace('PushCloseCtx() .. Add FreeCtx., Ptr=%d', [NativeUInt(aCtx)]); {$ENDIF}
|
|
Server_._RemoveClient(aCtx);
|
|
aCtx.Close;
|
|
aCtx.UpdateLastRcvTime;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(Self, E, 'Fail .. PushCloseCtx()');
|
|
end;
|
|
end;
|
|
|
|
procedure TThdClientClose.Execute;
|
|
const
|
|
FREE_WAIT_SEC = 180;
|
|
var
|
|
FreeCtx: TTgClientCtxBase;
|
|
wStep: WORD;
|
|
begin
|
|
while not Terminated and not WorkStop do
|
|
begin
|
|
wStep := 0;
|
|
try
|
|
Lock;
|
|
try
|
|
if qCloseCtx_.Count > 0 then
|
|
FreeCtx := qCloseCtx_.Dequeue
|
|
else
|
|
FreeCtx := nil;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
|
|
wStep := 1;
|
|
|
|
if FreeCtx <> nil then
|
|
begin
|
|
wStep := 2;
|
|
if SecondsBetween(FreeCtx.LastRcvTime, Now) < FREE_WAIT_SEC then
|
|
begin
|
|
wStep := 3;
|
|
Lock;
|
|
try
|
|
qCloseCtx_.Enqueue(FreeCtx);
|
|
finally
|
|
Unlock;
|
|
end;
|
|
Sleep(100);
|
|
end else begin
|
|
wStep := 4;
|
|
Lock;
|
|
try
|
|
DcCloseCtx_.Remove(FreeCtx);
|
|
finally
|
|
Unlock;
|
|
end;
|
|
wStep := 5;
|
|
|
|
FreeAndNil(FreeCtx);
|
|
end;
|
|
end else Sleep(500);
|
|
|
|
wStep := 6;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(Self, E, Format('Fail .. Execute(), Step=%d', [wStep]));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ TThdServerLogic }
|
|
|
|
Constructor TThdServerLogic.Create(aServer: TTgServerBase);
|
|
begin
|
|
Inherited Create(aServer);
|
|
Priority := tpTimeCritical;
|
|
end;
|
|
|
|
procedure TThdServerLogic.Execute;
|
|
var
|
|
Rcv: IRcvPacket;
|
|
pRcvData: PRcvPktData;
|
|
begin
|
|
while not Terminated and not bWorkStop_ and Server_.Active do
|
|
begin
|
|
try
|
|
pRcvData := Server_._DequeueRcvPacket;
|
|
if pRcvData <> nil then
|
|
begin
|
|
case pRcvData.PacketKind of
|
|
pkNormal :
|
|
try
|
|
Rcv := TTgPacket.Create(pRcvData.Ctx, pRcvData.pData, pRcvData.nLen);
|
|
if Rcv.Toss = 0 then
|
|
Server_.ProcessRcvPacket(pRcvData.Ctx, Rcv)
|
|
else
|
|
Server_._ProcessTossPacket(pRcvData.Ctx, Rcv);
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(Self, E, 'Normal packet - fail ..');
|
|
end;
|
|
pkFileQueue :
|
|
try
|
|
Server_.ProcessFileQueuePacket(pRcvData.Ctx, pRcvData.pData, pRcvData.nLen);
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(Self, E, 'FileQueue packet - fail ..');
|
|
end;
|
|
end;
|
|
FreeMem(pRcvData.pData);
|
|
Dispose(pRcvData);
|
|
end else Sleep(10);
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
ETgException.TraceException(Self, E, 'Fail .. Execute()');
|
|
Sleep(1000);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ TThdPingWorker }
|
|
|
|
Constructor TThdPingWorker.Create(aServer: TTgServerBase);
|
|
begin
|
|
Inherited Create;
|
|
Server_ := aServer;
|
|
StartThread;
|
|
end;
|
|
|
|
procedure TThdPingWorker.Execute;
|
|
const
|
|
PING_CYCLE = 120;
|
|
var
|
|
dwTick: DWORD;
|
|
enum: TClientEnumerator;
|
|
dtNow: TDateTime;
|
|
Ctx: TTgClientCtxBase;
|
|
begin
|
|
dwTick := GetTickCount;
|
|
|
|
while not Terminated and not WorkStop do
|
|
begin
|
|
Sleep(100);
|
|
|
|
try
|
|
if ((GetTickCount - dwTick) div 1000) > 60 then
|
|
begin
|
|
dtNow := Now;
|
|
|
|
Guard(enum, Server_.GetClientEnumerator);
|
|
|
|
while enum.MoveNext do
|
|
begin
|
|
try
|
|
if not Server_.IsValidClient(enum.Current) then
|
|
break;
|
|
|
|
if not (enum.Current is TTgClientCtxBase) then
|
|
begin
|
|
{$IFDEF DEBUG}
|
|
ASSERT(false, 'Invalid Ctx ...');
|
|
{$ELSE}
|
|
_Trace('Execute() .. Invalid Ctx ...');
|
|
{$ENDIF}
|
|
continue;
|
|
end;
|
|
|
|
Ctx := TTgClientCtxBase(enum.Current);
|
|
if not Ctx.bSendPktEncInfo_ and
|
|
(SecondsBetween(Ctx.dtCreate_, dtNow) > 600) then
|
|
begin
|
|
// 패킷 전송 처리 시간 체크 후 종료 처리
|
|
Server_.CloseClientCtx(Ctx);
|
|
_Trace('Execute() .. Close old connection .. (NoSendPktEncInfo)');
|
|
end else
|
|
if SecondsBetween(Ctx.LastRcvTime, dtNow) > PING_CYCLE then
|
|
begin
|
|
if Server_.IsValidClient(Ctx) then
|
|
begin
|
|
if Ctx.ClientInfo_.nType = 0 then
|
|
Server_.CloseClientCtx(Ctx)
|
|
else
|
|
Ctx.SendPacket(TTgPacket.Create(TOC_PING));
|
|
end;
|
|
end;
|
|
except
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
dwTick := GetTickCount;
|
|
end;
|
|
except
|
|
Sleep(1000);
|
|
dwTick := GetTickCount;
|
|
continue;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Initialization
|
|
if not Assigned(_CS) then
|
|
_CS := TCriticalSection.Create;
|
|
|
|
Finalization
|
|
if Assigned(_CS) then
|
|
FreeAndNil(_CS);
|
|
|
|
end.
|