zoukankan      html  css  js  c++  java
  • Thread: DataSnap detect disgraceful disconnect

    https://forums.embarcadero.com/thread.jspa?messageID=389618

    Thread: DataSnap - detect disgraceful disconnect


     Reply to this Thread   Search Forum    Watch this Thread    Back to Thread List 


      Replies: 6 - Last Post: Sep 8, 2011 5:14 AM Last Post By: Mathew DeLong  
     

    Mathias Burbach  

    Posts: 38
    Registered: 12/8/99 
       DataSnap - detect disgraceful disconnect         Reply      Headers 
     
      Posted: Jul 14, 2011 1:51 AM
    datasnap , detect , disgraceful , disconnect 
      X-Source-Client: web
    NNTP-Posting-Host: 120.146.135.230 
    X-Posting-Host: 120.146.135.230 
     
    Hello Folks!

    How can I detect a disgraceful disconnect in a DataSnap server? We want to build a logon/logoff audit table and need to detect when a client process gets killed or - even worse - a network cable is unplugged accidentally.

    When running the server in the Delphi debugger I can see an exception being raised if I kill the process of the client application without disconnecting first. Here is the call stack at the time:

    :75df9617 KERNELBASE.RaiseException + 0x54
    IdStack.TIdStack.RaiseSocketError(10054)
    IdStack.TIdStack.RaiseLastSocketError
    IdStack.TIdStack.CheckForSocketError(-1)
    IdStackBSDBase.TIdStackBSDBase.Receive(???,???)
    IdSocketHandle.TIdSocketHandle.Receive(???)
    IdIOHandlerStack.TIdIOHandlerStack.ReadDataFromSource(???)
    IdIOHandler.TIdIOHandler.ReadFromSource(False,0,False)
    IdIOHandlerStack.TIdIOHandlerStack.Connected
    IdTCPConnection.TIdTCPConnection.Connected
    IdCustomTCPServer.TIdCustomTCPServer.DoExecute(???)
    IdContext.TIdContext.Run
    IdTask.TIdTask.DoRun
    IdThread.TIdThreadWithTask.Run
    IdThread.TIdThread.Execute
    Classes.ThreadProc($17C9CC0)
    System.ThreadWrapper($1821050)
    :76681174 kernel32.BaseThreadInitThunk + 0x12
    :77bfb3f5 ntdll.RtlInitializeExceptionChain + 0x63
    :77bfb3c8 ntdll.RtlInitializeExceptionChain + 0x36

    But none of the components on the TServerContainer or the TServerContainer itself would allow me to detect the lost socket connection. The TDSServer.OnError event is only triggered if the error occurred during a server method call. But that may not be the case.

    What would you recommend to detect such a lost socket connection?

    Thanks for a short answer in advance.

    Salut,
    Mathias
     

      Mathias Burbach  

    Posts: 38
    Registered: 12/8/99 
       Re: DataSnap - detect disgraceful disconnect         Reply      Headers 
     
      Posted: Sep 2, 2011 12:27 AM    in response to: Mathias Burbach
    datasnap , detect , disgraceful , disconnect , delphi , xe2 
      X-Source-Client: web
    NNTP-Posting-Host: 120.146.135.230 
    X-Posting-Host: 120.146.135.230 
     
    Hello Folks!

    I have revisited this problem, now that I am a happy owner of Delphi XE2 and heard Damien Bootsma talking about improvements in DataSnap in version XE2 at the Sydney Delphi XE2 World Tour. I have created an event handler for the new TDSTCPServerTransport.OnDisconnect property. Now when the client connection is closed disgracefully I end up in this event handler. Here is the call stack at the time:

    UServerContainer.TServerContainer1.DSTCPServerTransport1Disconnect(($30663C0))
    Datasnap.DSTCPServerTransport.TDSTCPServerTransport.DoOnDisconnect(TIdContextPeer($3075150) as IIPContext)
    IndyPeerImpl.TIdTCPServerPeer.LOnDisconnectEvent(???)
    IdCustomTCPServer.TIdCustomTCPServer.DoDisconnect(???)
    IdCustomTCPServer.TIdCustomTCPServer.ContextDisconnected($30BAEF0)
    IdContext.TIdContext.AfterRun
    IdTask.TIdTask.DoAfterRun
    IdThread.TIdThreadWithTask.AfterRun
    IdThread.TIdThread.Execute
    System.Classes.ThreadProc($3029D40)
    System.ThreadWrapper($3081110)
    :764c3677 kernel32.BaseThreadInitThunk + 0x12
    :76ec9d72 ntdll.RtlInitializeExceptionChain + 0x63
    :76ec9d45 ntdll.RtlInitializeExceptionChain + 0x36

    The problem now is that the session has been deleted already and even trying to access TDSSessionManager.GetThreadSession would raise another exception. I checked some of the properties of the Event: TDSTCPDisconnectEventObject. For example I type casted Event.Connection as TIdTCPConnection, but most of the properties of TIdTCPConnection are empty (e.g. BoundIP).

    How am I supposed to figure which client connection turned bad?

    A sample project and the CodeSite log are available for download from here:
    http://www.maranatha-consulting.com/Delphi/LostConnection.zip

    Thanks for a short answer in advance.

    Salut,
    Mathias

    Edited by: Mathias Burbach on Sep 2, 2011 5:31 PM
     
     
     
      Mathew DeLong  
     

    Posts: 43
    Registered: 9/18/08 
       Re: DataSnap - detect disgraceful disconnect         Reply      Headers 
     
      Posted: Sep 2, 2011 6:07 AM    in response to: Mathias Burbach 
      X-Source-Client: web
    NNTP-Posting-Host: 10.40.30.224 
    X-Posting-Host: 10.40.30.224 
     
    On the TDSTCPServerTransport component you can set the value of the "KeepAliveEnablement" to "kaEnabled" and then set the "KeepAliveTime" to the number of milliseconds you want to wait before sending KeepAlive packets to the client. The KeepAliveInterval property doesn't need to be changed unless you want to... this property is the delay between KeepAlive packet attempts. The number of attempts is determined by the operating system. (Windows, I believe, is 10 attempts.) If you don't enable KeepAlive, the proper events I talk about below will probably not be fired.

    Once this is configured, you can register a session event on your application to determine the session which has been closed. You'd do that like this:

    TDSSessionManager.Instance.AddSessionEvent(
    procedure(Sender: TObject; const EventType: TDSSessionEventType; const Session: TDSSession)
    begin
    if (EventType = SessionClose) and (Session <> nil) then
    //Handle this case
    end);
    If you want to handle the connection itself closing, you can add an OnConnect event to theTDSTCPServerTransport component, which keeps a list of the connections, and their associated SessionIDs. Like this:

    procedure TForm1.ServerTransportConnectEvent(Event: TDSTCPConnectEventObject);
    begin
    //FConnections: TObjectDictionary<TIdTCPConnection,TDSTCPChannel>;
    System.TMonitor.Enter(FConnections);
    try
    FConnections.Add(TIdTCPConnection(Event.Connection), Event.Channel); //Event.Channel has a SessionId property
    finally
    System.TMonitor.Exit(FConnections);
    end;
    end;
    Then you can add an OnDisconnect event like this:

    procedure TCMServerForm.CMServerTransportDisconnectEvent(Event: TDSTCPDisconnectEventObject);
    begin
    System.TMonitor.Enter(FConnections);
    try
    FConnections.Remove(TIdTCPConnection(Event.Connection));
    finally
    System.TMonitor.Exit(FConnections);
    end;
    end;
    In the OnDisconnect procedure you will know the session ID of the connection being closed, because it is stored in the FConnections map.

    I hope that helps.

    --
    Thanks,
    Mat DeLong (Embarcadero)
     
     
      Mathias Burbach  

    Posts: 38
    Registered: 12/8/99 
       Re: DataSnap - detect disgraceful disconnect         Reply      Headers 
     
      Posted: Sep 3, 2011 12:03 AM    in response to: Mathew DeLong 
      X-Source-Client: web
    NNTP-Posting-Host: 120.146.135.230 
    X-Posting-Host: 120.146.135.230 
     
    Hello Mathew,

    Thanks for your reply.

    This answer certainly helped to identify the TIdTCPConnection and the Channel of the lost connection. However my CodeSite log file reveals that the Event.Channel.SessionId is not set yet in the TDSTCPServerTransport.OnConnect event. The comment on the Event.Channel.SessionId property confirms that "This will not be populated until a Read has been made.".

    Anyhow lateron in the process of connecting the client app/user I need to relate a TDSSessionManager.GetThreadSession.Id with the already stored TDSTCPChannel.SessionId. I may not see the wood for the trees but it seems I am unable to find a way to conclude from a DSConnectEventObject: TDSConnectEventObject to an Event: TDSTCPConnectEventObject.

    My idea would be to store the logon in TDSAuthenticationManager.OnConnect if user name & password are fine but then use the TDSTCPChannel.SessionId as the primary key in the database for my connection log table. That way I will be able to log the disconnect under both circumstances (graceful & disgraceful disconnect).

    I have amended my sample project LostConnection and added the CodeSite log for the scenario of a lost connection after introducing the FConnections: TObjectDictionary<TIdTCPConnection,TDSTCPChannel>. See also here:
    http://www.maranatha-consulting.com/Delphi/LostConnection.zip

    I would appreciate your directions in getting "this riddle" solved.

    Salut,
    Mathias
     
     
      Mathew DeLong  
     

    Posts: 43
    Registered: 9/18/08 
       Re: DataSnap - detect disgraceful disconnect         Reply      Headers 
     
      Posted: Sep 6, 2011 5:31 AM    in response to: Mathias Burbach 
      X-Source-Client: web
    NNTP-Posting-Host: 10.40.30.224 
    X-Posting-Host: 10.40.30.224 
     
    You should be able to use an Authentication Manager component, and in the OnUserAuthentication event you wold have access to the current session being used by the connecting user. This is like an OnConnect event. You could use that session ID (getting the thread session from the SessionManager class,) to look up in a map of stored channels (as I mentioned before) to find the user's connection.

    Hope that helps,

    --
    Thanks,
    Mat DeLong (Embarcadero)
     
     
      Mathias Burbach  

    Posts: 38
    Registered: 12/8/99 
       Re: DataSnap - detect disgraceful disconnect         Reply      Headers 
     
      Posted: Sep 7, 2011 3:40 PM    in response to: Mathew DeLong
    datasnap , detect , disgraceful , disconnect , delphi , xe2 
      X-Source-Client: web
    NNTP-Posting-Host: 120.146.135.230 
    X-Posting-Host: 120.146.135.230 
     
    Hello Mathew,

    Thanks for your reply.

    Mathew DeLong wrote:
    You should be able to use an Authentication Manager component, and in the OnUserAuthentication event you wold have access to the current session being used by the connecting user. This is like an OnConnect event. You could use that session ID (getting the thread session from the SessionManager class,) to look up in a map of stored channels (as I mentioned before) to find the user's connection.The difficulty for me seems to be to get the mapping between the Event.Channel: TDSTCPChannel with the DSConnectEventObject.ChannelInfo: TDBXChannelInfo done. The Event.Channel.SessionId is not related to the TDSSessionManager.GetThreadSession.Id.

    Instead I came up with a slightly different idea. I observed that the TThread.CurrentThread.ThreadID is unique for each connection as long as the user is connected maybe even the server is running. So I created a dictionary like this:
    TConnectionPK = Cardinal;
    FConnections: TDictionary<TThreadID,TConnectionPK>;

    In the TDSAuthenticationManager.OnUserAuthenticate event - if the user/password combination is valid - I log the connection in the DB getting a new primary key value from the DB server and store it together with the ThreadID in the dictionary:
    TMonitor.Enter(FConnections);
    try
      FConnectionPK := LogUserConnect(User);
      FConnections.Add(TThread.CurrentThread.ThreadID, FConnectionPK);
    finally
      TMonitor.Exit(FConnections);
    end;

    I can then remove the ThreadID when the TCP channel signals a disconnect in TDSTCPServerTransport.OnDisconnect:
    TMonitor.Enter(FConnections);
    try
      if FConnections.TryGetValue(TThread.CurrentThread.ThreadID, ConnectionPK) then
      begin
        LogUserDisconnect(ConnectionPK);
        FConnections.Remove(TThread.CurrentThread.ThreadID);
      end;
    finally
      TMonitor.Exit(FConnections);
    end;


    Is the assumption correct that a particular ThreadID is not assigned again until the user has either disconnected gracefully or lost its connection, which causes the TDSTCPServerTransport.OnDisconnect to be triggered? If that's the case I have found my unique identifier for a connection (e.g. TThread.CurrentThread.ThreadID) and can move on.

    Salut,
    Mathias
     
     
      Mathew DeLong  
     

    Posts: 43
    Registered: 9/18/08 
       Re: DataSnap - detect disgraceful disconnect         Reply      Headers 
     
      Posted: Sep 8, 2011 5:14 AM    in response to: Mathias Burbach 
      X-Source-Client: web
    NNTP-Posting-Host: 10.40.16.198 
    X-Posting-Host: 10.40.16.198 
     
    The issue is with this: "TDSSessionManager.GetThreadSession.Id". Unfortunately, this is somewhat confusingly named. The TDSSession's "Id" is intended for internal use only. The "SessionId" you care about is actually called "Name" on the TDSSession instance. Use the Name property, and everything should work fine for you.

    --
    Thanks,
    Mat DeLong (Embarcadero)
     
     
     

  • 相关阅读:
    面向对象高级
    面向对象基础总结
    面向对象基础剩余
    组合和封装
    继承与派生
    面向对象
    4.10
    4.9
    常用模块
    【转】CentOS: 开放80、22、3306端口操作
  • 原文地址:https://www.cnblogs.com/GarfieldTom/p/2855154.html
Copyright © 2011-2022 走看看