我有一个服务器(ASP.NET Core Web API 项目)和一个客户端(Unity/HoloLens)。正常情况下连接工作正常。但是,我想看看断开连接时会发生什么以及如何重新连接。
工作流程
我启动服务器,将客户端连接到它并关闭服务器。
代码中发生了什么
断开连接后,
WithAutomaticReconnect
中定义的行为被执行,然后连接被关闭。
关闭后我想建立一个新的连接。因此,我通过调用我的操作从方法
Closed
重新启动连接。 StartAsync
结果为 HttpRequestException
并且按预期工作,因为服务器仍处于离线状态。奇怪的是,一旦发生这种情况,我的应用程序(在 HoloLens 上)的 FPS 就会从 60FPS 下降到 10FPS。它会在这个低水平停留大约 20 秒,然后再次升至 60FPS。这里发生了什么?
错误
默认:⨯[SignalRClient]:System.Net.Http.HttpRequestException:发送请求时发生错误
System.Net.WebException:错误:ConnectFailure(无法建立连接,因为目标计算机拒绝连接。UnityEngine.DebugLogHandler:LogFormat (UnityEngine.LogType,UnityEngine.Object,string,object[])
MyLogHandler:LogFormat (UnityEngine.LogType,UnityEngine.Object,string,object[])(位于 Assets/Scripts/Logging/MyLogHandler.cs:29)
UnityEngine.Logger:LogError(字符串,对象)
MyApp.Logging.MyLogger:DoLog (MyApp.Logging.DebugMode,System.Action12<string, object>,string,object,object[]) (at Assets/Scripts/Logging/MyLogger.cs:66) MyApp.Logging.MyLogger:LogError (MyApp.Logging.DebugMode,object,object[]) (at Assets/Scripts/Logging/MyLogger.cs:33) SignalRClient/<StartConnection>d__4:MoveNext () (at Assets/Scripts/Clients/SignalRClient.cs:78) System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetException (System.Exception) Microsoft.AspNetCore.SignalR.Client.HubConnection/<StartAsync>d__48:MoveNext () System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetException (System.Exception) Microsoft.AspNetCore.SignalR.Client.HubConnection/<StartAsyncInner>d__49:MoveNext () System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetException (System.Exception) Microsoft.AspNetCore.SignalR.Client.HubConnection/<StartAsyncCore>d__58:MoveNext () System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder
:SetException (System.Exception)
Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionFactory/d__3:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetException(System.Exception)
Microsoft.AspNetCore.Http.Connections.Client.HttpConnection/d__40:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetException(System.Exception)
Microsoft.AspNetCore.Http.Connections.Client.HttpConnection/d__41:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetException(System.Exception) Microsoft.AspNetCore.Http.Connections.Client.HttpConnection/d__44:MoveNext () System.Runtime.CompilerServices.AsyncTaskMethodBuilder11<Microsoft.AspNetCore.Http.Connections.NegotiationResponse>:SetException (System.Exception) Microsoft.AspNetCore.Http.Connections.Client.HttpConnection/<GetNegotiationResponseAsync>d__52:MoveNext () System.Runtime.CompilerServices.AsyncTaskMethodBuilder
:SetException(System.Exception) Microsoft.AspNetCore.Http.Connections.Client.HttpConnection/d__45:MoveNext () System.Runtime.CompilerServices.AsyncTaskMethodBuilder 11<System.Net.Http.HttpResponseMessage>:SetException (System.Exception) Microsoft.AspNetCore.Http.Connections.Client.Internal.LoggingHttpMessageHandler/<SendAsync>d__2:MoveNext () System.Runtime.CompilerServices.AsyncTaskMethodBuilder
:SetException(System.Exception) Microsoft.AspNetCore.Http.Connections.Client.Internal.AccessTokenHttpMessageHandler/d__3:MoveNext () System.Threading.Tasks.TaskFactory`1 :FromAsyncCoreLogic(System.IAsyncR
代码:
public class SignalRClient
{
private Action _connectionClosed;
public SignalRClient()
{
_connectionClosed += Restart;
_hubConnection = new HubConnectionBuilder()
.WithUrl(ConfigModel.Config.MyUri + "/hmdHub")
.WithAutomaticReconnect(new[]{ TimeSpan.FromSeconds(5) })
.Build();
_hubConnection.Reconnecting += Reconnecting;
_hubConnection.Reconnected += Reconnected;
_hubConnection.Closed += Closed;
}
public async void Start()
{
ConfigureConnection();
_ = await StartConnection();
}
public async void Restart()
=> _ = await StartConnection();
public async Task Dispose()
{
_connectionClosed -= Restart;
_hubConnection.Reconnecting -= Reconnecting;
_hubConnection.Reconnected -= Reconnected;
_hubConnection.Closed -= Closed;
await _hubConnection.StopAsync();
await _hubConnection.DisposeAsync();
}
private void ConfigureConnection()
{
_hubConnection.On<Alarm>("CreateAlarm", (alarmObject) =>
{
MyLogger.Log(DebugMode.Default, "SignalRClient>CreateAlarm", $"Got new alarm with id: {alarmObject.Id} with message: {alarmObject.Message}");
});
}
private async Task<bool> StartConnection()
{
_tokenSource = new CancellationTokenSource();
while (true)
{
try
{
await _hubConnection.StartAsync(_tokenSource.Token);
MyLogger.LogSuccess(DebugMode.Default, this, "Connected to SignalR-Hub");
return true;
}
catch when (_tokenSource.IsCancellationRequested)
{
return false;
}
catch(Exception ex)
{
MyLogger.LogError(DebugMode.Default, this, ex.ToString());
await Task.Delay(5000);
}
}
}
private Task Reconnecting(Exception arg)
{
MyLogger.Log(DebugMode.Default, this, "Attempting to reconnect...");
return Task.CompletedTask;
}
private Task Reconnected(string arg)
{
MyLogger.LogSuccess(DebugMode.Default, this, "Connection restored.");
return Task.CompletedTask;
}
private Task Closed(Exception arg)
{
MyLogger.LogWarning(DebugMode.Default, this, "Connection closed.");
_connectionClosed?.Invoke();
return Task.CompletedTask;
}
private HubConnection _hubConnection;
private CancellationTokenSource _tokenSource;
}
方法
WithAutomaticReconnect
可以采用 TimeSpan
数组,或 RetryPolicy
。
如果您使用
WithAutomaticReconnect
,则手动调用 StartConnection
方法是没有意义的。 SignalR 将为您处理重新连接,尝试与数组中的对象一样多的次数(如果您不想要无限循环),或者通过使用自定义 RetryPolicy 无限次尝试。
您可以创建自定义 IRetryPolicy 来实现这一目标。