Compartilhar via


Tutorial: Criar aplicativo em tempo real de alta frequência com o SignalR 2

Este tutorial mostra como criar um aplicativo Web que usa o ASP.NET SignalR 2 para fornecer funcionalidade de mensagens de alta frequência. Nesse caso, "mensagens de alta frequência" significa que o servidor envia atualizações a uma taxa fixa. Você envia até 10 mensagens por segundo.

O aplicativo criado exibe uma forma que os usuários podem arrastar. O servidor atualiza a posição da forma em todos os navegadores conectados para corresponder à posição da forma arrastada usando atualizações cronometradas.

Os conceitos apresentados neste tutorial têm aplicações em jogos em tempo real e outras aplicações de simulação.

Neste tutorial, você:

  • Configurar o projeto
  • Criar o aplicativo base
  • Mapear para o hub quando o aplicativo for iniciado
  • Adicionar o cliente
  • Executar o aplicativo
  • Adicionar o loop do cliente
  • Adicionar o loop do servidor
  • Adicione animação suave

Aviso

Esta documentação não é para a versão mais recente do SignalR. Dê uma olhada no ASP.NET Core SignalR.

Pré-requisitos

Configurar o projeto

Nesta seção, você cria o projeto no Visual Studio 2017.

Esta seção mostra como usar o Visual Studio 2017 para criar um aplicativo Web ASP.NET vazio e adicionar as bibliotecas SignalR e jQuery.UI.

  1. No Visual Studio, crie um aplicativo Web ASP.NET.

    Criar web

  2. Na janela Novo ASP.NET Aplicativo Web – MoveShapeDemo, deixe Vazio selecionado e selecione OK.

  3. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto e selecione Adicionar>Novo Item.

  4. Em Adicionar Novo Item – MoveShapeDemo, selecione Visual C#>Web>SignalR Instalado>e, em seguida, selecione Classe de Hub do SignalR (v2).

  5. Nomeie a classe MoveShapeHub e adicione-a ao projeto.

    Esta etapa cria o arquivo de classe MoveShapeHub.cs . Simultaneamente, ele adiciona um conjunto de arquivos de script e referências de assembly que dão suporte ao SignalR ao projeto.

  6. Selecione Ferramentas>Gerenciador de Pacotes NuGet>Console do Gerenciador de Pacotes.

  7. No Console do Gerenciador de Pacotes, execute este comando:

    Install-Package jQuery.UI.Combined
    

    O comando instala a biblioteca de interface do usuário do jQuery. Você o usa para animar a forma.

  8. No Gerenciador de Soluções, expanda o nó Scripts.

    Referências da biblioteca de scripts

    As bibliotecas de scripts para jQuery, jQueryUI e SignalR são visíveis no projeto.

Criar o aplicativo base

Nesta seção, você cria um aplicativo de navegador. O aplicativo envia o local da forma para o servidor durante cada evento de movimentação do mouse. O servidor transmite essas informações para todos os outros clientes conectados em tempo real. Você aprenderá mais sobre esse aplicativo em seções posteriores.

  1. Abra o arquivo MoveShapeHub.cs .

  2. Substitua o código no arquivo MoveShapeHub.cs por este código:

    using Microsoft.AspNet.SignalR;
    using Newtonsoft.Json;
    
    namespace MoveShapeDemo
    {
        public class MoveShapeHub : Hub
        {
            public void UpdateModel(ShapeModel clientModel)
            {
                clientModel.LastUpdatedBy = Context.ConnectionId;
                // Update the shape model within our broadcaster
                Clients.AllExcept(clientModel.LastUpdatedBy).updateShape(clientModel);
            }
        }
        public class ShapeModel
        {
            // We declare Left and Top as lowercase with 
            // JsonProperty to sync the client and server models
            [JsonProperty("left")]
            public double Left { get; set; }
            [JsonProperty("top")]
            public double Top { get; set; }
            // We don't want the client to get the "LastUpdatedBy" property
            [JsonIgnore]
            public string LastUpdatedBy { get; set; }
        }
    }
    
  3. Salve o arquivo.

