Curso de dbExpress e DataSnap

Parte XXVIII – Multicamadas no Delphi 2005/2006 com .NET Remoting

Muitas tecnologias existem atualmente, destinadas a proverem a comunicação entre aplicações. Essas aplicações podem ser dois processos rodando na mesma máquina, ou em duas máquinas de uma rede. Exemplos de tecnologias distribuídas são: COM/DCOM/COM+, RMI, CORBA, SOAP, RPC etc.

No .NET, a tecnologia responsável pela construção de aplicações distribuídas se chama .NET Remoting. Essa tecnologia substitui o antigo DCOM/COM+ (apesar de ainda poderem ser usados no .NET) e oferece mecanismos que permite a comunicação entre clientes e servidores.

Observe que a arquitetura de objetos distribuídos é semelhante à arquitetura de aplicações de BD cliente / servidor. A diferença crucial é que o cliente estará usando objetos armazenados no servidor, e não tabelas como em um sistema cliente/server. Ao invés de passarmos instruções SQL, fazemos chamadas para métodos remotos.

Arquitetura do .NET Remoting

A Figura 1 mostra a arquitetura do .NET Remoting. Temos uma aplicação servidora e uma aplicação cliente, que se comunicam através de um Channel (discutido a seguir). O objeto remoto independe de localização.

 

image001.gif 

Figura. .NET Remoting: Channel, Formatters, Proxies e modos de ativação

Objetos Remotos: Marshal-by-Reference e Marshal-by-Value

No .NET, existem dois tipos de objetos usados em aplicações distribuídas:

Marshal-By-Reference - são passados por referência, são criados remotamente no servidor e são então controlados remotamente pela aplicação cliente. A aplicação cliente faz uso de um proxy, que se encarrega de repassar as chamadas para o servidor remoto;

Marshal-by-Value – são objetos passados por valor e precisam ser serializados para o transporte via rede. devem implementar a interface ISerializable ou receber o atributo Serializable.

Um exemplo prático

Neste capítulo criaremos um exemplo bastante prático e original: um Chat que usará o conceito de objetos distribuídos e .NET Remoting para trocar mensagens entre usuários da rede. Nossa aplicação será composta de três partes:

Server - aplicação servidora que conterá o objeto remoto que controla o chat;

Client – aplicação Windows Forms que permitirá enviar e receber mensagens;

Intf – pacote que define classes e interfaces destinadas a comunicação entre cliente e servidor (o “contrato”). Esse “contrato” é usado por ambas as aplicações, portanto vamos começar fazendo a definição da interface.

Interfaces: contrato entre servidor e cliente

Um dos fundamentos de qualquer sistema de comunicação de objetos distribuídos é o uso de uma  interface. Uma interface é semelhante a uma classe, mas que  possui somente métodos abstratos, ou seja, sem implementação. Esses métodos devem ser implementados por uma classe. Dessa forma, um objeto pode se comunicar com o outro apenas conhecendo a sua interface, que funciona como uma espécie de contrato (Figura).

 

image002.png 

Figura. Uma interface é um “contrato” entre cliente e servidor

Uma interface é como se fosse um controle remoto. Você consegue interagir com um objeto remoto conhecendo o que ele oferece, tendo a interface que descreve cada função, porém, sem a mínima idéia de como ele implementa essa funcionalidade internamente.

Criando a interface

No Delphi 2005, clique em File>New>Other>Delphi for .NET Projects>Package. Salve o com o nome de PackIntf.dpk em um diretório chamado Intf. A seguir, adicione uma unit ao pacote (File>New>Other> Delphi for .NET Projects>New Files>Unit) e salve-a com o nome de ChatIntf.

Altere o código da unit como mostrado a seguir:

 

unit ChatIntf;

 

interface

 

type

  [Serializable]

  ChatMsg = class

     Nome: System.String;

     Msg: System.String;

     DataHora: System.DateTime;

  end;

  ChatMsgs = array of ChatMsg;

 

  IChat = interface

    procedure EnviaMensagem(Msg: ChatMsg);

    function Refresh: ChatMsgs;

  end;

 

implementation

 

end.

 

Observe que definimos uma classe chamada ChatMsg, que representa uma mensagem do chat. Ela possui os atributos Nome, Msg e DataHora, que representam o nome do usuário que postou a mensagem (Nome), a mensagem em si (msg) e a data/hora em que foi postada. Como essa mensagem não será controlada remotamente (ela deve ser completamente serializada e enviada pela rede), a marcamos com o atributo Serializable. Também definimos um array de mensagens, chamado ChatMsgs.

A seguir, criamos uma interface chamada IChat. Essa interface define o “protoloco” de comunicação entre cliente e servidor, ou seja, é uma espécide de “contrato”. O cliente deve conhecer seus métodos para poder interagir com o servidor. O método EnviaMensagem recebe um Msg, que é a mensagem propriamente dita que irá trafegar pela rede. Refresh retorna todas as mensagens enviadas ao chat (um array de ChatMsg).

Pronto, basta compilaro Assembly clicando em Project>Build. Isso irá gerar uma DLL.

Criando o servidor remoto

Nosso servidor consistirá de uma aplicação Windows Forms, que terá o formulário somente para fins de ativação e para manter a aplicação em execução. Inicie então uma nova aplicação Windows Forms, dando o nome de Server.dpr ao projeto.

 

Dica: use um grupo (Project Group) para gerenciar facilmente os três projetos desta aplicação. Para isso, clique de direita sobre o ProjectGroup1 que está no Project Manager e crie ou adicione os projetos que desejar.

 

image004.png 

Figura. Project group facilita o acesso ao pacote, cliente e servidor

