Q&A

  • C/S에서 연결이 끊어질 때 알수있는 방법
혹 답변이 있나싶어 조회해 보니 저와 똑같은 고민의 질문은 있는데 답변이 없네요. 고수님들의 도움을 부탁드립니다.

아래는 질문 내용입니다.


음 C/S프로그램을 운영중인데요
만일에 서버에서 SQL SERVER를 중지시킬때는
전 클라이언트가 접속이 안되면서 클라이언트에는 General SQL Error가
발생하겠죠
그런데 일부 몇몇 클라이언트가 서버를 찾지 못하는 경우에도
클라이언트에 General SQL Error가 발생을 하는데요

실제적으로 클라이언트모듈에 있는 TDatabase가 현재
연결되어있는지 없는지를 알아내려면 어떻게 해야하나요???

서버에서 SQL Server를 Stop을 시켜도
클라이언트의 TDatabase  의 connected 프로러티는
계속 True로 나오더라구요

클라이언트 서버프로그램이 작동을 해서 일단 Server를 찾으면
이 프로퍼티는 프로그램 종료될때까지
프로퍼티가 True로 불변인건지...

현재 클라이언트프로그램과 서버사이의 연결상태를
StatusBar에 타이머컴퍼넌트를 써서 계속 체크메세지를 보여주고
싶은데요

