5

接続に問題がある場合、dbExpressドライバーはTDBXErrorをスローしますが、ソケットエラーコードは含まれていません。メッセージは単純です:

ホスト「exampledb.local」へのネットワーク要求を完了できません。接続の確立に失敗しました

このタイプの例外が発生したときに、根本的なソケットエラーを取得する方法はありますか?

スタックトレースは次のとおりです。

main thread ($5934):
0061aa59 +051 example.exe DBXCommon    447  +0 TDBXContext.Error
00817f14 +10c example.exe DBXDynalink  796 +21 TDBXMethodTable.RaiseError
00818553 +013 example.exe DBXDynalink  949  +1 TDBXDynalinkConnection.CheckResult
00818744 +050 example.exe DBXDynalink 1048  +4 TDBXDynalinkConnection.DerivedOpen
0061750f +007 example.exe DBXCommon    447  +0 TDBXConnection.Open
00612fed +0f5 example.exe DBXCommon    447  +0 TDBXConnectionFactory.GetConnection
00612ef1 +005 example.exe DBXCommon    447  +0 TDBXConnectionFactory.GetConnection
0062c08f +26f example.exe SqlExpr              TSQLConnection.DoConnect
005d9019 +039 example.exe DB                   TCustomConnection.SetConnected
005d8fd4 +004 example.exe DB                   TCustomConnection.Open
0062b98f +01b example.exe SqlExpr              TSQLConnection.CheckConnection
0062ebdf +01f example.exe SqlExpr              TCustomSQLDataSet.CheckConnection
0062efe4 +04c example.exe SqlExpr              TCustomSQLDataSet.OpenCursor
005e91d5 +055 example.exe DB                   TDataSet.SetActive
005e901c +004 example.exe DB                   TDataSet.Open
4

1 に答える 1

0

(DelphiXE2および'gds32.dll' 10.0.1.335(ansi)がこの回答で使用されています)

dbExpressは、プロバイダー固有の操作をドライバーに委任する高レベルのフレームワークです。これらのプロバイダー固有のドライバーはすべて、同じ基本的な共通機能を公開しているため、この共通機能の範囲外の特定の情報を取得することはできません。

エラー報告に関しては、ドライバーは2つの関数をエクスポートします。mysql、mssql、その他のドライバーのいずれであっても、これらの関数はとDBXBase_GetErrorMessageLengthですDBXBase_GetErrorMessage。ドライバーは、クライアントライブラリから問題のほとんどを取得し、これらの関数を介してその情報をフレームワークに報告します。すでに報告されていることを超えて、データベースサーバーへの接続またはその他の操作で何がうまくいかなかったかについてのより具体的な詳細を取得することはできません。

したがって、winsockエラー情報を含めるかどうかはクライアントライブラリ次第です。質問に含まれているエラーの原因であるベース間ドライバーの場合、この情報はすでに含まれています(存在する場合)。

これは、既存のサーバーと、データベースサーバーによってリッスンされていないポートに接続する試みの例です。

  Connection := TSQLConnection.Create(nil);
  Connection.DriverName := 'Interbase';
  Connection.Params.Values['Database'] := 'existingserver/80:database';
  try
    Connection.Open;
  except
    on E: TDBxError do begin
      writeln(E.Message + sLineBreak);

出力は次のとおりです。

ホスト「existingserver:80」へのネットワーク要求を完了できません。
接続の確立に失敗しました。

お気づきのとおり、これは質問のエラーメッセージとまったく同じです。テキストにwinsockエラーがないことに注意してください。これは、ネットワークプロトコルに関する限り、winsockエラーが発生しないため、接続は実際に成功します。


これは既存のサーバーへの試みですが、リッスンされていないポートへの試みです。

  Connection := TSQLConnection.Create(nil);
  Connection.DriverName := 'Interbase';
  Connection.Params.Values['Database'] := 'existingserver/81:database';
  try
    Connection.Open;
  except
    on E: TDBxError do begin
      writeln(E.Message + sLineBreak);

返されたエラーは次のとおりです。

ホスト「existingserver:81」へのネットワーク要求を完了できません。
接続の確立に失敗しました。
不明なWin32エラー10060

今回は、特定のポートのリスナーがないため、接続が失敗します。クライアントライブラリが10060を解決できない理由はわかりませんが、これがwinsockエラーです。


ローカルホストの場合、リッスンされていないポート:

ホスト「localhost:3051」へのネットワーク要求を完了できません。
接続の確立に失敗しました。
ターゲットマシンが積極的に拒否したため、接続できませんでした。

ここに10061があります。


解決できないホストに接続しようとしても、gds32.dllはAPIエラーを報告しません。ライブラリがホストを解決する方法や、エラーコードが含まれていない理由はわかりませんが、エラーメッセージは冗長です。

「nonexistingserver」をホストするためのネットワーク要求を完了できません。
ホストマシンを見つけることができませんでした。
指定された名前がhostsファイルまたはドメインネームサービスに見つかりませんでした。



