Polling Duplex


Como já falei há algum tempo, um dos tipos de mensagens suportados pelo WCF é o padrão Duplex. A finalidade desta técnica é permitir uma comunicação bidirecional, ou seja, o cliente invocar método de um serviço, bem como um serviço invocar um método do cliente. Esse tipo de comunicação permite, na maioria das vezes, um determinado serviço notificar o cliente de que algum evento ocorreu, dando a ele, uma chance de conseguir interagir com um cliente específico ou até mesmo vários clientes, através de um sistema publicador-assinante.

Aplicações Silverlight também podem precisar deste tipo de recurso, onde um serviço irá se comunicar com o cliente (que está rodando no navegador) para notificar sobre algum resultado, ou até mesmo para reportar a execução de alguma tarefa mais complexa. Felizmente, a partir da versão 2.0 do Silverlight, ele prove esta funcionalidade, e a partir a versão mais recente, Silverlight 3.0 (ainda em Beta), tornou esse trabalho mais simples. Para quem já está habituado a trabalhar com WCF, utilizar a API do mesmo em uma aplicação Silverlight (independentemente do tipo de comunicação) traz algumas limitações, quais falaremos neste artigo, principalmente dando mais foco no formato Duplex.

Ao utilizar um binding que suporte o tipo de comunicação Duplex, ele criará internamente um endpoint para que o serviço consiga invocar o callback do lado do cliente. Como a aplicação cliente está rodando dentro do navegador, ela não poderá criar um “listener” para receber os eventuais callbacks que o serviço possa disparar, impedindo a utilização dos bindings tradicionais para este cenário. Para resolver essa deficiência, o Silverlight utiliza o recurso conhecido como “poll”. Com esta técnica, o cliente ficará interrogando o serviço para determinar se há alguma mensagem que deve ser recebida por ele.

Antes de falarmos sobre as classes que dão suporte a esta técnica, vamos analisar os assemblies necessários. O Silverlight 2.0 e 3.0 traz dois assemblies com o nome de System.ServiceModel.PollingDuplex.dll, que estão em diretórios diferentes. Um deles contido no diretório %ProgramFiles%Microsoft SDKsSilverlightVERSAOLibrariesServer e outro no diretório %ProgramFiles%Microsoft SDKsSilverlightVERSAOLibrariesClient. Como podemos perceber, um dos assemblies será utilizado pelo lado do serviço, onde trará os tipos necessários para a criação de serviços Duplex, enquanto o segundo deverá ser utilizado por aplicações cliente, para o consumo de serviços Duplex.

O binding responsável por fornecer toda a infraestrutura necessária para a comunicação Duplex no Silverlight é chamado de PollingDuplexHttpBinding. Este binding herda diretamente da classe BasicHttpBinding, e customiza o mesmo para suportar o “poll” através do protocolo HTTP. Este binding utiliza os protocolos abertos Net Duplex e WS-Make Connection (WS-MC). O protocolo Net Duplex é utilizado para estabelecer uma sessão entre o cliente e o serviço, para que seja possível correlacionar as mensagens que serão trocadas. Já o protocolo WS-MC, faz o trabalho necessário para a criação de um canal dentro da sessão previamente criada, e que será utilizado pelo serviço para criar a mensagem que deve ser entregue ao cliente ou para o cliente interrogar o serviço.

Em termos de implementação do serviço nada muda, ou seja, a criação de uma Interface que representará o callback continuará sendo necessária. Além disso, a forma de invocarmos o callback continua sendo através do método GetCallbackChannel<T> da classe OperationContext. Para entender mais detalhes de como implementar um serviço que suporte callbacks, consulte este artigo. Se você já sabe construir serviços que utilizam callbacks, você notará que não há nenhuma diferença na criação destes mesmos serviços para Silverlight.

Depois de criado o contrato do serviço, de callback e da classe que representará o serviço, precisamos configurar o host para expor o serviço através do novo binding que mencionamos acima. Infelizmente, até a versão atual (Silverlight 3.0 Beta), ainda não existe suporte para a configuração deste tipo de serviços através de arquivos de configuração (como o Web.config). Na maioria das vezes o serviço estará hospedado no IIS (mas poderá utilizar outros hosts) e a deficiência na configuração declarativa, nos obrigará a customizar o ServiceHost e o ServiceHostFactoryBase (responsável por criar as instâncias da classe ServiceHost). Aqui não há diferenças em relação a forma que já conhecemos para a configuração de um endpoint, ou seja, continuamos necessitando o endereço, binding e contrato. A única atenção que temos que ter é especificar um binding que suporte este tipo de comunicação.

Utilizaremos o IIS como hosting (via arquivo *.svc), e como mencionado acima, será necessário criarmos uma especialização da classe ServiceHost e também da ServiceHostFactoryBase. Isso é mostrado através do código abaixo, e note que dentro do método InitializeRuntime adicionamos os endpoints necessários (através do método AddServiceEndpoint), incluindo um com um binding customizado, e entre os elementos que irão compor ele, temos a instância da classe PollingDuplexBindingElement, que habilita a comunicação Duplex entre o serviço e o cliente.

public class PollingDuplexServiceHostFactory : ServiceHostFactoryBase
{
    public override ServiceHostBase CreateServiceHost(
        string constructorString, Uri[] baseAddresses)
    {
        return new PollingDuplexServiceHost(baseAddresses);
    }

    private class PollingDuplexServiceHost : ServiceHost
    {
        public PollingDuplexServiceHost(params Uri[] addresses)
            : base(typeof(Servico), addresses)
        {
            this.Description.Behaviors.Add(
                new ServiceMetadataBehavior() { HttpGetEnabled = true });
        }

