ASP.NET Core SignalR .NET 用戶端
ASP.NET Core SignalR .NET 用戶端程式庫可讓您從 .NET 應用程式與 SignalR 中樞通訊。
檢視或下載範例程式碼 \(英文\) (如何下載)
本文中的程式碼範例是使用 ASP.NET Core SignalR .NET 用戶端的 WPF 應用程式。
安裝 SignalR .NET 用戶端套件
.NET 用戶端需要Microsoft.AspNetCore.SignalR.Client 套件才能連線到 SignalR 中樞。
若要安裝用戶端程式庫,請在 [套件管理員主控台] 視窗中執行下列命令:
Install-Package Microsoft.AspNetCore.SignalR.Client
連線到中樞
若要建立連接,請建立 HubConnectionBuilder
並呼叫 Build
。 您可以在建置連線時設定中樞 URL、通訊協定、傳輸類型、記錄層級、標頭和其他選項。 將任何 HubConnectionBuilder
方法插入 至 Build
,以設定任何必要選項。 使用 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);
}
}
}
}
處理遺失的連線
自動重新連線
透過使用 HubConnectionBuilder 上的 WithAutomaticReconnect
方法,可以將 HubConnection 設定為自動重新連線。 依預設,並不會自動重新連線。
HubConnection connection= new HubConnectionBuilder()
.WithUrl(new Uri("http://127.0.0.1:5000/chathub"))
.WithAutomaticReconnect()
.Build();
如果不使用任何參數,WithAutomaticReconnect()
將用戶端設定為在嘗試每次重新連線之前分別等待 0、2、10 和 30 秒,並在四次嘗試失敗後停止。
開始任何重新連線嘗試之前,HubConnection
轉換至 HubConnectionState.Reconnecting
狀態並引發 Reconnecting
事件。 這可讓您警告使用者連線已遺失,並停用 UI 元素。 非互動式應用程式可以開始佇列或卸除訊息。
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;
};
如果用戶端在前四次嘗試中成功重新連線,則 HubConnection
會轉換回 Connected
狀態並引發 Reconnected
事件。 這可讓您通知使用者連線已重新建立,並清除任何已排入佇列的訊息。
由於該連線對伺服器來說是全新的,因此將向 Reconnected
事件處理常式提供新的 ConnectionId
。
警告
如果 HubConnection
設定為 跳過交涉,Reconnected
事件處理常式的 connectionId
參數將會是 Null。
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()
不會將 HubConnection
設定為重試初始啟動失敗,因此必須手動處理啟動失敗:
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);
}
}
}
如果用戶端在前四次嘗試中未成功重新連線,則 HubConnection
會轉換至 Disconnected
狀態並引發 Closed 事件。 這可讓您嘗試手動重新啟動連線,或通知使用者連線已永久遺失。
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;
};
若要在中斷連線或變更重新連線時間之前設定自訂重新連線嘗試次數,WithAutomaticReconnect
接受一個數字陣列,表示在開始每次重新連線嘗試之前要等待的延遲時間 (以毫秒為單位)。
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.
上述範例會將 HubConnection
設定為在連線遺失後立即開始嘗試重新連線。 這也適用於預設設定。
如果第一次重新連線嘗試失敗,則第二次重新連線嘗試也會立即啟動,而不是像預設設定中那樣等待 2 秒。
如果第二次重新連線嘗試失敗,第三次重新連線嘗試將會在 10 秒內開始,這又與預設組態類似。
然後,自訂行為會在第三次重新連線嘗試失敗後停止,再次偏離預設行為。 在預設組態中,會再嘗試一次 30 秒的重新連線嘗試。
如果您想要更充分掌控自動重新連線嘗試的時間和次數,WithAutomaticReconnect
接受一個實作 IRetryPolicy
介面的物件,該介面有一個名為 NextRetryDelay
的方法。
NextRetryDelay
會採用類型為 RetryContext
的單一引數。 RetryContext
有三個屬性:PreviousRetryCount
、ElapsedTime
和 RetryReason
,分別是 long
、TimeSpan
和 Exception
。 在第一次重新連線嘗試之前,PreviousRetryCount
和 ElapsedTime
都會是零,而 RetryReason
會是造成連線中斷的例外狀況。 每次重試失敗之後,PreviousRetryCount
將會遞增一次,ElapsedTime
將會更新以反映到目前為止重新連線所花費的時間量,而 RetryReason
會是造成上次重新連線嘗試失敗的例外狀況。
NextRetryDelay
必須傳回一個時間範圍,代表下一次重新連線嘗試之前所要等候的時間,或者,如果 HubConnection
應該停止重新連線,則傳回 null
。
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();
或者,您可以撰寫程式碼,以手動方式重新連線用戶端,如手動重新連線中所示。
手動重新連線
警告
在 3.0 之前,SignalR 的 .NET 用戶端不會自動重新連線。 您必須撰寫程式碼,以手動方式重新連線您的用戶端。
使用 Closed 事件來回應連線中斷。 例如,您可能想要自動重新連線。
Closed
事件需要傳回 Task
的委派,其可讓非同步程式碼執行而不使用 async void
。 若要滿足以同步方式執行的 Closed
事件處理常式中的委派簽章,請傳回 Task.CompletedTask
:
connection.Closed += (error) => {
// Do your close logic.
return Task.CompletedTask;
};
非同步支援的主要原因是您可以重新啟動連線。 啟動連線是一個非同步動作。
在重新啟動連線的 Closed
處理常式中,請考慮等候一些任意的延遲以防止伺服器多載,如下列範例所示:
connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0,5) * 1000);
await connection.StartAsync();
};
從用戶端呼叫中樞方法
InvokeAsync
會呼叫中樞上的方法。 將中樞方法名稱和中樞方法中定義的任何引數傳遞至 InvokeAsync
。 SignalR 是非同步,因此請在進行呼叫時使用 async
和 await
。
await connection.InvokeAsync("SendMessage",
userTextBox.Text, messageTextBox.Text);
方法 InvokeAsync
會傳回 Task
,其會在伺服器方法傳回時完成。 如果有任何傳回值,則會以 Task
的結果提供。 方法在伺服器上擲回的任何例外狀況都會產生錯誤 Task
。 使用 await
語法等候伺服器方法完成,並使用 try...catch
語法處理錯誤。
方法 SendAsync
會傳回 Task
,其會在訊息傳送至伺服器時完成。 未提供傳回值,因為此 Task
不會等到伺服器方法完成。 傳送訊息時,用戶端上擲回的任何例外狀況都會產生錯誤的 Task
。 使用 await
和 try...catch
語法來處理傳送錯誤。
注意
只有在以預設 SignalR 模式使用 Azure Service 時,才支援從用戶端呼叫中樞方法。 如需詳細資訊,請參閱常見問題集 (azure-signalr GitHub 存放庫)。
從中樞呼叫用戶端方法
定義中樞在建置之後但在啟動連線之前使用 connection.On
呼叫的方法。
connection.On<string, string>("ReceiveMessage", (user, message) =>
{
this.Dispatcher.Invoke(() =>
{
var newMessage = $"{user}: {message}";
messagesList.Items.Add(newMessage);
});
});
在 connection.On
中的上述程式碼會在伺服器端程式碼使用 SendAsync
方法呼叫時執行。
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user,message);
}
注意
雖然連線的中樞端支援強型別傳訊,但用戶端必須使用具有方法名稱的泛型方法 HubConnection.On 進行註冊。 如需範例,請參閱背景服務中的主機 ASP.NET Core SignalR。
錯誤處理和記錄
使用 try-catch 陳述式處理錯誤。 檢查 Exception
物件,以判斷錯誤發生後要採取的適當動作。
try
{
await connection.InvokeAsync("SendMessage",
userTextBox.Text, messageTextBox.Text);
}
catch (Exception ex)
{
messagesList.Items.Add(ex.Message);
}