Conforme comentei anteriormente, a interface é usada por ambos os projetos, cliente e servidor. O servidor implementa o chat, enquanto o cliente utiliza seus serviços. Então, precisamos adicionar uma referência no projeto servidor para o Assembly criado anteriormente.

Para isso, clique de direita no nome do projeto no Project Manager e escolha Add Reference. Na janela que aparece, vemos na lista superior apenas os Assemblies registrados no GAC. Como nosso assembly é privado, clique no botão Browse e localiza o PackIntf.dll, selecione-a na lista e clique em OK.

Adicione uma nova unit ao projeto (File>New>Other> Delphi for .NET Projects>New Files>Unit), salve-a com o nome de uChatServer.pas e digite o seguinte:

 

unit uChatServer;

 

interface

 

uses

  ChatIntf;

 

type

  ChatServer = class (MarshalByRefObject,IChat)

    Msgs: ChatMsgs;

    procedure EnviaMensagem(Msg: ChatMsg);

    function Refresh: ChatMsgs;

  end;

 

implementation

 

function ChatServer.Refresh: ChatMsgs;

begin

  result := Msgs;

end;

 

procedure ChatServer.EnviaMensagem(Msg: ChatMsg);

var

  i: integer;

begin

  i := Length(Msgs) + 1;

  msg.DataHora := System.DateTime.Now;

  SetLength(Msgs,i);

  Msgs[i - 1] := Msg;

end;

 

A cláusula uses faz referência à unit ChatIntf.pas, contida no assembly PackIntf.dll. A seguir, temos a declaração da classe ChatServer:

 

ChatServer = class (MarshalByRefObject,IChat)

 

Aqui estamos dizendo que a classe descende de MarshalByRefObject, indicando que se trata de um objeto que será controlado remotamente e que implementa os métodos da interface IChat. O método EnviaMensagem simplesmente guarda a mensagem em um array (Msgs) que permanece ativo durante toda a vida útil do objeto remoto. Reresh simplesmente devolve ao cliente todas as mensagens postadas.

Volte ao formulário principal e no seu evento Load digite o seguinte:

 

uses uChatServer;

...

cnl := HttpChannel.Create(8888);

ChannelServices.RegisterChannel(cnl);

RemotingConfiguration.RegisterWellKnownServiceType(

  typeOf(ChatServer),'ChatServer',WellKnownObjectMode.Singleton);

 

Declare a variável cnl na seção public do formulário:

 

cnl: HttpChannel;

 

Declare os seguintes namespaces na cláusula uses da sessão interface:

 

System.Runtime.Remoting,

System.Runtime.Remoting.Channels,

System.Runtime.Remoting.Channels.Http;

 

Vamos conhecer um pouco mais sobre o que significam os objetos e métodos usados no código anterior.

Channels e Formatters

O namespace System.Runtime.Remoting.Channels contém classes para suporte a channels (canais), que são usados como meio de transporte para chamadas de métodos de um cliente para um objeto remoto. Channels são objetos que transportam mensagens entre aplicações remotas e são responsáveis por “escutar” mensagens remotas.

Antes de processar essas mensagens, um Channel pode enviar a mensagem para uma cadeia de objetos Channel Sink (receptores). Cada Sink na cadeia recebe a mensagem, efetua operações específicas e repassa para o próximo Sink.

Existem dois tipos de Channels no .NET:

·         HttpChannel ­- namespace System.Runtime.Remoting.Channels.Http;

·         TcpChannel - namespace System.Runtime.Remoting.Channels.Tcp.

Formatters (formatadores) são responsáveis por codificar/decodificar mensagens para transmissão através de um Channel. Existem dois tipos de Formatters no .NET:

·         SoapFormatter;

·         BinaryFormatter.

O interessante é que podemos ter uma mesma aplicação usando canais diferentes com tipos de formatadores diferentes. Por exemplo, um objeto remoto por ser ativado tanto por Tcp/Binary, o que é mais rápido em uma rede interna, quanto por Http/Soap através da internet.

Em nosso exemplo, declaramos uma variável do tipo HttpChannel. O construtor da classe recebe como parâmetro o número da porta a ser utilizada, nesse caso usamos 8888.

ChannelServices

ChannelServices, do namespace System.Runtime.Remoting.Channels, é uma classe composta inteiramente de membros estáticos, de forma que não precisamos instanciá-la para usar seus métodos. Oferece métodos para manipular objetos Channels, como registrar, desregistrar, obter uma lista de Channels registrados e assim por diante.

Em nosso exemplo, usamos o método RegisterChannel de ChannelServices para regitrar o objeto cnl do tipo HttpChannel, criado na linha anterior.

Tipos de ativação de objetos

Objetos remotos do tipo Marshal-By-Reference podem ser criados (instanciados) de duas formas:

·         Servidor (Server Activation) – são registrados no .NET Remoting e são criadas no servidor somente quando necessário. Não são criados quando o objeto proxy é criado na máquina cliente, mas somente quando é feita a primeira chamada de método. Objetos servidores podem ser ativados de duas formas:

o        Singleton - somente um objeto reside na memória do servidor e todas as chamadas clientes a esse objeto são serializadas;

o        SingleCall – um novo objeto é criado para cada solicitação cliente;

·         Cliente (Client Activation) – a criação de objetos no servidor é comandada pela aplicação cliente.

RemotingConfiguration

RemotingConfiguration, do namespace System.Runtime.Remoting, é uma importante classe destinada a manutenção e configuração de uma aplicação distribuída usando .NET Remoting. Possui somente métodos estáticos.

Em nosso exemplo, chamamos o método RegisterWellKnownServiceType, que registra em uma aplicação servidora um determinado tipo como sendo remoto, tornando-o assim, acessível por outras aplicações.

Na próxima parte deste artigo, veremos como criar a aplicação cliente.

 

Leia todos artigos da série