파이어몽키
[데이터스냅] 데이터스냅 클라이언트에서 TCP/IP로 접속 시 ConnectTimeout이 적용되지 않는 경우 대처방법
험프리.김현수
2015. 11. 4. 15:55
(이글은 RAD 스튜디오 10 시애틀 기준으로 테스트하고, 작성되었습니다. 다른 버전 사용자들은 이슈 발생여부를 먼저 확인하고 아래 내용을 참고하시기 바랍니다.)
이 글은 데이터스냅 클라이언트에서 TCP/IP로 접속 시 Timeout이 적용되지 않는 이슈에 대해 원인을 확인하고 회피하는 내용을 소개합니다.
데이터스냅 클라이언트 프로그램에서 서버에 접속하기 위해 SQLConnection을 사용합니다.
연결방식(CommunicationProtocol)은 tcp/ip, http, https가 있습니다.
Timeout 속성은 CommnunicationTimeout(송수신 시간제한)과 ConnectTimeout(연결 시간제한) 두가지가 있습니다.
결론적으로 Timeout 속성이 적용되지 않는 경우는 tcp/ip 연결방식으로 연결한 경우 두가지 Timeout 속성이 모두 적용되지 않습니다.(http, https 연결방식에서는 Timeout이 정상 적용됩니다.)
이미 엠바카데로 QC에 이슈가 등록되었지만 적용되지 않는다는 답변으로 아쉽게 마무리되었습니다.
http://qc.embarcadero.com/wc/qcmain.aspx?d=80954#sthash.9Apdxn5i.dpuf
제일 손쉬운 조치방법은 CommunicationProtocol을 http로 변경하는 것입니다.(물론 데이터스냅 서버에서 http를 지원해야 합니다.)
만약, TCP/IP로 연결하실 분들은 아래 내용을 참고하세요.
(RAD 스튜디오 10 시애틀에서 진행했습니다. 다른 버전 사용자분들은 참고해서 수정하시기 바랍니다.)
총 2개의 Unit을 수정하면됩니다.
아래 2개 파일을 프로젝트 파일 경로로 복사 후 프로젝트에 추가(Add) 합니다.(즉, 해당 프로젝트에서만 수정된 내용이 반영됩니다.)
- Data.DbxSocketChannelNative.pas(C:\Program Files (x86)\Embarcadero\Studio\17.0\source\data\dbx)
- IPPeerClient.pas(C:\Program Files (x86)\Embarcadero\Studio\17.0\source\indy\implementation)
Data.DbxSocketChannelNative.pas(C:\Program Files (x86)\Embarcadero\Studio\17.0\source\data\dbx)
procedure TDBXIdTCPLayer.Open(const DBXProperties: TDBXProperties);
var
timeout: string;
commTimeout: string;
LIPVersionStr: string;
LIPVersion: Integer;
begin
Close;
FIPImplementationID := DbxProperties[TDBXPropertyNames.IPImplementationID];
if FIdSocket = nil then
FIdSocket := CreateClientSocket;
FIdSocket.Host := DbxProperties[TDBXPropertyNames.HostName];
FIdSocket.Port := DbxProperties.GetInteger(TDBXPropertyNames.Port);
LIPVersionStr := DbxProperties[TDBXPropertyNames.CommunicationIPVersion].Trim;
LIPVersion := GetEnumValue(TypeInfo(TIPVersionPeer), LIPVersionStr);
if LIPVersion > -1 then
FIdSocket.IPVersion := TIPVersionPeer(LIPVersion)
else
FIdSocket.IPVersion := TIPVersionPeer.IP_IPv4;
timeout := DbxProperties[TDBXPropertyNames.ConnectTimeout];
if timeout = '' then
ConnectTimeout := 0
else
ConnectTimeout := StrToInt(timeout);
commTimeout := DbxProperties[TDBXPropertyNames.CommunicationTimeout];
if commTimeout = '' then
CommunicationTimeout := 0
else
CommunicationTimeout := StrToInt(commTimeout);
{ Added Humphrey}
// ConnectTimeout과 CommunicationTimeout을 FIdSocket에 전달
if Supports(FIdSocket, IIPPeerClientSetTimeout) then
begin
(FIdSocket as IIPPeerClientSetTimeout).SetConnectTimeout(ConnectTimeout);
(FIdSocket as IIPPeerClientSetTimeout).SetReadTimeout(CommunicationTimeout);
end;
{ Added Humphrey}
FIdSocket.UseNagle := false;
FIdSocket.Connect;
FConnected := false;
end;
위 메소드는 tcp/ip로 데이터스냅 클라이언트가 접속하는 일부 소스코드입니다.
주의깊게 볼 부분은 ConnectTimeout과 CommunicationTimeout 값을 가져오고 FIdSocket에 전달하는 내용이 누락되었습니다.
그래서 "Added Humphery" 주석으로 감싸진 내용을 추가했습니다.
uses 절에 IPPeerClient 추가해야 합니다.
IPPeerClient.pas(C:\Program Files (x86)\Embarcadero\Studio\17.0\source\indy\implementation)
interface
{ Added Humphrey}
type
IIPPeerClientSetTimeout = interface
['{9924134C-9C7D-464F-8ABE-F3E1E408C566}']
procedure SetConnectTimeout(const ATimeout: Integer);
procedure SetReadTimeout(const ATimeout: Integer);
end;
{ Added Humphrey}
상단 interface 아래에 IIPPeerClientSettimeout 인터페이스 추가
TIdTCPClientPeerIP = class(TIdClassIP, IIPTCPClient, IIPObject{ Added Humphrey}, IIPPeerClientSetTimeout{ Added Humphrey})
private
FTCPClient: TIdTCPClientIP;
FIOHandler: IIPIOHandler;
FSocket: IIPIOHandlerSocket;
protected
function Connected: Boolean;
function GetSocket: IIPIOHandlerSocket;
procedure SetIOHandler(Handler: IIPIOHandler);
function GetIOHandler: IIPIOHandler;
function GetBoundIP: string;
procedure SetBoundIP(IP: string);
function GetHost: string;
procedure SetHost(LHost: string);
function GetPort: TIPPortPeer;
procedure SetPort(LPort: TIPPortPeer);
procedure SetIPVersion(const AValue: TIPVersionPeer);
function GetIPVersion: TIPVersionPeer;
function GetUseNagle: Boolean;
procedure SetUseNagle(Use: Boolean);
procedure Connect;
procedure Disconnect;
function GetManagedIOHandler: Boolean;
procedure SetManagedIOHandler(AManagedIOHandler: Boolean);
public
function GetObject: TObject;
function GetIPImplementationID: string;
constructor Create(AOwner: TComponent);
destructor Destroy; override;
{ Added Humphrey}
procedure SetConnectTimeout(const ATimeout: Integer);
procedure SetReadTimeout(const ATimeout: Integer);
{ Added Humphrey}
end;TIdTCPClientPeerIP 클래스 선언부에 위 IIPPeerClientSetTimeout 인터페이스 위임, 인터페이스 메소드 추가
구현부에 타임아웃 적용하도록 구현
{ Added Humphrey}
procedure TIdTCPClientPeerIP.SetConnectTimeout(const ATimeout: Integer);
begin
FTCPClient.ConnectTimeout := ATimeout;
end;
procedure TIdTCPClientPeerIP.SetReadTimeout(const ATimeout: Integer);
begin
FTCPClient.ReadTimeout := ATimeout;
end;
{ Added Humphrey}위와 같이 조치하면 TSQLConnection으로 DataSnap을 TCP/IP로 연결하는 경우에도 Timeout 속성이 모두 적용됩니다.
완성된 소스코드는 저작권 문제(?)로 올려놓지 못하니 양해부탁드리며 필요하신 분들은 개인적으로 요청 해주세요.