A MoveShapeHub classe é uma implementação de um hub do SignalR. Como no tutorial Introdução ao SignalR , o hub tem um método que os clientes chamam diretamente. Nesse caso, o cliente envia um objeto com as novas coordenadas X e Y da forma para o servidor. Essas coordenadas são transmitidas para todos os outros clientes conectados. O SignalR serializa automaticamente esse objeto usando JSON.

O aplicativo envia o ShapeModel objeto para o cliente. Possui membros para armazenar a posição da forma. A versão do objeto no servidor também tem um membro para rastrear quais dados do cliente estão sendo armazenados. Esse objeto impede que o servidor envie os dados de um cliente de volta para si mesmo. Esse membro usa o JsonIgnore atributo para impedir que o aplicativo serialize os dados e os envie de volta ao cliente.

Mapear para o hub quando o aplicativo for iniciado

Em seguida, você configura o mapeamento para o hub quando o aplicativo é iniciado. No SignalR 2, adicionar uma classe de inicialização OWIN cria o mapeamento.

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto e selecione Adicionar>Novo Item.

  2. Em Adicionar Novo Item – MoveShapeDemo, selecione Visual C#>Web Instalado>e, em seguida, selecione Classe de Inicialização OWIN.

  3. Nomeie a classe Startup e selecione OK.

  4. Substitua o código padrão no arquivo Startup.cs por este código:

    using Microsoft.Owin;
    using Owin;
    
    [assembly: OwinStartup(typeof(MoveShapeDemo.Startup))]
    namespace MoveShapeDemo
    {
        public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                // Any connection or hub wire up and configuration should go here
                app.MapSignalR();
            }
        }
    }
    

A classe de inicialização OWIN chama MapSignalR quando o aplicativo executa o Configuration método. O aplicativo adiciona a classe ao processo de inicialização do OWIN usando o OwinStartup atributo assembly.

Adicionar o cliente

Adicione a página HTML para o cliente.

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto e selecione Adicionar>Página HTML.

  2. Nomeie a página como Padrão e selecione OK.

  3. No Gerenciador de Soluções, clique com o botão direito do mouse em Default.html e selecione Definir como Página Inicial.

  4. Substitua o código padrão no arquivo Default.html por este código:

    <!DOCTYPE html>
    <html>
    <head>
        <title>SignalR MoveShape Demo</title>
        <style>
            #shape {
                width: 100px;
                height: 100px;
                background-color: #FF0000;
            }
        </style>
    </head>
    <body>
    <script src="Scripts/jquery-1.10.2.min.js"></script>
    <script src="Scripts/jquery-ui-1.10.4.min.js"></script>
    <script src="Scripts/jquery.signalR-2.1.0.js"></script>
    <script src="/signalr/hubs"></script>
    <script>
     $(function () {
                var moveShapeHub = $.connection.moveShapeHub,
                $shape = $("#shape"),
                shapeModel = {
                    left: 0,
                    top: 0
                };
                moveShapeHub.client.updateShape = function (model) {
                    shapeModel = model;
                    $shape.css({ left: model.left, top: model.top });
                };
                $.connection.hub.start().done(function () {
                    $shape.draggable({
                        drag: function () {
                            shapeModel = $shape.offset();
                            moveShapeHub.server.updateModel(shapeModel);
                        }
                    });
                });
            });
    </script>
        
        <div id="shape" />
    </body>
    </html>
    
  5. No Gerenciador de Soluções, expanda Scripts.

    As bibliotecas de scripts para jQuery e SignalR são visíveis no projeto.

    Importante

    O gerenciador de pacotes instala uma versão posterior dos scripts do SignalR.

  6. Atualize as referências de script no bloco de código para corresponder às versões dos arquivos de script no projeto.

Esse código HTML e JavaScript cria um vermelho div chamado shape. Ele habilita o comportamento de arrastar da forma usando a biblioteca jQuery e usa o drag evento para enviar a posição da forma para o servidor.

