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.
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).
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.
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
- Curso de dbExpress e DataSnap
- Curso de dbExpress e DataSnap - Parte II
- Curso de dbExpress e DataSnap - Parte III
- Curso de dbExpress e DataSnap - Parte IV
- Curso de dbExpress e DataSnap - Parte V
- Curso de dbExpress e DataSnap - Parte VI
- Curso de dbExpress e DataSnap - Parte VII
- Curso de dbExpress e DataSnap - Parte VIII
- Curso de dbExpress e DataSnap - Parte IX
- Curso de dbExpress e DataSnap - Parte X
- Curso de dbExpress e DataSnap - Parte XI
- Curso de dbExpress e DataSnap - Parte XII
- Curso de dbExpress e DataSnap - Parte XIII
- Curso de dbExpress e DataSnap - Parte XIV
- Curso de dbExpress e DataSnap - Parte XV
- Curso de dbExpress e DataSnap - Parte XVI
- Curso de dbExpress e DataSnap - Parte XVII
- Curso de dbExpress e DataSnap - Parte XVIII
- Curso de dbExpress e DataSnap - Parte XIX
- Curso de dbExpress e DataSnap - Parte XX
- Curso de dbExpress e DataSnap - Parte XXI
- Curso de dbExpress e DataSnap - Parte XXII
- Curso de dbExpress e DataSnap - Parte XXIII
- Curso de dbExpress e DataSnap - Parte XXIV
- Curso de dbExpress e DataSnap - Parte XXV
- Curso de dbExpress e DataSnap - Parte XXVI
- Curso de dbExpress e DataSnap - Parte XXVII
- Curso de dbExpress e DataSnap - Parte XXVIII
- Curso de dbExpress e DataSnap - Parte XXIX
- Curso de dbExpress e DataSnap - Parte XXX