You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 22:14:53