Xamarin Android弱4G网络下SignalR连接异常:StartAsync无响应或网络不可达
Xamarin Android弱4G环境下SignalR重连失败问题排查与解决方案
问题描述
我们开发的Xamarin Android应用通过前台服务维持SignalR长连接,在弱4G网络环境下断开后无法重新建立连接:
- 调用
HubConnection.StartAsync时有时无返回结果 - 有时抛出“网络不可达”的
HttpRequestException
但同一时段内其他应用及本应用内的API调用均能正常访问服务器,仅SignalR连接失败,目前只能通过开关4G恢复。
连接创建代码
public static async Task<SignalrConnection> NewConnection(string token, CancellationToken cancellationToken) { try { var url = $"{ConfigService.TRANSFER_URL}"; var connection = new HubConnectionBuilder() .WithUrl(url, opts => { opts.Headers.Add("Authorization", $"Bearer {token}"); }) .AddNewtonsoftJsonProtocol() .Build(); await connection.StartAsync(cancellationToken); var sc = new SignalrConnection(connection); return sc; } catch (HttpRequestException ex1) { if (ex1.Message.Contains("401")) { throw new NotAuthorizeException(); } else throw; } catch (Exception) { throw; } }
捕获到的异常信息
异常1:网络不可达
System.Net.Http.HttpRequestException 消息:网络不可达 StackTrace : at System.Net.Http.ConnectHelper.ConnectAsync (System.String host, System.Int32 port, System.Threading.CancellationToken cancellationToken) [0x001ac] in <7fd014203845408d8ac08616d88f8f38>:0 at System.Net.Http.HttpConnectionPool.CreateConnectionAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x00134] in <7fd014203845408d8ac08616d88f8f38>:0 at System.Net.Http.HttpConnectionPool.WaitForCreatedConnectionAsync (System.Threading.Tasks.ValueTask`1[TResult] creationTask) [0x000a2] in <7fd014203845408d8ac08616d88f8f38>:0 at System.Net.Http.HttpConnectionPool.SendWithRetryAsync (System.Net.Http.HttpRequestMessage request, System.Boolean doRequestAuth, System.Threading.CancellationToken cancellationToken) [0x00089] in <7fd014203845408d8ac08616d88f8f38>:0 at System.Net.Http.RedirectHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x000ba] in <7fd014203845408d8ac08616d88f8f38>:0 at Microsoft.AspNetCore.Http.Connections.Client.Internal.AccessTokenHttpMessageHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x00163] in <d5c96e7f095e4a9ba210d5848b0716e1>:0 at Microsoft.AspNetCore.Http.Connections.Client.Internal.LoggingHttpMessageHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x0009f] in <d5c96e7f095e4a9ba210d5848b0716e1>:0 at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered (System.Threading.Tasks.Task`1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) [0x000b3] in <7fd014203845408d8ac08616d88f8f38>:0 at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.NegotiateAsync (System.Uri url, System.Net.Http.HttpClient httpClient, Microsoft.Extensions.Logging.ILogger logger, System.Threading.CancellationToken cancellationToken) [0x00284] in <d5c96e7f095e4a9ba210d5848b0716e1>:0 at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.GetNegotiationResponseAsync (System.Uri uri, System.Threading.CancellationToken cancellationToken) [0x0008a] in <d5c96e7f095e4a9ba210d5848b0716e1>:0 at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.SelectAndStartTransport (Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken) [0x0018e] in <d5c96e7f095e4a9ba210d5848b0716e1>:0 at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.StartAsyncCore (Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken) [0x00139] in <d5c96e7f095e4a9ba210d5848b0716e1>:0 at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.StartAsync (Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken) [0x00092] in <d5c96e7f095e4a9ba210d5848b0716e1>:0 at Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionFactory.ConnectAsync (System.Net.EndPoint endPoint, System.Threading.CancellationToken cancellationToken) [0x0011e] in <d5c96e7f095e4a9ba210d5848b0716e1>:0 at Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionFactory.ConnectAsync (System.Net.EndPoint endPoint, System.Threading.CancellationToken cancellationToken) [0x001d3] in <d5c96e7f095e4a9ba210d5848b0716e1>:0 at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsyncCore (System.Threading.CancellationToken cancellationToken) [0x000b3] in <d3cc4f3426ea47de89566d0665431af2>:0 at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsyncInner (System.Threading.CancellationToken cancellationToken) [0x001b6] in <d3cc4f3426ea47de89566d0665431af2>:0 at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsync (System.Threading.CancellationToken cancellationToken) [0x00092] in <d3cc4f3426ea47de89566d0665431af2>:0
异常2:WebSocket连接失败
消息:WebSockets连接失败:无法连接到远程服务器 StackTrace : System.Net.WebSockets.WebSocketException 消息:无法连接到远程服务器 StackTrace : at System.Net.WebSockets.WebSocketHandle.ConnectAsyncCore (System.Uri uri, System.Threading.CancellationToken cancellationToken, System.Net.WebSockets.ClientWebSocketOptions options) [0x00385] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs:150 at System.Net.WebSockets.ClientWebSocket.ConnectAsyncCore (System.Uri uri, System.Threading.CancellationToken cancellationToken) [0x000d1] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/ClientWebSocket.cs:157 at Microsoft.AspNetCore.Http.Connections.Client.Internal.WebSocketsTransport.DefaultWebSocketFactory (Microsoft.AspNetCore.Http.Connections.Client.WebSocketConnectionContext context, System.Threading.CancellationToken cancellationToken) [0x003da] in <d5c96e7f095e4a9ba210d5848b0716e1>:0 at System.Threading.Tasks.ValueTask`1[TResult].get_Result () [0x0001b] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/ValueTask.cs:813 at Microsoft.AspNetCore.Http.Connections.Client.Internal.WebSocketsTransport.StartAsync (System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken) [0x00123] in <d5c96e7f095e4a9ba210d5848b0716e1>:0 at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.StartTransport (System.Uri connectUrl, Microsoft.AspNetCore.Http.Connections.HttpTransportType transportType, Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken) [0x000c5] in <d5c96e7f095e4a9ba210d5848b0716e1>:0 at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.SelectAndStartTransport (Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken) [0x00531] in <d5c96e7f095e4a9ba210d5848b0716e1>:0 System.OperationCanceledException 消息:操作已取消 StackTrace : at System.Net.WebSockets.WebSocketHandle.ConnectSocketAsync (System.String host, System.Int32 port, System.Threading.CancellationToken cancellationToken) [0x001ec] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs:187 at System.Net.WebSockets.WebSocketHandle.ConnectSocketAsync (System.String host, System.Int32 port, System.Threading.CancellationToken cancellationToken) [0x002b5] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs:202 at System.Net.WebSockets.WebSocketHandle.ConnectAsyncCore (System.Uri uri, System.Threading.CancellationToken cancellationToken, System.Net.WebSockets.ClientWebSocketOptions options) [0x0008d] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs:97 System.ObjectDisposedException 消息:无法访问已释放的对象。 对象名称: 'System.Net.Sockets.Socket'. StackTrace : at System.Net.Sockets.Socket.ThrowIfDisposedAndClosed () [0x00010] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/mcs/class/System/System.Net.Sockets/Socket.cs:2896 at System.Net.Sockets.Socket.EndConnect (System.IAsyncResult asyncResult) [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/mcs/class/System/System.Net.Sockets/Socket.cs:1207 at System.Net.Sockets.Socket+<>c.<ConnectAsync>b__10_0 (System.IAsyncResult iar) [0x0000c] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs:141 --- End of stack trace from previous location where exception was thrown --- at System.Net.WebSockets.WebSocketHandle.ConnectSocketAsync (System.String host, System.Int32 port, System.Threading.CancellationToken cancellationToken) [0x0016c] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs:178
解决方案建议
1. 配置传输方式降级策略
SignalR默认优先使用WebSocket,但弱网下WebSocket连接稳定性差。指定多种传输方式,让客户端自动降级到更适合弱网的方式:
var connection = new HubConnectionBuilder() .WithUrl(url, opts => { opts.Headers.Add("Authorization", $"Bearer {token}"); // 按优先级设置传输方式:WebSocket → Server-Sent Events → 长轮询 opts.Transports = HttpTransportType.WebSockets | HttpTransportType.ServerSentEvents | HttpTransportType.LongPolling; }) .AddNewtonsoftJsonProtocol() .Build();
2. 添加重连策略与超时控制
- 使用内置自动重连机制,避免手动重连的复杂性
- 为
StartAsync添加超时,防止无限等待
var connection = new HubConnectionBuilder() .WithUrl(url, opts => { opts.Headers.Add("Authorization", $"Bearer {token}"); opts.Transports = HttpTransportType.WebSockets | HttpTransportType.ServerSentEvents | HttpTransportType.LongPolling; }) .AddNewtonsoftJsonProtocol() // 配置自动重连间隔:立即重试→2秒→5秒→10秒 .WithAutomaticReconnect(new[] { TimeSpan.Zero, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10) }) .Build(); // 为连接启动添加超时控制 using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); try { await connection.StartAsync(cts.Token); } catch (OperationCanceledException) { // 超时后触发重试逻辑 }
3. 清理旧连接资源
异常栈中出现ObjectDisposedException,说明旧连接资源未正确释放。重连前需确保旧连接已销毁:
// 重连前清理旧连接 if (oldConnection != null) { try { await oldConnection.StopAsync(); } catch { } oldConnection.Dispose(); } // 创建新连接 var newConnection = await NewConnection(token, cancellationToken);
4. 监听Android网络状态
注册网络状态回调,当网络恢复时主动触发重连,减少无效重试:
- 使用
ConnectivityManager.NetworkCallback监听网络连接状态变化 - 当网络从断开变为可用时,执行SignalR重连逻辑
5. 自定义HttpClient配置
弱网下调整HttpClient连接池参数,优化连接复用:
var httpClient = new HttpClient(new HttpClientHandler { MaxConnectionsPerServer = 10, AllowAutoRedirect = true, UseCookies = false }); var connection = new HubConnectionBuilder() .WithUrl(url, opts => { opts.Headers.Add("Authorization", $"Bearer {token}"); opts.HttpClient = httpClient; opts.Transports = HttpTransportType.WebSockets | HttpTransportType.ServerSentEvents | HttpTransportType.LongPolling; }) .AddNewtonsoftJsonProtocol() .Build();
6. 升级依赖版本
当前使用的Mono版本较旧(2020-02),可能存在WebSocket连接的已知bug。升级SignalR客户端包到最新稳定版,同时更新Xamarin.Android环境,修复底层网络库问题。
内容的提问来源于stack exchange,提问作者Pravin Patil
相关产品推荐
相关产品推荐