Executar o aplicativo

Você pode executar o aplicativo para ver se funciona. Quando você arrasta a forma em uma janela do navegador, ela também se move nos outros navegadores.

  1. Na barra de ferramentas, ative a Depuração de Script e selecione o botão de reprodução para executar o aplicativo no modo de Depuração.

    Captura de tela do usuário ativando o modo de depuração e selecionando reproduzir.

    Uma janela do navegador é aberta com a forma vermelha no canto superior direito.

  2. Copie o URL da página.

  3. Abra outro navegador e cole o URL na barra de endereço.

  4. Arraste a forma em uma das janelas do navegador. A forma na outra janela do navegador segue.

Embora o aplicativo funcione usando esse método, não é um modelo de programação recomendado. Não há limite superior para o número de mensagens enviadas. Como resultado, os clientes e o servidor ficam sobrecarregados com mensagens e o desempenho diminui. Além disso, o aplicativo exibe uma animação desconexa no cliente. Essa animação irregular acontece porque a forma se move instantaneamente por cada método. É melhor que a forma se mova suavemente para cada novo local. Em seguida, você aprenderá como corrigir esses problemas.

Adicionar o loop do cliente

Enviar o local da forma em cada evento de movimentação do mouse cria uma quantidade desnecessária de tráfego de rede. O aplicativo precisa limitar as mensagens do cliente.

Use a função javascript setInterval para configurar um loop que envia novas informações de posição para o servidor a uma taxa fixa. Este loop é uma representação básica de um "loop de jogo". É uma função repetidamente chamada que impulsiona todas as funcionalidades de um jogo.

  1. Substitua o código do cliente no arquivo Default.html por este código:

    <!DOCTYPE html>
    <html>
    <head>
    <title>SignalR MoveShape Demo</title>
    <style>
        #shape {
            width: 100px;
            height: 100px;
            background-color: #FF0000;
        }
    </style>
    </head>
    <body>
    <script src="Scripts/jquery-1.10.2.min.js"></script>
    <script src="Scripts/jquery-ui-1.10.4.min.js"></script>
    <script src="Scripts/jquery.signalR-2.1.0.js"></script>
    <script src="/signalr/hubs"></script>
    <script>
        $(function () {
            var moveShapeHub = $.connection.moveShapeHub,
                $shape = $("#shape"),
                // Send a maximum of 10 messages per second 
                // (mouse movements trigger a lot of messages)
                messageFrequency = 10, 
                // Determine how often to send messages in
                // time to abide by the messageFrequency
                updateRate = 1000 / messageFrequency, 
                shapeModel = {
                    left: 0,
                    top: 0
                },
                moved = false;
            moveShapeHub.client.updateShape = function (model) {
                shapeModel = model;
                $shape.css({ left: model.left, top: model.top });
            };
            $.connection.hub.start().done(function () {
                $shape.draggable({
                    drag: function () {
                        shapeModel = $shape.offset();
                        moved = true;
                    }
                });
                // Start the client side server update interval
                setInterval(updateServerModel, updateRate);
            });
            function updateServerModel() {
                // Only update server if we have a new movement
                if (moved) {
                    moveShapeHub.server.updateModel(shapeModel);
                    moved = false;
                }
            }
        });
    </script>
       
    <div id="shape" />
    </body>
    </html>
    

    Importante

    Você precisa substituir as referências de script novamente. Eles devem corresponder às versões dos scripts no projeto.

    Esse novo código adiciona a updateServerModel função. Ele é chamado em uma frequência fixa. A função envia os dados de posição para o servidor sempre que o moved sinalizador indica que há novos dados de posição a serem enviados.

  2. Selecione o botão play para iniciar o aplicativo

  3. Copie o URL da página.

  4. Abra outro navegador e cole o URL na barra de endereço.

  5. Arraste a forma em uma das janelas do navegador. A forma na outra janela do navegador segue.

Como o aplicativo limita o número de mensagens enviadas ao servidor, a animação não aparecerá tão suave no início.

Adicionar o loop do servidor

