Delphi

Monitoramento e controle de conexões em servidores DataSnap

A versão do DataSnap que acompanha o Delphi XE2 finalmente trouxe o tão esperado e solicitado recurso para controlar e monitorar conexões efetuadas através de TCP/IP em um servidor DataSnap, até a versão XE o monitoramento e controle era possível, entretanto requeria muito código por parte do desenvolvedor. No Delphi XE2 o framework do DataSnap traz classes e métodos que facilitam e muito isso e neste artigo vou explicar como funciona.

Pra começar, toda a implementação do monitoramento e controle está implementada para protocolo TCP/IP o que permite monitorar e derrubar qualquer conexão, assim sendo o componente TDSTCPServerTransporter recebeu dois novos eventos OnConnect e OnDisconnect, estes eventos são muito importantes, pois através deles gerenciamos a lista de conexões no servidor podendo fazer o que quiser com as mesmas. Além disso toda conexão está associado a um Session ID que é único, o Session ID já está disponível desde as versões anteriores do DataSnap.

Para exemplificar o funcionamento e implementação do monitoramento definimos uma lista (collection) conforme abaixo para armazenar as instâncias de cada conexão (TIdTCPConnection) e protocolo (TDSTCPChannel)

FConnections: TObjectDictionary<TIdTCPConnection,TDSTCPChannel>;

 

A partir dai implementamos os eventos OnConnect e OnDisconnect no TDSTCPServerTransporter. Implementar o evento OnConnect é bem simples, você tem apenas que armazenar as informações referente a conexão na sua lista e caso tenha alguma interface para atualizar com as informações referentes a esta conexão, assim faze-lo.

O código abaixo adiciona a nova conexão utilizando os métodos TMonitor.Enter e TMonitor.Exit para assegurar que nenhuma outra thread irá tentar atualizar esta lista.

Após adicionar a lista utilizamos o método EnableKeepAlive que irei explicar em seguida.

procedure TCMServerForm.CMServerTransportConnectEvent(Event: TDSTCPConnectEventObject);
begin
  System.TMonitor.Enter(FConnections);
  try
    FConnections.Add(TIdTCPConnection(Event.Connection), Event.Channel);
  finally
    System.TMonitor.Exit(FConnections);
  end;

  //Enable Keep-alive for this new connection.
    Event.Channel.EnableKeepAlive(10000);
end;

A implementação do método OnDisconnect terá que localizar a instância da conexão e remove-la da lista, OnDisconnect será executado quando a aplicação cliente desconectar do servidor, seja de forma natural, queda de rede, etc.

Por padrão, o evento OnDisconnect não será notificado se o cliente de forma abrupta perde sua conexão internet. Isso acontece porque a conexão Socket permanece aberta até que ocorra alguma chamada e esta falhe. Se o sistema operacional está configurado para usar pacotes keep-alive para todas as conexões TCP/IP e com base na sua configuração, você verá o evento OnDisconnect ser notificado. Se você desejar controlar este comportamento para cada conexão, então use os métodos EnableKeepAlive e DisableKeepAlive para cada instância do TDSTCPChannel.

No código acima foi definido para cada conexão o KeepAlive em 10 segundos, isso quer dizer que o server irá enviar um pacote (fazer um ping) no cliente quando o mesmo estiver inativo por mais de 10 segundos, se o cliente não responder o server irá tentar novamente, a quantidade de tentativas é baseada no sistema operacional, por exemplo Windows 7 tenta 10 vezes. Você ainda pode definir o intervalo entre os pings, basta configurar o KeepAliveInterval.

O código abaixo demonstra como remover a instância da conexão da lista e como atualizar a interface do usuário utilizando TThread.Synchronize.

procedure TCMServerForm.CMServerTransportDisconnectEvent(Event: TDSTCPDisconnectEventObject);
var
  Index: Integer;
