Gianluca Colombo
Gianluca Colombo

Reputation: 829

Android Socket Timeout Exception during Rest data connection, only during mobile network connection

After updating Delphi version 10.1 to 10.4.2, I encountered this problem, which only occurs on some Android 10 and 11 devices.

The APP exchanges data with the server via TDSRestConnection. On the same phone, where previously the app developed with Delphi 10.1 worked correctly, stops working after the update to Delphi version 10.4.2 and after the Android upgrade on the phone.

The APP works only over the WiFi network and no longer works over the mobile network, generating this error after a while (7/8 seconds):

java.net.SocketTimeoutException: failed to connect to /xxx.xxx.xx.xx (port 17200) from /100.118.154.102 (port 42050) after 300000ms: isConnected failed: ETIMEDOUT (Connection timed out).

I also tried using different internet mobile providers (TIM, VODAFONE) The 4G connection is Very Good and the Internet speed test doesn't show any problem.

The error doesn't appear immediately. The function GetSql is called more times in the sincro procedure, but unfortunately it stops working not at the same point.

The code:

Client side:

const fConnectTimeout=300000;
const fReadTimeout=300000;


function TServerMethods1Client.GetSql(StringaSql: string; const ARequestFilter: string): TFDJSONDataSets;
begin
  if FGetSqlCommand = nil then
  begin
    FGetSqlCommand := FConnection.CreateCommand;
    FGetSqlCommand.RequestType := 'GET';
    FGetSqlCommand.Text := 'TServerMethods1.GetSql';
    FGetSqlCommand.Prepare(TServerMethods1_GetSql);
  end;
  FGetSqlCommand.Parameters[0].Value.SetWideString(StringaSql);
  FGetSqlCommand.Execute(ARequestFilter);
  if not FGetSqlCommand.Parameters[1].Value.IsNull then
  begin
    FUnMarshal := TDSRestCommand(FGetSqlCommand.Parameters[1].ConnectionHandler).GetJSONUnMarshaler;
    try
      Result := TFDJSONDataSets(FUnMarshal.UnMarshal(FGetSqlCommand.Parameters[1].Value.GetJSONValue(True)));
      if FInstanceOwner then
        FGetSqlCommand.FreeOnExecute(Result);
    finally
      FreeAndNil(FUnMarshal)
    end
  end
  else
    Result := nil;
end;

function SelectSql(StringaSQL:String;tb:TFDMemTable;out Errore:String):Boolean; overload;
var
  jds: TFDJSONDataSets;
  data: TFDAdaptedDataSet;
begin
  try

     result:=false;

     if DMClientRestModule=nil then DMClientRestModule:=TDMClientRestModule.Create(nil);

     DMClientRestModule.DSRestConnection1.Port := STRTOINT(REST_PORT);
     DMClientRestModule.DSRestConnection1.Host := REST_HOST;
     DMClientRestModule.DSRestConnection1.SessionID := '';
     DMClientRestModule.DSRestConnection1.HTTP.ConnectTimeout := fConnectTimeout;
     DMClientRestModule.DSRestConnection1.HTTP.ReadTimeout := fReadTimeout;

     jds :=TFDJSONDataSets.Create;

     jds := DMClientRestModule.ServerMethods1Client.GetSql(StringaSql);
     if (Assigned(jds)) then begin
       data := TFDJSONDataSetsReader.GetListValue(jds, 0);
       tb.AppendData(data);
       tb.Open;
       result:=true;
     end;


     try
       DMClientRestModule.Free;
       DMClientRestModule:=NIL;
     except
     end;

  except
     result:=false;
     Errore:=GetStrException(ExceptObject,ExceptAddr);
  end;

END;

Server side:

function TServerMethods1.GetSql(StringaSql: String): TFDJSONDataSets;
var
 dset:TFDJSONDataSets;
begin

try

 DbConn.Params.Clear; //TFDConnection
 DbConn.Params.Database:=fdb;
 DbConn.Params.UserName:=fuid;
 DbConn.Params.Password:=fpwd;
 DbConn.Params.Add('server='+fdbserver);
 DbConn.Params.Add('port='+fport);
 DbConn.Params.DriverID:='MySQL';

try
 q1.Close; // TFDQuery

 if not DbConn.Connected then
  DbConn.Connected:=true;

 if not DbConn.Connected then
     log('Not conneted to db')
      else
       log('Connected to db')  ;
  with q1 do
   begin
    close;
    SQL.Clear;
    SQL.Add(StringaSql);
    Log(StringaSQl);
    open;
   end;

  Result := TFDJSONDataSets.Create;
  // The "TFDJSONDataSetsWriter" class provides static "ListAdd" method.
  // It uses reflection to convert results of the query into "TFDJSONDataSets".
  TFDJSONDataSetsWriter.ListAdd(Result,'',q1);
