[데이터스냅] 데이터스냅 클라이언트에서 TCP/IP로 접속 시 ConnectTimeout이 적용되지 않는 경우 대처방법

2015.11.04 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 속성이 모두 적용됩니다.

완성된 소스코드는 저작권 문제(?)로 올려놓지 못하니 양해부탁드리며 필요하신 분들은 개인적으로 요청 해주세요.


저작자 표시 비영리 동일 조건 변경 허락
신고

험프리.김현수 파이어몽키

  1. Blog Icon

    비밀댓글입니다