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
- Visual Studio 2017 com a carga de trabalho ASP.NET e desenvolvimento para a Web.
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.
No Visual Studio, crie um aplicativo Web ASP.NET.
Na janela Novo ASP.NET Aplicativo Web – MoveShapeDemo, deixe Vazio selecionado e selecione OK.
No Gerenciador de Soluções, clique com o botão direito do mouse no projeto e selecione Adicionar>Novo Item.
Em Adicionar Novo Item – MoveShapeDemo, selecione Visual C#>Web>SignalR Instalado>e, em seguida, selecione Classe de Hub do SignalR (v2).
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.
Selecione Ferramentas>Gerenciador de Pacotes NuGet>Console do Gerenciador de Pacotes.
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.
No Gerenciador de Soluções, expanda o nó 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.
Abra o arquivo MoveShapeHub.cs .
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; } } }
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.
No Gerenciador de Soluções, clique com o botão direito do mouse no projeto e selecione Adicionar>Novo Item.
Em Adicionar Novo Item – MoveShapeDemo, selecione Visual C#>Web Instalado>e, em seguida, selecione Classe de Inicialização OWIN.
Nomeie a classe Startup e selecione OK.
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.
No Gerenciador de Soluções, clique com o botão direito do mouse no projeto e selecione Adicionar>Página HTML.
Nomeie a página como Padrão e selecione OK.
No Gerenciador de Soluções, clique com o botão direito do mouse em Default.html e selecione Definir como Página Inicial.
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>
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.
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.
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.
Uma janela do navegador é aberta com a forma vermelha no canto superior direito.
Copie o URL da página.
Abra outro navegador e cole o URL na barra de endereço.
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.
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 omoved
sinalizador indica que há novos dados de posição a serem enviados.Selecione o botão play para iniciar o aplicativo
Copie o URL da página.
Abra outro navegador e cole o URL na barra de endereço.
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.
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; } } }
Selecione o botão play para iniciar o aplicativo.
Copie o URL da página.
Abra outro navegador e cole o URL na barra de endereço.
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 Broadcaster
criaçã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.
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>
Selecione o botão play para iniciar o aplicativo.
Copie o URL da página.
Abra outro navegador e cole o URL na barra de endereço.
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
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.