WCF – Suporte à WebSockets


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.

WCFWebSockets.zip (57.53 kb)

2 comentários sobre “WCF – Suporte à WebSockets

    • 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.

Deixar mensagem para Leandro Cancelar resposta