No aplicativo atual, as mensagens enviadas do servidor para o cliente são enviadas com a mesma frequência com que são recebidas. Esse tráfego de rede apresenta um problema semelhante ao que vemos no cliente.

O aplicativo pode enviar mensagens com mais frequência do que o necessário. Como resultado, a conexão pode ficar inundada. Esta seção descreve como atualizar o servidor para adicionar um temporizador que limita a taxa das mensagens enviadas.

  1. Substitua os conteúdos de MoveShapeHub.cs por este código:

    using System;
    using System.Threading;
    using Microsoft.AspNet.SignalR;
    using Newtonsoft.Json;
    
    namespace MoveShapeDemo
    {
        public class Broadcaster
        {
            private readonly static Lazy<Broadcaster> _instance = 
                new Lazy<Broadcaster>(() => new Broadcaster());
            // We're going to broadcast to all clients a maximum of 25 times per second
            private readonly TimeSpan BroadcastInterval = 
                TimeSpan.FromMilliseconds(40); 
            private readonly IHubContext _hubContext;
            private Timer _broadcastLoop;
            private ShapeModel _model;
            private bool _modelUpdated;
            public Broadcaster()
            {
                // Save our hub context so we can easily use it 
                // to send to its connected clients
                _hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>();
                _model = new ShapeModel();
                _modelUpdated = false;
                // Start the broadcast loop
                _broadcastLoop = new Timer(
                    BroadcastShape, 
                    null, 
                    BroadcastInterval, 
                    BroadcastInterval);
            }
            public void BroadcastShape(object state)
            {
                // No need to send anything if our model hasn't changed
                if (_modelUpdated)
                {
                    // This is how we can access the Clients property 
                    // in a static hub method or outside of the hub entirely
                    _hubContext.Clients.AllExcept(_model.LastUpdatedBy).updateShape(_model);
                    _modelUpdated = false;
                }
            }
            public void UpdateShape(ShapeModel clientModel)
            {
                _model = clientModel;
                _modelUpdated = true;
            }
            public static Broadcaster Instance
            {
                get
                {
                    return _instance.Value;
                }
            }
        }
            
        public class MoveShapeHub : Hub
        {
            // Is set via the constructor on each creation
            private Broadcaster _broadcaster;
            public MoveShapeHub()
                : this(Broadcaster.Instance)
            {
            }
            public MoveShapeHub(Broadcaster broadcaster)
            {
                _broadcaster = broadcaster;
            }
            public void UpdateModel(ShapeModel clientModel)
            {
                clientModel.LastUpdatedBy = Context.ConnectionId;
                // Update the shape model within our broadcaster
                _broadcaster.UpdateShape(clientModel);
            }
        }
        public class ShapeModel
        {
            // We declare Left and Top as lowercase with 
            // JsonProperty to sync the client and server models
            [JsonProperty("left")]
            public double Left { get; set; }
            [JsonProperty("top")]
            public double Top { get; set; }
            // We don't want the client to get the "LastUpdatedBy" property
            [JsonIgnore]
            public string LastUpdatedBy { get; set; }
        }
        
    }
    
  2. Selecione o botão play para iniciar o aplicativo.

  3. Copie o URL da página.

  4. Abra outro navegador e cole o URL na barra de endereço.

  5. Arraste a forma em uma das janelas do navegador.

Esse código expande o cliente para adicionar a Broadcaster classe. A nova classe limita as mensagens de saída usando a Timer classe do .NET Framework.

É bom saber que o hub em si é transitório. É criado sempre que é necessário. Portanto, o aplicativo cria o Broadcaster como um singleton. Ele usa inicialização lenta para adiar a Broadcastercriação do até que seja necessário. Isso garante que o aplicativo crie a primeira instância do hub completamente antes de iniciar o cronômetro.

A chamada para a função dos UpdateShape clientes é então movida para fora do método do UpdateModel hub. Ele não é mais chamado imediatamente sempre que o aplicativo recebe mensagens. Em vez disso, o aplicativo envia as mensagens para os clientes a uma taxa de 25 chamadas por segundo. O processo é gerenciado _broadcastLoop pelo cronômetro de dentro da Broadcaster classe.