고수님들의 고견바랍니다.
2  COMMENTS
  • Profile
    구창민 2003.01.21 20:12
    안녕하세요~ 구창민입니다.

    아래 내용은 오래전에 영대씨가 오라클이 사용할 수 있는지 여부를

    조사하는 루틴을 만드신 건데, 참고 하시고 즐거운 프로그래밍 하시길~~


    unit main;

    interface

    uses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
      StdCtrls, Winsock;

    type
      TForm1 = class(TForm)
        Label1: TLabel;
        B_GetResponse: TButton;
        E_OracleIP: TEdit;
        procedure FormActivate(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
        procedure B_GetResponseClick(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
        procedure DOclosesocket(var socket_id: Tsocket);
      end;

    const
      IPPORT_ORACLE1 = 1521;
      // IPPORT_ORACLE2 = 1526;
      
    var
      Form1: TForm1;
      FirstActive: Boolean;

    implementation
    {$R *.DFM}

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      FirstActive := False;
    end;

    procedure TForm1.FormActivate(Sender: TObject);
    var
      WData: TWSAData;
    begin
      Application.ProcessMessages;
      if FirstActive then  {맨 처음의 active 이벤트일때만 TCP/IP 초기화}
        System.Exit;
      FirstActive := True;

      // WSAStartup은 응용 프로그램이 원도우즈 소겟을 이용할 때 최초로 호출하여
      // 다른 소켓 함수를 사용할 수 있도록 초기화 한다.(마무리는 WSACleanup사용)
      // 이 함수는 응용 프로그램에 필요한 원도우즈 소켓 API의 버전을 알려주고,
      // 소켓 내부의 구현 사항을 리턴한다.
      if WSAStartup($0101, WData) <> 0  then // winsock 1.1 이상만 지원
      begin
        MessageDlg('네트워크 오류: 원도우즈에 WINSOCK.DLL(WSOCK32.DLL)이 구동되지 않습니다.',mtInformation,[mbOk],0);
        PostMessage(Handle, WM_CLOSE, 0, 0); // Close;
      end;
    end;

    procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    begin
      // 블록킹(blocking)된 소켓함수가 있다면 이를 취소시킨다
      // 어느 경우에나 WSACancelBlockingCall() 이 호출되면 Block이 진행 중이던
      // 원래의 함수는 에러로 리턴하고, 이 때 에러 코드는 WSAEINTR이 된다
      // 이식성이 높은 응용 프로그램을 개발하기 위하여 WSACancelBlockingCall() 후에
      // closesocket() 이외의 다른 소켓 함수 호출은 하지 않는 것이 좋다.
      if WSAISBlocking then // 어떤 소켓 함수가 Block 상태에 있는 가를 검사
        WSACancelBlockingCall;

      // 어떤 소켓 함수가 Block 중인 경우, 다른 메세지 처리 도중 또는 Blocking 처리 함수(WSASetBlockingHook()함수로 지정)
      // 내에서 WSACleanup()을 해야 한다면 WSACancelBlockingCall() 로 Block이 진행 중인
      // 함수를 먼저 취소시킨 후 WSACleanup()을 호출해야 한다
      WSACleanup;
      
      CanClose := True;
    end;


    //오라클 접속여부 확인 버튼클릭 이벤트
    procedure TForm1.B_GetResponseClick(Sender: TObject);
    var
      ORACLE_IP: String; // 호스트 IP
      Address: DWord;    // 이진의 IP주소(4 bytes)
      Phe: PHostEnt;     // HostEntry 구조체
      oracle_in: TSockAddrIn;
      oracle_skt: Tsocket;
      delay_time: Longint;
    begin
      ORACLE_IP := E_OracleIP.Text;

      // 인터넷의 IP주소를 의미하는 문자열은 네 개의 숫자와 그들을 구분하는 도트(".')
      // 로 구성된다. 그래서 inet_addr은 그 문자열에 해당하는 네트워크 바이트 순서로
      // 된 이진의 IP주소를 리턴한다(4 bytes).
      Address := inet_addr(PChar(ORACLE_IP));

      // inet_addr은 인자로 전달된 도트 표현의 IP주소에서 4개의 숫자 가운데
      // 255를 넘는 값이 있다거나 기타 이유로 이진 IP주소로 변환될 수 없는
      // 문제가 있는 문자열인 경우 INADDR_NONE 값을 리턴한다.
      if (Address = INADDR_NONE) then
      begin
        // GetHostByName 함수는 인자에 해당하는 호스트의 이름을 가진 호스트에 대한
        // hostent구조에 대한 포인터를 리턴한다.
        // GetHostByName 함수는 보통 네트워크 데이터베이스의 hosts파일을 참조하거나,
        // 도메인 네임 서버(domain name server)에다 주소에 해당하는 호스트를 찾는
        // 메시지를 보내고 그에 대한 응답을 기다린다.
        Phe := GetHostByName(PChar(ORACLE_IP));
        if Phe <> Nil then
          Address  := Longint(plongint(Phe^.h_addr_list^)^); {이진의 IP주소(4 bytes)}
      end
      else
      begin
        // GetHostByAddr 함수는 인자로 주어진 호스트의 주소와 형태를 가진 호스트를 찾아
        // hostent구조에 대한 포인터를 리턴한다("4, PF_INET"는 변함 없음)
        // Phe := GetHostByAddr(@Address, 4, PF_INET);
        // if Phe = Nil then
        //   Address := INADDR_NONE;
      end;

      if Address = INADDR_NONE then
      begin
        MessageDlg('ORACLE 서버를 알 수 없습니다.  IP가 정확한지 확인하세요.',mtInformation,[mbOk],0);
        System.Exit;
      end;

      // 서버연결을 위한 상대방의 주소 지정
      with oracle_in do
      begin
        FillChar(oracle_in, SizeOf(oracle_in), #0);
        sin_family          := PF_INET; // 주소 영역은 현재 PF_INET 뿐이다
        sin_addr.s_addr     := Address; // 이진의 IP주소(네트워크 바이트 순서의 4 bytes)
        sin_port            := htons(IPPORT_ORACLE1); // 네트워크 바이트 순서의 ORACLE1 port 번호(well-known port numer)
      end;

      Application.ProcessMessages;;

      oracle_skt := socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
      if oracle_skt = INVALID_SOCKET then
      begin
        MessageDlg('네트워크 오류: 소켓핸들을 얻을 수 없습니다.',mtInformation,[mbOk],0);
        System.Exit;
      end;

      delay_time := 1000;
      
      if setsockopt(oracle_skt, SOL_SOCKET, SO_RCVTIMEO, @delay_time, sizeof(delay_time)) <> 0 then
      begin
        MessageDlg('네트워크 오류: 소켓의 옵션을 바꿀 수 없습니다.',mtInformation,[mbOk],0);
        DOclosesocket(oracle_skt);
        System.Exit;
      end;
      
      if Connect(oracle_skt, oracle_in, sizeof(oracle_in)) <> 0 then
      begin
        MessageDlg('ORACLE 서버('+E_OracleIP.Text+')에 접속할 수 없습니다.',mtInformation,[mbOk],0);
        DOclosesocket(oracle_skt);
        System.Exit;
      end;

      MessageDlg('ORACLE 서버('+E_OracleIP.Text+')에 접속할 수 있습니다.',mtInformation,[mbOk],0);
      DOclosesocket(oracle_skt);
    end;

    {parameter로 지정한 소켓을 강제로 즉시 닫는다(블록킹 해제됨)}
    procedure TForm1.DOclosesocket(var socket_id: TSocket);
    var
      lingerinfo: Tlinger;
    begin
      // 블록킹(blocking)된 소켓함수가 있다면 이를 취소시킨다
      // 어느 경우에나 WSACancelBlockingCall() 이 호출되면 Block이 진행 중이던
      // 원래의 함수는 에러로 리턴하고, 이 때 에러 코드는 WSAEINTR이 된다
      // 이식성이 높은 응용 프로그램을 개발하기 위하여 WSACancelBlockingCall() 후에
      // closesocket() 이외의 다른 소켓 함수 호출은 하지 않는 것이 좋다.
      if socket_id <> INVALID_SOCKET then
        if WSAISBlocking then // 어떤 소켓 함수가 Block 상태에 있는 가를 검사
          WSACancelBlockingCall;

    // 아래 comment는 절대 열지말것 - 자료가 전송중일때(버퍼가 비어있지 않을때) 강제종료될 수 있으므로  
    (*
      lingerinfo.l_onoff  := 1; {소켓을 강제로 즉시 종료}
      lingerinfo.l_linger := 0; {시간지연 없음}
      // setsockopt() 는 지정된 소켓의 형태, 상태와 관계 없이 소켓 옵션을 소켓에
      // 지정한다
      SetSockopt(socket_id, SOL_SOCKET, SO_LINGER, Pchar(@lingerinfo), sizeof(lingerinfo));
    *)
      
      CloseSocket(socket_id);
      socket_id := INVALID_SOCKET;
    end;

    end.



  • Profile
    고재남 2003.01.21 22:13
    ^^;

    신속한 답변에 감사드립니다.

    열심히 분석해 보겠습니다. 항상 행복하시고 건강하시길~~~