Cliente .NET de ASP.NET Core SignalR

La biblioteca cliente .NET de ASP.NET Core SignalR le permite comunicarse con SignalR centros de conectividad desde aplicaciones .NET.

Vea o descargue el código de ejemplo (cómo descargarlo)

El ejemplo de código de este artículo es una aplicación WPF que usa el cliente .NET de ASP.NET Core SignalR.

Instalación del paquete cliente .NET SignalR

El paquete de Microsoft.AspNetCore.SignalR.Client es necesario para que los clientes de .NET se conecten a los centros de conectividad SignalR.

Para instalar la biblioteca cliente, ejecute el comando siguiente en la ventana de la consola del Administrador de paquetes:

Install-Package Microsoft.AspNetCore.SignalR.Client

Conexión a un centro de conectividad

Para establecer una conexión, cree un HubConnectionBuilder y llame a Build. La dirección URL del centro de conectividad, el protocolo, el tipo de transporte, el nivel de registro, los encabezados y otras opciones se pueden configurar al crear una conexión. Configure las opciones necesarias insertando cualquiera de losHubConnectionBuilder métodos en Build. Inicie la conexión con 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 la conexión perdida

Volver a conectarse automáticamente

El HubConnection se puede configurar para volver a conectarse automáticamente mediante el WithAutomaticReconnect método en HubConnectionBuilder. No se volverá a conectar automáticamente de forma predeterminada.

HubConnection connection= new HubConnectionBuilder()
    .WithUrl(new Uri("http://127.0.0.1:5000/chathub"))
    .WithAutomaticReconnect()
    .Build();

Sin ningún parámetro, WithAutomaticReconnect() configura el cliente para que espere 0, 2, 10 y 30 segundos respectivamente antes de intentar cada intento de reconexión, deteniéndose tras cuatro intentos fallidos.

Antes de iniciar cualquier intento de reconexión, HubConnection pasará al estado HubConnectionState.Reconnecting y desencadenará el evento Reconnecting. Esto proporciona una oportunidad para advertir a los usuarios de que se ha perdido la conexión y deshabilitar los elementos de la interfaz de usuario. Las aplicaciones no interactivas pueden iniciar colas o quitar mensajes.

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;
};

Si el cliente se vuelve a conectar correctamente en sus cuatro primeros intentos, el HubConnection vuelve al estado Connected y desencadena el evento Reconnected. Esto proporciona la oportunidad de informar a los usuarios de la conexión que se ha restablecido y quitar de la cola cualquier mensaje.

Dado que la conexión parece totalmente nueva para el servidor, se proporciona un nuevo ConnectionId al controlador de eventos Reconnected.

Advertencia

El parámetro Reconnected del controlador de eventos connectionId será null si se configuró el HubConnection para omitir la negociación.

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() no configurará HubConnection para reintentar los errores de arranque iniciales, por lo que los errores de arranque deben tratarse 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);
        }
    }
}

Si el cliente se vuelve a conectar correctamente en sus cuatro primeros intentos, el HubConnection vuelve al estado Disconnected y desencadena el evento Closed. Esto proporciona la oportunidad de intentar reiniciar la conexión manualmente o informar a los usuarios de que la conexión se ha perdido 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 un número personalizado de intentos de reconexión antes de desconectar o cambiar el tiempo de reconexión, WithAutomaticReconnect acepta una matriz de números que representa el retraso en milisegundos para esperar antes de iniciar cada intento de reconexión.

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.

El ejemplo anterior configura el HubConnection para que empiece a intentar reconectarse inmediatamente después de que se pierda la conexión. Esto también es cierto para la configuración predeterminada.

Si se produce un error en el primer intento de reconexión, el segundo intento de reconexión también se iniciará inmediatamente en lugar de esperar 2 segundos como lo haría en la configuración predeterminada.

Si se produce un error en el segundo intento de conexión, el tercer intento de conexión se iniciará en 10 segundos, como la configuración predeterminada.

A continuación, el comportamiento personalizado se diferencia de nuevo del comportamiento predeterminado al detenerse después del tercer error de intento de conexión. En la configuración predeterminada, habría un intento de conexión más en otros 30 segundos.

Si quiere aún más control sobre el tiempo y el número de intentos de conexión automática, WithAutomaticReconnect acepta un objeto que implemente la interfaz IRetryPolicy, que tiene un único método llamado NextRetryDelay.

