2. 테스트 클라이언트 만들기
2-1. DataModule 선언부 및 이벤트
테스트 클라이언트를 만들기 위해 New 메뉴의 Project 트리에서 Application을 선택한다. 컴파일 옵션은 앞의 미들웨어 서버와 동일하게 설정해서 실행 파일의 용량이 커지지않게 한다. 메인 폼의 이름을 적당히 정하고 Position을 지정한다.(※ poScreenCenter로 지정해서 화면 가운데에서 실행 됨.)
이번에는 New메뉴에서 TDataModule을 선택해서 생성한다. TDataModule은 서버와의 데이터 송/수신 및 클라이언트에서 사용하는 공통 사항들을 묶어서 관리하기위한 용도로 사용한다. TDataModule의 Name은 "dmSIMLaz"로 정하고 아래 화면과 같이 컴퍼넌트를 배치한 후 Name을 지정하고 "simlaz_dm.pas" 파일로 저장한다.
TDataModule에 ini File 처리를 위한 "IniFiles"와 TCP/IP 통신용 공통변수 및 함수를 가지고 있는 "simlaz_lib", SHA256 값을 가져오기위한 "IdHash" / "IdHashSHA" / "IdSSLOpenSSLHeaders", Lock 처리를 위한 "syncobjs", XML 처리를 위한 "XMLDatapacketReader" 등을 uses 구문에 추가 한다.
unit simlaz_dm;
{$mode objfpc}{$H+}
interface
uses
..., IniFiles, syncobjs, simlaz_lib, XMLDatapacketReader, IdGlobal, IdHash, IdHashSHA, IdSSLOpenSSLHeaders;
서버와 통신을 하거나 클라이언트 프로그램 전반에 사용될 변수와 함수들을 정의한다.
{ TdmSIMLaz }
TdmSIMLaz = class(TDataModule)
IdAntiFrz: TIdAntiFreeze;
IdTCPConn: TIdTCPClient;
qrTmp: TBufDataset;
tmDummy: TTimer;
procedure DataModuleCreate(Sender: TObject);
procedure DataModuleDestroy(Sender: TObject);
procedure IdTCPConnConnected(Sender: TObject);
procedure IdTCPConnDisconnected(Sender: TObject);
procedure tmDummyTimer(Sender: TObject);
private
public
u_Lock: TCriticalSection;
u_Encoding: IIdTextEncoding;
u_uid, // 사용자ID
u_uname: string; // 사용자명
u_admin_flag: Boolean; // 관리자 여부
function UF_SIMLazSnd(ACmd: UInt8; var ACont: TIdBytes): Boolean; // 데이터 전송
function UF_SIMLazRcv(var ACmd: UInt8; out ACont: TIdBytes): Boolean; // 데이터 수신
function UF_SIMLazSHA(const AValue: string): string; // SHA256
function UF_SIMLazDummy: Boolean; // Dummy
function UF_SIMLazList: string; // 접속자 목록
function UF_SIMLazVer(AID: string): string; // 버전 정보
function UF_SIMLazLogin(AID, APasswd: string): Boolean; // 로그인
function UF_SIMLazExec(AQuery: string): Int32; // Query 실행
procedure UP_SIMLazOpen(ADataSet: TCustomBufDataset; AQuery: string; const ALoadFields: Boolean); // 데이터 가져오기
procedure UP_SIMLazFile(AID, AFileName: string); // 파일 가져오기
end;
DataModule이 생성될 때 Lock 처리를 위한 객체를 생성하고 접속할 서버의 IP와 Port를 ini 파일에서 가져와 Indy TCP Client에 할당하고 접속한다. DataModule이 Destroy될 때 서버에 접속이 되어있으면 접속을 종료하고 사용했던 Lock 객체를 Free 시킨다. 사용자 컴퓨터에 설치할 때 미들웨어 서버의 IP나 Port 정보를 노출시키고 싶지 않을 때는 INI 파일의 데이터를 암호화해서 저장해야 한다. (※ 본 예제에서는 INI 파일의 암호화 처리는 하지 않음.)
procedure TdmSIMLaz.DataModuleCreate(Sender: TObject);
begin
if IdTCPConn.Connected then IdTCPConn.Disconnect;
u_Lock := TCriticalSection.Create;
u_Encoding := IndyTextEncoding_UTF8;
with TIniFile.Create(ExtractFilePath(ParamStr(0)) + 'simlaz_client.ini') do
begin
try
IdTCPConn.Host := ReadString('Setup', 'IP', '');
IdTCPConn.Port := ReadInteger('Setup', 'Port', 0);
finally
Free;
end;
end;
IdTCPConn.Connect;
end;
procedure TdmSIMLaz.DataModuleDestroy(Sender: TObject);
begin
if IdTCPConn.Connected then IdTCPConn.Disconnect;
FreeAndNil(u_Lock);
end;
서버에 접속한 후에는 서버로부터 접속되었다는 정보를 수신하고 제대로 수신 되었으면 접속 유지를 위한 Dummy 전송용 타이머를 구동시킨다. 접속 종료후에는 Dummy 전송용 타이머를 종료시킨다.
procedure TdmSIMLaz.IdTCPConnConnected(Sender: TObject);
var
l_Cmd: UInt8;
l_Data: TIdBytes;
begin
l_Data := nil;
l_Cmd := smlc_CONNECT;
if UF_SIMLazRcv(l_Cmd, l_Data) then
begin
tmDummy.Tag := 0;
tmDummy.Enabled := True;
end
else IdTCPConn.Disconnect;
end;
procedure TdmSIMLaz.IdTCPConnDisconnected(Sender: TObject);
begin
tmDummy.Enabled := False;
end;
Dummy 타이머는 1분마다 체크하게끔 Interval을 60000으로 설정하고 5분마다 Dummy 데이터를 보낸다.
procedure TdmSIMLaz.tmDummyTimer(Sender: TObject);
begin
tmDummy.Tag := tmDummy.Tag + 1;
if tmDummy.Tag >= 5 then
begin
tmDummy.Enabled := False;
try
if UF_SIMLazDummy then tmDummy.Enabled := True;
tmDummy.Tag := 0;
except
IdTCPConn.IOHandler.InputBuffer.Clear;
IdTCPConn.Disconnect;
end;
end;
end;
// Dummy
function TdmSIMLaz.UF_SIMLazDummy: Boolean;
var
l_Cmd: UInt8;
l_Data: TIdBytes;
begin
Result := False;
if not IdTCPConn.Connected then
begin
MessageDlg(Application.Title, ERR_SIMLAZ302, mtError, [mbOK], 0);
Exit;
end;
u_Lock.Enter;
try
l_Data := nil;
l_Cmd := smlc_DUMMY;
if not UF_SIMLazSnd(l_Cmd, l_Data) then Exit;
l_Data := nil;
Result := UF_SIMLazRcv(l_Cmd, l_Data);
finally
u_Lock.Leave;
end;
end;