        protected override void InitializeRuntime()
        {
            this.AddServiceEndpoint(
                typeof(IContrato),
                new CustomBinding(
                    new PollingDuplexBindingElement(),
                    new TextMessageEncodingBindingElement(),
                    new HttpTransportBindingElement()),
                “”);

            this.AddServiceEndpoint(
                typeof(IMetadataExchange),
                MetadataExchangeBindings.CreateMexHttpBinding(),
                “mex”);

            base.InitializeRuntime();
        }
    }
}

É importante dizer ao runtime do WCF que criamos uma factory própria, e para vinculá-la ao serviço, utilizamos a diretiva @ServiceHost no arquivo *.svc. Essa diretiva possui um atributo chamado Factory, onde podemos especificar o tipo de uma classe que implemente a classe base ServiceHostFactoryBase.

<%@ ServiceHost
    Language=”C#”
    Debug=”true”
    Factory=”Web.UI.Host.Network.PollingDuplexServiceHostFactory” %>

Com isso finalizamos tudo o que é necessário por parte do serviço. Com o endereço do mesmo em mãos, devemos referenciá-lo no cliente através da IDE do Visual Studio .NET ou através do utilitário de linha de comando chamado slsvcutil.exe. Ao fazer isso, uma classe que corresponderá ao proxy será criada mas o arquivo de configuração não terá nenhuma informação. Isso se deve ao fato de que a versão atual (Silverlight 3.0 Beta) ainda não traz suporte a este tipo de funcionalidade. Um outro detalhe importante que você notará ao explorar a classe que representa o proxy, é que não haverá a versão síncrona do método, pois o Silverlight não possui isso nativamente. Assim, todos os métodos expostos pelo contrato, o cliente somente terá a versão assíncrona dos mesmos, que é composta por um par de métodos, BeginOperacao e EndOperacao. Apesar destes dois métodos existirem, eles são privados e não estão acessíveis além do proxy, que disponibiliza o modelo baseado em eventos para o consumo. Para maiores detalhes de como o processo assíncrono funciona, consulte este artigo.

Da mesma forma que utilizamos um binding customizado do lado do serviço, temos que fazer o mesmo do lado do cliente. Temos apenas que tomar os devidos cuidados para utilizar o assembly System.ServiceModel.PollingDuplex.dll que está no diretório %ProgramFiles%Microsoft SDKsSilverlightVERSAOLibrariesClient. Veja que estamos montando o binding customizado com os mesmos elementos (e na mesma ordem) que utilizamos na configuração do serviço, apenas nos atentando que a classe PollingDuplexBindingElement está no assembly que faz parte do cliente. O código abaixo mostra como devemos proceder para utilizar o proxy que foi gerado:

private Servico.ContratoClient _proxy;

public MainPage()
{
    InitializeComponent();

    EndpointAddress ea = new EndpointAddress(“http://127.0.0.1.:51116/Servico.svc”);
    CustomBinding cb =
        new CustomBinding(
            new PollingDuplexBindingElement(),
            new TextMessageEncodingBindingElement(),
            new HttpTransportBindingElement());

    _proxy = new ContratoClient(cb, ea);
    _proxy.OnCallbackReceived += (o, e) => MessageBox.Show(e.msg);
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    _proxy.PingAsync(“Teste”);
}

Antes de invocar o método Ping, é necessário nos vincularmos ao evento OnCallbackReceived, que será disparado quando a operação finalizar. Note que as operações são sufixadas com a palavra “Async”, e haverá sempre um evento que corresponde ao término da execução da respectiva operação.

Limites e Timeouts

As classes que vimos acima possuem algumas propriedades que podemos utilizar para ter um maior controle em alguns timeouts, e também na quantidade máxima de sessões e de mensagens que podem ser criadas. Para iniciar vamos falar sobre as propriedades fornecidas pela classe PollingDuplexBindingElement do serviço. As propriedades que gerenciam as quantidades máximas são: MaxPendingSessions e MaxPendingMessagesPerSession. A primeira determina a quantidade máxima de sessões suportadas, enquanto a segunda determina a quantidade máxima de mensagens para cada sessão criada. Com relação aos timeouts, temos as propriedades InactivityTimeout e ServerPollTimeout. A primeira propriedade determina um intervalo de tempo que o serviço pode passar sem nenhuma atividade antes do canal se tornar inválido. Já a segunda propriedade, especifica o tempo em que o serviço irá monitorar o cliente anter de retornar.

Do lado do cliente apenas temos duas propriedades, também fornecidas pela classe PollingDuplexBindingElement: InactivityTimeout e ClientPollTimeout. A primeira propriedade tem a mesma finalidade da propriedade InactivityTimeout do lado do serviço, enquanto a propriedade ClientPollTimeout determina o intervalo de tempo que o cliente ficará interrogando o serviço, para verificar a existência de novas mensagens. Todas essas propriedades devem ser configuradas antes da conexão ser estabelecida entre o cliente e o serviço.

Conclusão: Felizmente o Silverlight já traz suporte à comunicação Duplex. Como vimos no decorrer deste artigo, o único binding que possibilita isso em aplicações Silverlight é o PollingDuplexHttpBinding. Como estamos atualmente na versão Beta, ainda há algumas deficiências, como é o caso da ausência do suporte a configuração declarativa deste binding (via arquivo Web.config), que torna o processo de desenvolvimento um pouco mais trabalhoso e propício a erros. De qualquer forma, conseguimos atingir o nosso objetivo, que é proporcionar uma boa interatividade, já que podemos inicar uma tarefa e o serviço nos notificar quando a mesma finalizar.

SLComWCF.zip (92.46 kb)

Anúncios

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s