RTC Forums
May 20, 2024, 01:34:57 AM *
Welcome, Guest. Please login or register.

Login with username, password and session length
 
   Home   Help Login Register  
Pages: [1]
  Print  
Author Topic: Problem with sessions  (Read 3547 times)
ClementDoss
RTC License
***
Posts: 36


« on: February 09, 2015, 12:24:45 AM »

Hi,

I'm having a very annoying problem with session management.
I designed a 3tier application using delphi rtc.
The application is running with several clients and one server.
After 10 min (or more ) of inactivity, the client freezes. (anyone of them).

No matter what I try on the client side, I'm not able to reconnect to the server.
Code:
  HttpClient.MultiThreaded := True;
  HttpClient.UseWinHTTP := False;
  HttpClient.AutoConnect := True;


  HttpClient.ReconnectOn.ConnectError := True;
  HttpClient.ReconnectOn.ConnectFail := True;
  HttpClient.ReconnectOn.ConnectLost := True;

Those events are hooked to the rtcHttpclient:

Code:
procedure TmdwClient.HttpClientConnectError(Sender: TRtcConnection;
  E: Exception);
begin
   if Assigned(FOnConnectionStatus) then FOnConnectionStatus( Self, csError, E );
end;

// #1 After 10min or more this event is fired
// I want to reestablish a connection, but all I get from the server is a timeout error.
procedure TmdwClient.HttpClientSessionClose(Sender: TRtcConnection);
begin
  if Sender.inMainThread then begin
     HttpClient.Disconnect;
     if Assigned( FOnLogoff ) then
        FOnLogoff(Self);
  end
  else
     Sender.Sync(HttpClientSessionClose);
end;

procedure TmdwClient.HttpClientSessionOpen(Sender: TRtcConnection);
begin
  if Sender.inMainThread then begin
     if Assigned( FOnLoginRequired ) then
        // Aqui iremos retornar um Login e senha que serão verificados
        FOnLoginRequired( Self );
  end
  else
     Sender.Sync(HttpClientSessionOpen);
end;


The code that freezes the client application is :
Code:
  if not FInGetRows then begin
     FInGetRows := True;
     try
       lFunc := glbClient.cmDataset.Data.NewFunction('Dataset');
       lFunc.asString['Type']            := FParams.Values['Type']; // Table ou Query
       lFunc.asString['TableName']       := FParams.Values['TableName'];
       lFunc.asString['IndexFieldNames'] := FParams.Values['IndexFieldNames'];
       lFunc.asString['KeyValue']        := FParams.Values['KeyValue'];
       lFunc.asString['SQL']             := FParams.Values['SQL'];
       // Buscando dados
       lResultSet := glbClient.cmDataset.Execute(True,10); // #2 I was using "Execute" without parameters
       // Precisamos dos campos e dos dados
       if lResultSet.isType = rtc_Record then
          // Aqui iremos preencher o dataset com as linhas retornadas!
          TBOrtcUtils.rtcRecord2Dataset( lResultSet.asRecord, Self );
     finally
       FInGetRows := False;
     end;
  end;

When calling Execute(True,10) the application became responsive.
But still the call returns a TimeoutError, which is strange because the server is listening. All other workstations are running fine.

But If any of them keep idle for over 10min, the same thing happens. So it's not the hardware. It's me.. again  Undecided

Let me describe the server side now.
This application reads user information from a database, so when opening a session, I must know the user login in order to get the data required by the application.
When the session ends, everything is destroyed.

This is the relevant server side code:

Code:
procedure TmdwServer.HTTPserverSessionOpen(Sender: TRtcConnection);
var
  lSessionData : TBOSessionData;
begin
  lSessionData := TBOSessionData.Create;
  TrtcDataServer( Sender ).Session.Obj['SessionData'] := lSessionData;
  FLog.LogMsgFMT( 'Sessão aberta para -> %s:%s',[Sender.PeerAddr, Sender.PeerPort] );
end;

procedure TmdwServer.HTTPserverSessionClose(Sender: TRtcConnection);
var
  lSessionData : TBOSessionData;
begin
  lSessionData := TBOSessionData( TrtcDataServer( Sender ).Session.Obj['SessionData'] );
  // Retirando objeto da lista de sessões
  TrtcDataServer( Sender ).Session.Obj['SessionData'] := nil;
  lSessionData.Free;

  FLog.LogMsgFMT( 'Sessão encerrada para -> %s:%s',[Sender.PeerAddr, Sender.PeerPort] );
end;

There's an important remote function that must be called when the session is created in order to load the user credentials:

Code:
procedure TmdwServer.fncCheckCredencialsExecute(Sender: TRtcConnection;
  Param: TRtcFunctionInfo; Result: TRtcValue);
var
  lLogin,
  lPwd  ,
  lVersion : String;
  lBOUser : TBOUser;
  lBOSession : TBOSessionData;
begin
  lLogin := Param.AsString['Login'];
  lPwd   := Param.AsString['PWD'];
  lBOSession := TBOSessionData( TrtcDataServer( Sender ).Session.Obj['SessionData']);
  lBOUser := TBoUser.Create( lBOSession );
  try
     if lBOUser.CheckCredencials( lLogin, lPwd ) then begin // #3
       {... some irrelevant code here }
     end

  finally
     lBOUser.Free;
  end;
end;

lBOUser.CheckCredencials( lLogin, lPwd ) will load the user information in my session class (SessionData) that will be used by other remote functions.
That's why this function is important.

The session ends gracefully,  but I don't understand why I can't reconnect the client.

Any advice?

Clément
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #1 on: February 09, 2015, 12:52:55 AM »

1. RTC Sessions have nothing to do with physical connections, so there is no need to close a connection when a Session was closed.

2. By setting the AutoConnect property to TRUE and setting ReconnectOn properties to TRUE, you have configured the Client to re-open a connection as needed. By using "Disconnect", you are effectively disabling the Reconnect feature.

3. The Execute method may ONLY be called from the Main Thread and ONLY from code executed by an action performed by the user. It may NOT be called as a result of ANY events triggered by any RTC component, because it would result in a deadlock.

4. It is a bad idea to assign a Database Session to a Client Session (on the Server), for several reasons. First, because the Server would then require a separate Database Session (=Connection) for every single Client for whom the Server has an open Session. And Secondly, because a new Database Session will be opened for each new Client and Closed every time that Session expires. Normally, you would use a Database Connection Pool for all Database-related tasks on the Server. There are already topics on using and writing Database Connection Pools on the Forum (use the Forum "Search" feature).

5. Use the AutoSessionsPing feature on the TRtcClientModule component to send a PING function to the Server when the Client is idle (not making remote calls). There is already a topic about this on the RTC Forum, HERE.

Best Regards,
Danijel Tkalcec
Logged
ClementDoss
RTC License
***
Posts: 36


« Reply #2 on: February 09, 2015, 11:34:59 PM »

Hi Danijel

Thanks to your instructions I managed to solve the problem!  Cheesy

Clément
Logged
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2015, Simple Machines Valid XHTML 1.0! Valid CSS!
Page created in 0.025 seconds with 17 queries.