Por fim, em vez de chamar o método do cliente diretamente do hub, a Broadcaster classe precisa obter uma referência ao hub em operação _hubContext no momento. Ele obtém a referência com o GlobalHost.

Adicione animação suave

O aplicativo está quase terminado, mas poderíamos fazer mais uma melhoria. O aplicativo move a forma no cliente em resposta às mensagens do servidor. Em vez de definir a posição da forma para o novo local fornecido pelo servidor, use a função da animate biblioteca JQuery UI. Ele pode mover a forma suavemente entre sua posição atual e nova.

  1. Atualize o método do updateShape cliente no arquivo Default.html para se parecer com o código realçado:

    <!DOCTYPE html>
    <html>
    <head>
        <title>SignalR MoveShape Demo</title>
        <style>
            #shape {
                width: 100px;
                height: 100px;
                background-color: #FF0000;
            }
        </style>
    </head>
    <body>
    <script src="Scripts/jquery-1.10.2.min.js"></script>
    <script src="Scripts/jquery-ui-1.10.4.min.js"></script>
    <script src="Scripts/jquery.signalR-2.1.0.js"></script>
    <script src="/signalr/hubs"></script>
    <script>
            $(function () {
                var moveShapeHub = $.connection.moveShapeHub,
                    $shape = $("#shape"),
                    // Send a maximum of 10 messages per second 
                    // (mouse movements trigger a lot of messages)
                    messageFrequency = 10, 
                    // Determine how often to send messages in
                    // time to abide by the messageFrequency
                    updateRate = 1000 / messageFrequency, 
                    shapeModel = {
                        left: 0,
                        top: 0
                    },
                    moved = false;
                moveShapeHub.client.updateShape = function (model) {
                     shapeModel = model;
                     // Gradually move the shape towards the new location (interpolate)
                     // The updateRate is used as the duration because by the time 
                     // we get to the next location we want to be at the "last" location
                     // We also clear the animation queue so that we start a new 
                     // animation and don't lag behind.
                     $shape.animate(shapeModel, { duration: updateRate, queue: false });
                };
                $.connection.hub.start().done(function () {
                    $shape.draggable({
                        drag: function () {
                            shapeModel = $shape.offset();
                            moved = true;
                        }
                    });
                    // Start the client side server update interval
                    setInterval(updateServerModel, updateRate);
                });
                function updateServerModel() {
                    // Only update server if we have a new movement
                    if (moved) {
                        moveShapeHub.server.updateModel(shapeModel);
                        moved = false;
                    }
                }
            });
    </script>
       
        <div id="shape" />
    </body>
    </html>
    
  2. Selecione o botão play para iniciar o aplicativo.

  3. Copie o URL da página.

  4. Abra outro navegador e cole o URL na barra de endereço.

  5. Arraste a forma em uma das janelas do navegador.

O movimento da forma na outra janela parece menos irregular. O aplicativo interpola seu movimento ao longo do tempo, em vez de ser definido uma vez por mensagem recebida.

Esse código move a forma do local antigo para o novo. O servidor fornece a posição da forma ao longo do intervalo de animação. Nesse caso, são 100 milissegundos. O aplicativo limpa qualquer animação anterior em execução na forma antes do início da nova animação.

Obter o código

Download do projeto concluído

Recursos adicionais

Para obter mais informações sobre o SignalR, consulte os seguintes recursos:

Próximas etapas

Neste tutorial, você:

  • Configurar o projeto
  • Criou o aplicativo base
  • Mapeado para o hub quando o aplicativo é iniciado
  • Adicionado o cliente
  • Executou o aplicativo
  • Adicionado o loop do cliente
  • Adicionado o loop do servidor
  • Adicionada animação suave

Avance para o próximo artigo para saber como criar um aplicativo Web que usa o ASP.NET SignalR 2 para fornecer a funcionalidade de transmissão do servidor.