begin
  if (FConnections = nil) or (Event.Connection = nil) then
    Exit;

  //This is called when a TCP connection is closed, for whatever reason.
  System.TMonitor.Enter(FConnections);
  try
    FConnections.Remove(TIdTCPConnection(Event.Connection));

    TThread.Synchronize(nil, procedure
      begin
        //update the connection list box, removing the connection that was just closed
        Index := ConnectionsList.Items.IndexOfObject(Event.Connection);
        if Index > -1 then
        begin
          ConnectionsList.Items.Delete(Index);

          if ConnectionsList.SelCount = 0 then
            SessionIdList.ClearSelection;
        end;
      end);
  finally
    System.TMonitor.Exit(FConnections);
  end;

Até aqui sabemos quem está conectado ao servidor, a partir dai podemos derrubar qualquer conexão, visto que temos a instância de cada TDSTCPChannel e este contém o método close que encerra a conexão cliente, ao ser desconectado o lado cliente recebe uma exception quando tentar executar qualquer operação no servidor.
No exemplo abaixo encerramos a conexão selecionada em um ListBox, o método GetSelectedChannel busca a instância do Channel baseado na conexão selecionada no ListBox.

Quando executamos o método Channel.Close o evento OnDisconnect será notificado.

procedure TCMServerForm.ButtCloseSelectedClick(Sender: TObject);
var
  Channel: TDSTCPChannel;
begin
  //Find the selected connection's channel, and then close it.
  Channel := GetSelectedChannel;

  //after calling close, the CMServerTransportDisconnectEvent event will be notified.
  if Channel <> nil then
  begin
    Channel.Close;
  end;
end;
function TCMServerForm.GetSelectedChannel(Conn: TIdTCPConnection): TDSTCPChannel;
begin
  Result := nil;

  //Finds the selected connection
  if Conn = nil then
    Conn := GetSelectedConnection;

  //uses the selected connection to look up the associated channel.
  if Conn <> nil then
    FConnections.TryGetValue(Conn, Result)
end;

O Delphi XE2 traz um exemplo completo sobre o assunto e se você já usa a versão XE2 pode encontrá-lo em C:\Users\Public\Documents\RAD Studio\9.0\Samples\Delphi\DataSnap\DataSnapMonitor ou fazer o download direto do repositório de demos do RAD Studio XE2 aqui.

3 respostas
  1. Diego
    Diego says:

    Olá Lanusse tudo bom..
    Primeiramente gostaria de parabeniza-lo pelo blog e pelos artigos que tem publicado são muito bons mesmo, tenho acompanhado seus post´s e estes tem me ajudado muito em minhas duvidas sobre DataSnap e outros assuntos, estou aqui com mais uma duvida que ainda não ficou claro para mim e gostaria que se possível me ajudasse a esclarecer e identificar uma maneira de fazer o controle de transações entre um Cliente e um Servidor DataSnap este assunto e bem explorado nos blog e sites na internet mas nenhum que eu encontrei estava relacionado a uma aplicação DataSnap, desde já agradeço-lhe.

    Responder
  2. Leandra Mendes do Vale
    Leandra Mendes do Vale says:

    Sou desenvolvedora Delphi e atualmente estamos migrando nossos aplicativos para a estrutura DataSnap. Tenho acompanhado seus posts e outras fontes inclusive as da edn da embarcadero. Mas estou com um problema relativo a: Starto o server, Starto o cliente e tudo funciona normalmente… depois de umas 8 horas assim… geralmente não se reestarta nem o server, nem o client….. o server trava do nada.
    Assim sendo, penso que seja algo como Monitoramento e controle de conexões em servidores datasnap… Muito bom o seu post… Só que a nossa versão é a XE… Vc tem algum referencial bom para isso?
    Obrigada pela atenção.

    Responder

Deixe uma resposta

Quer participar da discussão?
Fique a vontade para contribuir!

Deixe uma resposta

O seu endereço de email não será publicado Campos obrigatórios são marcados *

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>