NextRetryDelay toma un único argumento con el tipo RetryContext. El RetryContext tiene tres propiedades: PreviousRetryCount, ElapsedTime y RetryReason, que son long, TimeSpan y Exception respectivamente. Antes del primer intento de conexión, tanto PreviousRetryCount como ElapsedTime serán cero, y RetryReason será la excepción que provocó la pérdida de conexión. Después de cada intento de conexión fallido, PreviousRetryCount se incrementará en uno, ElapsedTime se actualizará para reflejar la cantidad de tiempo empleado en la reconexión hasta el momento, y RetryReason será la excepción que causó el último intento de conexión fallido.

NextRetryDelay debe devolver un TimeSpan que represente el tiempo que hay que esperar antes del siguiente intento de conexión o null si HubConnection debe dejar de conectarse de nuevo.

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, puede escribir código que vuelva a conectar el cliente manualmente, como se muestra en Volver a conectarse manualmente.

Volver a conectarse manualmente

Advertencia

Antes de la versión 3.0, el cliente .NET para SignalR no se vuelve a conectar automáticamente. Debe escribir código que vuelva a conectar el cliente manualmente.

Use el evento Closed para responder a una conexión perdida. Por ejemplo, puede que desee automatizar la reconexión.

El evento Closed requiere un delegado que devuelva un Task, que permite que el código asincrónico se ejecute sin usar async void. Para satisfacer la firma del delegado en un controlador de eventos Closed que se ejecute de forma sincrónica, devuelva Task.CompletedTask:

connection.Closed += (error) => {
    // Do your close logic.
    return Task.CompletedTask;
};

La razón principal de la compatibilidad asincrónica es que puede reiniciar la conexión. Iniciar una conexión es una acción asincrónica.

En un controlador Closed que reinicie la conexión, considere la posibilidad de esperar un retraso aleatorio para evitar sobrecargar el servidor, como se muestra en el ejemplo siguiente:

connection.Closed += async (error) =>
{
    await Task.Delay(new Random().Next(0,5) * 1000);
    await connection.StartAsync();
};

Llamada a los métodos del centro de conectividad desde el cliente

InvokeAsync llama a métodos en el centro de conectividad. Pase el nombre y los argumentos del método del centro de conectividad definidos en él a InvokeAsync. SignalR es asincrónico, así que use async y await al realizar las llamadas.

await connection.InvokeAsync("SendMessage", 
    userTextBox.Text, messageTextBox.Text);

El método InvokeAsync devuelve un Task que se completa cuando devuelve el método de servidor. El valor devuelto, si existe, se proporciona como resultado de Task. Todas las excepciones producidas por el método en el servidor producen un error Task. Use la sintaxis await para esperar a que el método de servidor se complete y la sintaxis try...catch controle los errores.

El método SendAsync devuelve un Task que se completa cuando se ha enviado el mensaje al servidor. No se proporciona ningún valor devuelto, ya que Task no espera hasta que se complete el método de servidor. Todas las excepciones producidas en el cliente al enviar el mensaje producen un error Task. Use la sintaxis await y try...catch para controlar los errores de envío.

Nota

La llamada a los métodos del concentrador desde un cliente solo es compatible cuando se usa el Servicio de Azure SignalR en modo Predeterminado. Para más información, consulte Preguntas más frecuentes (repositorio de GitHub de azure-signalr).

Llamada a los métodos de cliente desde el centro de conectividad

Defina los métodos a los que el centro de conectividad llama mediante connection.On después de la compilación, pero antes de iniciar la conexión.

connection.On<string, string>("ReceiveMessage", (user, message) =>
{
    this.Dispatcher.Invoke(() =>
    {
       var newMessage = $"{user}: {message}";
       messagesList.Items.Add(newMessage);
    });
});

El código anterior de connection.On se ejecuta cuando el código del lado servidor lo llama mediante el método SendAsync.

public async Task SendMessage(string user, string message)
{
    await Clients.All.SendAsync("ReceiveMessage", user,message);
}

Nota

Aunque el lado del centro de conectividad de la conexión admite mensajería fuertemente tipada, el cliente debe registrarse mediante el método genérico HubConnection.On con el nombre del método. Para obtener un ejemplo, consulte Host ASP.NET CoreSignalR en servicios en segundo plano.

Registro y control de errores

Controle los errores con una instrucción try-catch. Inspeccione el objeto Exception para determinar la acción adecuada que se realizará después de que se produzca un error.

try
{
    await connection.InvokeAsync("SendMessage", 
        userTextBox.Text, messageTextBox.Text);
}
catch (Exception ex)
{                
    messagesList.Items.Add(ex.Message);                
}

Recursos adicionales