except
  Log(GetStrException(ExceptObject,ExceptAddr));
  Result := TFDJSONDataSets.Create;
  TFDJSONDataSetsWriter.ListAdd(Result,'',q1);
end;
finally
  DbConn.Connected:=false;
end;

end;

update 16/04/2021

Investigating better, it seems that the Sql String that causes the exception is the following (Long text sql string)

          StringaSql :='SELECT PLANNING_ENGINEERS.DATE_JOB,NOT_A_JOB, ';
          StringaSql := StringaSql + ' PLANNING.PLAN_ID,PLANNING.CONTACTID,PLANNING.CALLER_REFER, ';
          StringaSql := StringaSql + ' PLANNING.COMPANYID,PLANNING.DEPOTID,CALLTYPEID,TIME_BEGIN,';
          StringaSql := StringaSql + ' TIME_END,LOCATION_DESCRIPTION,ADDRESS1,ADDRESS2,ZIP,PLACE,PROVINCE,';
          StringaSql := StringaSql + ' PHONE1,PHONE2,FAX,EMAIL,LATITUDE,LONGITUDE, ';
          StringaSql := StringaSql + ' PLANNING.DELETED,PLANNING.CLOSED,PLANNING.SUSPENDED,PLANNING.NOTES,';
          StringaSql := StringaSql + ' PLANNING.INTERNAL_NOTES,PLANNING.SUGGESTION_NOTES,PLANNING.CLOSING_NOTES,ORDER_REFERENCE,EXTERNAL_REFERENCE, ';
          StringaSql := StringaSql + ' EXTERNAL_REFERENCE_DESC ';
          StringaSql := StringaSql + ' ,(SELECT CALLTYPES.DESCRIPTION FROM CALLTYPES WHERE CALLTYPES.CALLTYPE_ID=PLANNING.CALLTYPEID)';
          StringaSql := StringaSql + ' AS CALL_DESC ';
          StringaSql := StringaSql + ' ,(SELECT CALLTYPES.TYPE FROM CALLTYPES WHERE CALLTYPES.CALLTYPE_ID=PLANNING.CALLTYPEID)';
          StringaSql := StringaSql + ' AS CALL_TYPE ';
          StringaSql := StringaSql + ' ,(SELECT DEPOTS.NOTES FROM DEPOTS WHERE DEPOTS.DEPOT_ID=PLANNING.DEPOTID)';
          StringaSql := StringaSql + ' AS DEPOT_NOTES ';
          StringaSql := StringaSql + ' FROM PLANNING,PLANNING_ENGINEERS WHERE  ';
          StringaSql := StringaSql + ' PLANNING.DELETED=''N'' AND PLANNING.CLOSED=''N'' AND PLANNING.SUSPENDED=''N'' ';
          StringaSql := StringaSql + ' AND PLANNING_ENGINEERS.PLANID=PLAN_ID ';
          StringaSql := StringaSql + ' AND PLANNING_ENGINEERS.ENGINEERID='+'42';
          StringaSql := StringaSql + ' AND PLANNING_ENGINEERS.DATE_JOB BETWEEN '+QuotedStr(FormatDateTime('YYYYMMDD', date))+' AND '+QuotedStr(FormatDateTime('YYYYMMDD',date))+' ';
          StringaSql := StringaSql + ' ORDER BY DATE_JOB,TIME_BEGIN';

During the WIFI connection, the call to the server works fine then it send back the result.

Instead during the 4G connection, the call raise the exception below:

Exception ENetHTTPClientException in module ... java.net.SocketException : Connection reset.

I also reported a ticket RSP-33699 to Embarcadero

Here the video: RestIssue

Upvotes: 1

Views: 1150

Answers (1)

S. Dabrowski
S. Dabrowski

Reputation: 71

I suppose you use cleartext HTTP (ie. http://172.16.30.24:8088). Consider given solution :)

Upgrading from Delphi 10.1 to 10.4.1 changed the target SDK in your project. One of the changes you might expect is that Android does not allow you to use direct claertext HTTP requests starting from Android 9.

In your AndroidManifest.xml file you need to remember of internet permission and put one additional line as shown below:

<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        ...
        android:usesCleartextTraffic="true"
        ...>
        ...
    </application>
</manifest>

More information can be found here: StackOverflow and Android docs

Upvotes: 1

Related Questions