3
votes

I am trying to use a Client-Server Datasnap based architecture. The client is inside an Android App that connects by Wifi with the Server Program which runs in a PC.

This are the server and client features:

Server Side:

Server Methods

TSQLConnection

  • Driver: Firebird.
  • KeepConnection: true.

Server Container

TDSServer

  • Queuesize: 100
  • ChannelresponseTimeout: 3000

TDSTCPServerTransport

  • BufferKBSize: 32
  • KeepAliveEnablement: kaDisabled
  • MaxThreads: 30
  • PoolSize: 10
  • Port: 211

Client Side

Main

TSQLConnection

  • Driver: Datasnap
  • ConnectTimeout:2000
  • CommunicationTimeOut:5000
  • DBXConnection constructor:

The function:

function TFrm_Principal.GetServerMethods1Client: TServerMethods1Client;
begin
  Conexion.Close;
  Conexion.Open;
  if FServerMethods1Client = nil then
  begin
    FServerMethods1Client := TServerMethods1Client.Create
      (Conexion.DBXConnection, FInstanceOwner);
  end;
  result := FServerMethods1Client;
end;

ClientClasses

  • Command Example (which off course has its equivalent at the server side)

The function:

function TServerMethods2Client.validaEstado(factura: string): Boolean;
begin
  try
    if FvalidaEstadoCommand = nil then
    begin
      FvalidaEstadoCommand := FDBXConnection.CreateCommand;
      FvalidaEstadoCommand.CommandType := TDBXCommandTypes.DSServerMethod;
      FvalidaEstadoCommand.Text := 'TServerMethods1.validaEstado';
      FvalidaEstadoCommand.Prepare;
    end;
    FvalidaEstadoCommand.CommandTimeout := 3;
    FvalidaEstadoCommand.Parameters[0].Value.SetWideString(factura);
    FvalidaEstadoCommand.ExecuteUpdate;
    Result := FvalidaEstadoCommand.Parameters[1].Value.GetBoolean;
  except
    on e: Exception do
    begin
      controlarError;
    end;
  end;
end;

Everything works very fine and fast, but when the Tablet looses Wifi connection with the Server, it hangs for more than the timeout time assigned in the different properties. Some times I wait for 30 or 40 seconds and there is no response. If I go closer to the network router, sometimes it recovers the flow, but if I stay away, the app finally crashes. The question is: why would it hang if there are timeouts which should make the app respond with a "could not connect to the network on time" or "timeout error" instead of just hanging with out any posibility for the user than waiting or restarting the app?

1

1 Answers

1
votes

I haven't used Firebird yet, but have used TSQLConnection with DataSnap. I assume Firebird uses the same communication libraries as TSQLConnection. If so, then:

The underlying calls are handed off to Windows (i.e., WinSock) and there is no way that I have found to check to see if the connection is still intact.

I wrote a simple DataSnap server-side function called CheckCon(), which does nothing besides give the client app something to call on the server to see if the communication channel is still connected. I put the client-side call to CheckCon() in a try...except block so I could trap the error and auto-reconnect when an EIdSocketError is raised.

The user gets auto-reconnected, which is helpful, but the user still has to wait 30 seconds for the Windows WinSock to timeout before it returns a socket error and the auto-reconnect code executes.

This old thread has an explanation from Remy Lebeau about a similar topic, and since DataSnap uses Indy, I think it applies here too:

You won't be able to [check if the client is still connected to the server] in a timely manner. Indy uses blocking sockets, and blocking sockets simply are not designed to detect abnormal disconnects quickly. When an abnormal disconnect occurs, the underlying socket library (WinSock on Windows, Libc on Linux, etc) will not know that the socket is lost until the library times out internally and then invalidates the socket. Until that happens, there is no way for Indy to know whether the socket is actually connected or not, because the library itself does not know.