Em um mundo cada vez mais conectado, cresce a necessidade de termos acesso às informações de forma muito mais rápido, em qualquer lugar, a qualquer momento e em qualquer dispositivo. E quando falamos em ter acesso rapidamente à informações, intrinsicamente estamos pensando em “tempo real” que, teoricamente, assim que a informação chega em algum concentrador, ela deve ser encaminhada à todos os interessados.
Uma vez que temos o provedor de informações, os clientes são meros assinantes que recebem as informações e as exibem ou armazenam para seu uso específico. Quando estamos dentro de um ambiente controlado, como por exemplo, a rede privada de uma empresa e com uma infraestrutura homogênea, podemos recorrer à recursos específicos da tecnologia, como é o caso do TCP ou UDP. Mas nem sempre os provedores e consumidores estão dentro de um mesmo ambiente ou utilizando uma mesma tecnologia. Hoje é comum as empresas dependerem excessivamente de parceiros de negócio que fornecem informações extremamente relavantes para o bom andamento de suas atividades.
Existem algumas técnicas para possibilitar, ou melhor, emular a conexão ativa entre as partes para o envio e recebimento de informações. Entre estas técnicas podems citar a técnica de polling, que consiste em ficar consultando o serviço fornecedor das informações de duas maneiras:
-
Periódico: A cada X minutos enviar uma nova requisição para o serviço em busca de novas informações.
-
Requisição Fixa: Esta técnica consiste no servidor prender a requisição até que uma nova informação esteja disponível. Quando estiver, devolve o resultado para o cliente, que por vez, realiza uma nova requisição e fica, novamente, aguardando um novo resultado.
Ambas as técnicas funcionam e do ponto de vista do usuário acaba sendo transparente. Mas o grande ponto negativo é a latência que teremos, sem contar na possibilidade de prejudicar a escalabilidade na medida em que o número de clientes aumenta. Felizmente os grupos que regem a internet fizeram um trabalho, criando e especificando um – novo – protocolo chamado de WebSockets, que tenta resolver grande parte destes problemas de forma simples e objetiva, sem a necessidade de improvisações que vínhamos fazendo até então.
Basicamente falando, este protocolo tem a finalidade de criar um vínculo entre o cliente o servidor sem a necessidade de realizar múltiplas conexões HTTP, fornecendo, nativamente, uma comunicação bidirecional. Ele faz uso de alguns novos headers que são incluídos no cabeçalho da requisição, e que irão controlar se o cliente e o servidor podem ou não estabelecer uma conexão, e a partir daí, passam a trocar mensagens entre eles. A imagem abaixo ilustra as etapas que ocorrem entre as partes quando utilizamos este protocolo:
-
Passo 1: O cliente envia uma requisição para servidor para iniciar e configurar uma conexão via WebSockets.
-
Passo 2: O servidor aceita a requisição e passa ao cliente uma chave para indicar a efetivação da conexão.
-
Passo 3: A partir da conexão estabelecida, ambos podem enviar mensagem um para o outro.
-
Passo 4: Finalmente, quando o cliente não estiver mais interessado, ele pode encerrar a conexão com o servidor.
Os passos 1 e 2 são conhecidos como handshake, que determina se aquele cliente pode se conectar àquele servidor. Caso positivo, o servidor instrui o cliente à migrar/trocar para o protocolo WebSocket. Mais tarde, ainda neste artigo, vamos monitorar o tráfego entre as partes e analisar as mensagens que são trocadas.
Por se tratar de um protocolo multi-plataforma, todos os grandes fabricantes estão se preocupando em adaptar e desenvolver mecanismos em suas tecnologias e ferramentas para entenderem tal protocolo. A Microsoft se preocupa em adaptar suas tecnologias para que seja possível o desenvolvimento e o consumo de serviços que façam uso deste protocolo. O .NET Framework 4.5 incluiu um namespace chamado System.Net.WebSockets, que fornecem classes de baixo nível para este protocolo. Já as camadas mais acima, tais como o ASP.NET e o WCF fazem uso destas classes para abstrair e facilitar a criação e exposição destes tipos de serviços.
Recapitulando, o WCF fornece um binding chamado WSDualHttpBinding que fornece a conexão duplex sobre o protocolo HTTP, mas este utiliza dois canais para a comunicação e faz uso do protocolo WS-RM (Reliable Messaging) para gerenciar a sessão entre as partes. Além disso, muitas vezes o firewall, quando está devidamente configurado, pode barrar o callback que o serviço envia para o cliente.
Para encapsular toda a comunicação utilizando o WebSockets, a Microsoft incluiu no WCF um novo binding chamado de NetHttpBinding. Este binding fornece uma propriedade interessante chamada TransportUsage que recebe uma das três opções expostas pelo enumerador WebSocketTransportUsage:
-
Always: Força a utilização de WebSockets mesmo quando trata-se de contratos request-reply.
-
Never: Evita o uso de WebSockets, resultando em uma exceção se o contrato for duplex.
-
WhenDuplex: Como o próprio nome indica, será utilizado quando trata-se de um contrato duplex.
É importante dizer que este binding, ao contrário do WSDualHttpBinding que utiliza o WS-RM para gerenciamento da sessão, recorre aos recursos do transporte para gerenciar a mesma. Já a codificação das mensagens (que são SOAP) não são interoperáveis, pois estão em formato binário. Para possibilitar o consumo por outras partes que não são .NET, podemos configurar o binding para que as mensagens sejam codificadas em texto através da propriedade MessageEncoding.
Como exemplo de utilização deste protocolo, vamos criar uma espécie de chat. A criação dos contratos e configuração do serviço para a utilização deste novo protocolo é idêntica aquela que já conhecemos até então. Utilizamos duas interfaces, sendo uma para descrever as ações do chat, e a segunda para determinar como e onde será realizado o retorno (callback) das mensagens.
[ServiceContract(CallbackContract = typeof(IChatCallback))]
public interface IChat
{
[OperationContract]
void Entrar(string nome);
[OperationContract]
void EnviarMensagem(string de, string para, string mensagem);
[OperationContract]
void Sair(string nome);
}
public interface IChatCallback
{
[OperationContract(IsOneWay = true)]
void ReceberMensagem(string de, string para, string mensagem);
}
Depois do contrato criado e implementado (vou omitir aqui a classe que representa o serviço, mas ela está no arquivo em anexo para download), temos que expor o serviço para que os consumidores consigam acessar. Novamente, veremos que a estrutura é semelhante aquela que já utilizamos na criação de outros serviços em WCF “tradicionais”, sendo a única exceção o binding, que deve ser o NetHttpBinding.
using (var host = new ServiceHost(typeof(Comunicador),
new Uri(“http://localhost:8282”)))
{
host.AddServiceEndpoint(typeof(IChat), new NetHttpBinding(), “chat”);
host.Open();
Console.WriteLine(“Comunicador no Ar”);
Console.ReadLine();
}
Ao rodar o serviço e analisar os metadados no navegador, veremos que o endereço possui um novo scheme chamado “ws”. Nós podemos utilizar tanto o “http” quanto o “ws”. Ao referenciar o serviço através da opção “Add Service Reference” em uma aplicação cliente, o WCF já faz a configuração utilizando o “ws”, conforme podemos ver no trecho extraído do arquivo de configuração.
<client>
<endpoint address=”ws://localhost:8282/chat”
binding=”netHttpBinding”
bindingConfiguration=”NetHttpBinding_IChat”
contract=”ServicoDeComunicacao.IChat”
name=”NetHttpBinding_IChat” />
</client>
Vou omitir o código da aplicação cliente por não haver nada de especial em relação aquilo que já conhecemos em como consumir um serviço WCF que geram callbacks, mas de qualquer forma, todo o código de exemplo está na arquivo anexado à este artigo. Vamos nos atentar as mensagens que trafegam entre as partes quando o cliente e o servidor entram em ação, e para isso, vamos utilizar o Fiddler para monitorar as requisições, que não deixam de ser HTTP. Quando rodamos o serviço e dois clientes (Israel e Jack), podemos ver a conexão sendo estabelecida com o servidor que gerencia o chat e as mensagens sendo trocadas entre eles:
Por fim, ao monitorarmos as requisições via Fiddler, podemos perceber a conexão sendo criada entre as partes, e tudo isso é definido através de headers no cabeçalho das mensagens, conforme notamos na imagem abaixo. Os primeiros que nos chamam a atenção são o header Connection e Upgrade, que indica ao servidor que a conexão deve ser migrada para WebSockets. Na sequencia temos o header Sec-WebSocket-Key, que trata-se de um hash gerado e codificado em Base64, que em conjunto com o header de resposta chamado Sec-WebSocket-Accept, previnem um ataque conhecido como “cross-protocol attack”, e garantem a conexão estabelecida e que as mensagens já podem ser trocadas.
Neste artigo foi possível analisar superficialmente os benefícios e como utilizar este protocolo no WCF. Apesar de útil em alguns cenários, ele pode ser impraticável em outros, como por exemplo, o consumo por aplicações Web (Javascript), que tem certa dificuldade em lidar com mensagens SOAP. Com um pouco de trabalho é possível customizar o serviço para conseguir se comunicar com este tipo de cliente, mas o assunto fica para um próximo artigo.
Israel, obrigado por este ótimo artigo.
Continue assim, fico no aguardo dos próximos artigos.
Artigo muito bom.
Israel gostaria de aproveitar e lhe perguntar algo. Não sei se este seria o melhor lugar.
Quando criamos serviços com WCF, existe alguma maneira de criar por exemplo um serviço genérico de segurança? Um serviço que eu escolha o cenário internet ou intranet e ele aplique as politicas de segurança para os serviços criados, independente do programador codificar no novo serviço.
Você sugere algo, algum exemplo.