Cliente .NET SignalR do ASP.NET Core
A biblioteca de clientes .NET SignalR do ASP.NET Core permite que você se comunique com hubs SignalR de aplicativos .NET.
Exibir ou baixar código de exemplo (como baixar)
O exemplo de código neste artigo é um aplicativo WPF que usa o cliente .NET SignalR do ASP.NET Core.
Instalar o pacote SignalR do cliente .NET
O pacote Microsoft.AspNetCore.SignalR.Client é necessário para que os clientes .NET se conectem aos hubs SignalR.
Para instalar a biblioteca de clientes, execute o seguinte comando na janela Console do gerenciador de pacotes:
Install-Package Microsoft.AspNetCore.SignalR.Client
Conectar-se a um hub
Para estabelecer uma conexão, crie um HubConnectionBuilder
e chame Build
. A URL do hub, o protocolo, o tipo de transporte, o nível de log, os cabeçalhos e outras opções podem ser configurados durante a criação de uma conexão. Configure as opções necessárias inserindo qualquer um dos métodos HubConnectionBuilder
em Build
. Inicie a conexão com StartAsync
.
using System;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.AspNetCore.SignalR.Client;
namespace SignalRChatClient
{
public partial class MainWindow : Window
{
HubConnection connection;
public MainWindow()
{
InitializeComponent();
connection = new HubConnectionBuilder()
.WithUrl("http://localhost:53353/ChatHub")
.Build();
connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0,5) * 1000);
await connection.StartAsync();
};
}
private async void connectButton_Click(object sender, RoutedEventArgs e)
{
connection.On<string, string>("ReceiveMessage", (user, message) =>
{
this.Dispatcher.Invoke(() =>
{
var newMessage = $"{user}: {message}";
messagesList.Items.Add(newMessage);
});
});
try
{
await connection.StartAsync();
messagesList.Items.Add("Connection started");
connectButton.IsEnabled = false;
sendButton.IsEnabled = true;
}
catch (Exception ex)
{
messagesList.Items.Add(ex.Message);
}
}
private async void sendButton_Click(object sender, RoutedEventArgs e)
{
try
{
await connection.InvokeAsync("SendMessage",
userTextBox.Text, messageTextBox.Text);
}
catch (Exception ex)
{
messagesList.Items.Add(ex.Message);
}
}
}
}
Manipular conexão perdida
Reconectar automaticamente
O HubConnection pode ser configurado para reconectar automaticamente usando o método WithAutomaticReconnect
no HubConnectionBuilder. Ele não se reconectará automaticamente por padrão.
HubConnection connection= new HubConnectionBuilder()
.WithUrl(new Uri("http://127.0.0.1:5000/chathub"))
.WithAutomaticReconnect()
.Build();
Sem parâmetros, WithAutomaticReconnect()
configura o cliente para aguardar 0, 2, 10 e 30 segundos, respectivamente, antes de tentar cada tentativa de reconexão, parando após quatro tentativas com falha.
Antes de iniciar qualquer tentativa de reconexão, o HubConnection
fará a transição para o estado HubConnectionState.Reconnecting
e disparará o evento Reconnecting
. Isso oferece uma oportunidade de avisar os usuários de que a conexão foi perdida e desabilitar elementos da interface do usuário. Aplicativos não interativos podem começar a enfileirar ou soltar mensagens.
connection.Reconnecting += error =>
{
Debug.Assert(connection.State == HubConnectionState.Reconnecting);
// Notify users the connection was lost and the client is reconnecting.
// Start queuing or dropping messages.
return Task.CompletedTask;
};
Se o cliente se reconectar com êxito em suas quatro primeiras tentativas, o HubConnection
fará a transição de volta para o estado Connected
e disparará o evento Reconnected
. Isso oferece uma oportunidade de informar aos usuários que a conexão foi restabelecida e desativar qualquer mensagem na fila.
Como a conexão parece totalmente nova para o servidor, um novo ConnectionId
será fornecido aos manipuladores de eventos Reconnected
.
Aviso
O parâmetro connectionId
do manipulador de eventos Reconnected
será nulo se o HubConnection
tiver sido configurado para ignorar a negociação.
connection.Reconnected += connectionId =>
{
Debug.Assert(connection.State == HubConnectionState.Connected);
// Notify users the connection was reestablished.
// Start dequeuing messages queued while reconnecting if any.
return Task.CompletedTask;
};
WithAutomaticReconnect()
não configurará o HubConnection
para repetir falhas iniciais de início, portanto, as falhas de inicialização precisam ser tratadas manualmente:
public static async Task<bool> ConnectWithRetryAsync(HubConnection connection, CancellationToken token)
{
// Keep trying to until we can start or the token is canceled.
while (true)
{
try
{
await connection.StartAsync(token);
Debug.Assert(connection.State == HubConnectionState.Connected);
return true;
}
catch when (token.IsCancellationRequested)
{
return false;
}
catch
{
// Failed to connect, trying again in 5000 ms.
Debug.Assert(connection.State == HubConnectionState.Disconnected);
await Task.Delay(5000);
}
}
}
Se o cliente não se reconectar com êxito em suas quatro primeiras tentativas, o HubConnection
fará a transição para o estado Disconnected
e disparará o evento Closed. Isso oferece uma oportunidade de tentar reiniciar a conexão manualmente ou informar aos usuários que a conexão foi perdida permanentemente.
connection.Closed += error =>
{
Debug.Assert(connection.State == HubConnectionState.Disconnected);
// Notify users the connection has been closed or manually try to restart the connection.
return Task.CompletedTask;
};
Para configurar um número personalizado de tentativas de reconexão antes de desconectar ou alterar o tempo de reconexão, WithAutomaticReconnect
aceita uma matriz de números que representam o atraso em milissegundos para aguardar antes de iniciar cada tentativa de reconexão.
HubConnection connection= new HubConnectionBuilder()
.WithUrl(new Uri("http://127.0.0.1:5000/chathub"))
.WithAutomaticReconnect(new[] { TimeSpan.Zero, TimeSpan.Zero, TimeSpan.FromSeconds(10) })
.Build();
// .WithAutomaticReconnect(new[] { TimeSpan.Zero, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(30) }) yields the default behavior.
O exemplo anterior configura o HubConnection
para começar a tentar se conectar novamente imediatamente após a conexão ser perdida. Isso também é verdadeiro para a configuração padrão.
Se a primeira tentativa de reconexão falhar, a segunda tentativa de reconexão também será iniciada imediatamente em vez de aguardar 2 segundos como ocorreria na configuração padrão.
Se a segunda tentativa de reconexão falhar, a terceira tentativa de reconexão será iniciada em 10 segundos, o que é, novamente, como a configuração padrão.
Em seguida, o comportamento personalizado diverge novamente do comportamento padrão, parando após a terceira falha de tentativa de reconexão. Na configuração padrão, haveria mais uma tentativa de reconexão em mais 30 segundos.
Se você quiser ter ainda mais controle sobre o tempo e o número de tentativas de reconexão automática, WithAutomaticReconnect
aceitará um objeto que implementa a interface IRetryPolicy
, que tem um único método chamado NextRetryDelay
.
NextRetryDelay
usa um único argumento com o tipo RetryContext
. O RetryContext
tem três propriedades: PreviousRetryCount
, ElapsedTime
e RetryReason
, que são um long
, um TimeSpan
e um Exception
, respectivamente. Antes da primeira tentativa de reconexão, PreviousRetryCount
e ElapsedTime
serão zero, e RetryReason
será a Exceção que causou a perda da conexão. Após cada tentativa de repetição com falha, PreviousRetryCount
será incrementado em um, ElapsedTime
será atualizado para refletir o tempo gasto na reconexão até agora e RetryReason
será a Exceção que causou a falha da última tentativa de reconexão.
NextRetryDelay
deve retornar um TimeSpan que representa o tempo de espera antes da próxima tentativa de reconexão ou null
se o HubConnection
deve parar de se reconectar.
public class RandomRetryPolicy : IRetryPolicy
{
private readonly Random _random = new Random();
public TimeSpan? NextRetryDelay(RetryContext retryContext)
{
// If we've been reconnecting for less than 60 seconds so far,
// wait between 0 and 10 seconds before the next reconnect attempt.
if (retryContext.ElapsedTime < TimeSpan.FromSeconds(60))
{
return TimeSpan.FromSeconds(_random.NextDouble() * 10);
}
else
{
// If we've been reconnecting for more than 60 seconds so far, stop reconnecting.
return null;
}
}
}
HubConnection connection = new HubConnectionBuilder()
.WithUrl(new Uri("http://127.0.0.1:5000/chathub"))
.WithAutomaticReconnect(new RandomRetryPolicy())
.Build();
Como alternativa, você pode escrever código que reconectará o cliente manualmente, conforme demonstrado em Reconectar manualmente.
Reconectar manualmente
Aviso
Antes da versão 3.0, o cliente .NET para SignalR não se reconecta automaticamente. Você deve escrever um código que reconecte o cliente manualmente.
Use o evento Closed para responder a uma conexão perdida. Por exemplo, talvez você queira automatizar a reconexão.
O evento Closed
requer um delegado que retorna um Task
, que permite que o código assíncrono seja executado sem usar async void
. Para satisfazer a assinatura de delegado em um manipulador de eventos Closed
que é executado de forma síncrona, retorne Task.CompletedTask
:
connection.Closed += (error) => {
// Do your close logic.
return Task.CompletedTask;
};
O principal motivo para o suporte assíncrono é para que você possa reiniciar a conexão. Iniciar uma conexão é uma ação assíncrona.
Em um manipulador Closed
que reinicia a conexão, considere aguardar algum atraso aleatório para evitar sobrecarregar o servidor, conforme mostrado no exemplo a seguir:
connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0,5) * 1000);
await connection.StartAsync();
};
Chamar métodos de hub do cliente
InvokeAsync
chama métodos no hub. Passe o nome do método do hub e todos os argumentos definidos no método de hub para InvokeAsync
. SignalR é assíncrono, portanto, use async
e await
ao fazer as chamadas.
await connection.InvokeAsync("SendMessage",
userTextBox.Text, messageTextBox.Text);
O método InvokeAsync
retorna um Task
que é concluído quando o método de servidor retorna. O valor retornado, se houver, é fornecido como o resultado do Task
. Todas as exceções geradas pelo método no servidor produzem um Task
com falha. Use a sintaxe await
para aguardar a conclusão do método de servidor e a sintaxe try...catch
para tratar erros.
O método SendAsync
retorna um Task
que é concluído quando a mensagem é enviada ao servidor. Nenhum valor retornado é fornecido, pois este Task
não aguarda até que o método de servidor seja concluído. Todas as exceções geradas no cliente ao enviar a mensagem produzem um Task
com falha. Use a sintaxe await
e try...catch
para lidar com erros de envio.
Observação
Os métodos de hub de chamada de um cliente só têm suporte ao usar o Serviço SignalR do Azure no modo Padrão. Para obter mais informações, consulte Perguntas frequentes (repositório GitHub azure-signalr).
Chamar métodos de cliente do hub
Defina os métodos que o hub chama usando connection.On
após a criação, mas antes de iniciar a conexão.
connection.On<string, string>("ReceiveMessage", (user, message) =>
{
this.Dispatcher.Invoke(() =>
{
var newMessage = $"{user}: {message}";
messagesList.Items.Add(newMessage);
});
});
O código anterior em connection.On
é executado quando o código do lado do servidor o chama usando o método SendAsync
.
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user,message);
}
Observação
Embora o lado do hub da conexão dê suporte a mensagens fortemente tipadas, o cliente deve se registrar usando o método genérico HubConnection.On com o nome do método. Para obter um exemplo, confira Host ASP.NET Core SignalR em serviços em segundo plano.
Registro em log e tratamento de erros
Tratar erros com uma instrução try-catch. Inspecione o objeto Exception
para determinar a ação adequada a ser tomada após a ocorrência de um erro.
try
{
await connection.InvokeAsync("SendMessage",
userTextBox.Text, messageTextBox.Text);
}
catch (Exception ex)
{
messagesList.Items.Add(ex.Message);
}