クライアントライブラリを直接使用する場合、APIエラーのみが発生する可能性があります。既存のサーバーと閉じたポートへの接続が試行される次のプログラムを調べます。

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  system.sysutils,
  data.dbxcommon,
  data.sqlexpr,
  data.dbxinterbase;

function isc_attach_database(status_vector: PLongint; db_name_length: Smallint;
    db_name: PAnsiChar; db_handle: PPointer; parm_buffer_length: Smallint;
    parm_buffer: PAnsiChar): Longint; stdcall; external 'gds32.dll';

function isc_interprete(buffer: PAnsiChar; var status_vector: Pointer): Longint;
    stdcall; external 'gds32.dll';

var
  Connection: TSQLConnection;

  StatusVector: array[0..19] of Longint;
  Handle: PPointer;
  Error: array[0..255] of AnsiChar;
  IntrStatus: Pointer;
  s: string;
begin
  try
    Connection := TSQLConnection.Create(nil);
    Connection.DriverName := 'Interbase';
    Connection.Params.Values['Database'] := 'server/3051:database'; // closed port
    try
      Connection.Open;
    except
      on E: TDBxError do begin
        writeln(E.Message + sLineBreak);

        if E.ErrorCode = TDBXErrorCodes.ConnectionFailed then begin

          Handle := nil;
          if isc_attach_database(@StatusVector, 0,
                      PAnsiChar(AnsiString(Connection.Params.Values['Database'])),
                      @Handle, 0, nil) <> 0 then begin

            IntrStatus := @StatusVector;
            s := '';
            while isc_interprete(Error, IntrStatus) <> 0 do begin
              s := s + AnsiString(Error) + sLineBreak;
              if PLongint(IntrStatus)^ = 17 then  // isc_arg_win32
                s := s + ' --below is an api error--' + sLineBreak +
                     Format('%d: %s', [PLongint(Longint(IntrStatus) + 4)^,
                     SysErrorMessage(PLongint(Longint(IntrStatus) + 4)^)]) +
                     sLineBreak;
            end;
            Writeln(s);
          end;
        end else
          raise;
      end;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  readln;
end.

どの出力:

ホスト「sever:3051」へのネットワーク要求を完了できません。
接続の確立に失敗しました。
不明なWin32エラー10060

ホスト「server:3051」へのネットワーク要求を完了できません。
接続の確立に失敗しました。
--以下はAPIエラーです-10060 :接続されたパーティが 一定期間後に
適切に応答しなかったために接続の試行が失敗したか、接続された ホストが 不明なWin32エラー10060に応答できなかったために接続の確立に失敗しました



最初の段落はdbxが報告するものです。2番目の段落は、APIエラーコードとテキストの挿入を含む「gds32.dll」から取得したものです。それ以外は同じです。

上記は大まかなデモンストレーションであり、適切に入力するには「interbase.h」を使用します。また、考えられるAPIエラーの選択の詳細については、「ステータスベクトルの解析」または一般的に「エラー状態の処理」を参照してください。



いずれにせよ、ご覧のとおり、特定の情報を取得できるかどうかは、dbxがデータベースサーバーへの接続に使用するクライアントライブラリに完全に依存します。

一般的なケースでは、使用しているデータベースサーバーとは別にwinsockエラー情報を取得するには、データベース接続を開こうとする前にサーバーにソケットを接続してみて、これが成功した場合にのみテスト接続を閉じます。次に、データベースへの接続を続行します。これを行うには、任意のライブラリまたはベアAPIを使用できます。

簡単な例を次に示します。

function TestConnect(server: string; port: Integer): Boolean;

  procedure WinsockError(step: string);
  begin
    raise Exception.Create(Format('"%s" fail. %d: %s:',
        [step, WSAGetLastError, SysErrorMessage(WSAGetLastError)]));  
  end;

var
  Error: Integer;
  Data: TWSAData;
  Socket: TSocket;
  SockAddr: TSockAddrIn;
  Host: PHostEnt;
begin
  Result := False;
  Error := WSAStartup(MakeWord(1, 1), Data);
  if Error = 0 then begin
    try
      Socket := winsock.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
      if Socket <> INVALID_SOCKET then begin
        try
          Host := gethostbyname(PAnsiChar(AnsiString(server)));
          if Host <> nil then begin
            SockAddr.sin_family := AF_INET;
            SockAddr.sin_addr.S_addr := Longint(PLongint(Host^.h_addr_list^)^);
            SockAddr.sin_port := htons(port);
            if connect(Socket, SockAddr,  SizeOf(SockAddr)) <> 0 then
              WinsockError('connect')
            else
              Result := True;
          end else
            WinsockError('gethostbyname');
        finally
          closesocket(Socket);
        end;  
      end else
        WinsockError('socket');
    finally  
      WSACleanup;
    end;  
  end else
    raise Exception.Create('winsock initialization fail');
end;

次のようなものを使用できます。

if TestConnect('server', 3050) then
  //
于 2015-01-31T14:27:05.060 に答える