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

SignalR连接异常:Hub握手完成前连接断开问题求助

SignalR握手失败重试无效的问题排查与修复

问题诊断

你遇到的The underlying connection was closed before the hub handshake could complete错误,以及重试机制失效,通常由以下几个核心原因导致:

  • Access Token过期/无效:初始连接失败后,重试时复用了过期的token,服务器拒绝握手。
  • 手动重试与自动重连冲突:代码同时启用了SignalR自带的withAutomaticReconnect()和自定义的_retryConnection,两者的重试逻辑互相干扰。
  • 旧连接实例不可复用:断开后的HubConnection实例可能处于异常状态,直接调用start()无法恢复连接。
  • WebSocket协商配置不兼容:skipNegotiation: true要求服务器直接支持WebSocket连接,如果服务器需要先协商,会导致握手失败。

修复方案

1. 重试前强制刷新Token

每次重试前先刷新access token,确保请求携带有效的认证信息:

Future<void> _retryConnection() async {
  const int maxRetries = 5;
  int retryCount = 0;

  while (retryCount < maxRetries && !_isConnected) {
    try {
      // 重试前先刷新Token
      await refreshToken();
      
      Logger().i("Retrying SignalR connection: Attempt ${retryCount + 1}");
      // 重新初始化连接实例(避免复用旧实例)
      await _initSignalR();
      await _startConnection();
      _isConnected = true;
      Logger().i("SignalR reconnected successfully.");
      break;
    } catch (e) {
      Logger().e("Retry ${retryCount + 1} failed: $e");
      retryCount++;
      await Future.delayed(const Duration(seconds: 5));
    }
  }

  if (!_isConnected) {
    Logger().e("Failed to reconnect after $maxRetries attempts.");
  }
}

2. 移除冲突的自动重连逻辑

保留自定义重试机制,去掉SignalR自带的自动重连,避免逻辑冲突:

// 修改_initSignalR方法,移除withAutomaticReconnect()
_hubConnection = HubConnectionBuilder()
    .withUrl(
      "https://legalis-api.webconsole.az/hubaction",
      options: HttpConnectionOptions(
        transport: HttpTransportType.WebSockets,
        accessTokenFactory: () async {
          return await _getAccessToken(apiService) ?? '';
        },
        // 如果服务器需要协商,改为false
        skipNegotiation: false,
        logger: null,
      ),
    )
    // 移除这一行
    // .withAutomaticReconnect()
    .build();

3. 确保连接实例可复用

将_initSignalR改为可重复调用的方法,每次重试时重新创建连接实例:

Future<void> _initSignalR() async {
  // 先关闭旧连接(如果存在)
  if (_hubConnection.state != HubConnectionState.disconnected) {
    await _hubConnection.stop();
  }

  ApiService apiService = ApiService();
  JsonHubProtocol protocol = JsonHubProtocol();
  _hubConnection = HubConnectionBuilder()
      .withUrl(
        "https://legalis-api.webconsole.az/hubaction",
        options: HttpConnectionOptions(
          transport: HttpTransportType.WebSockets,
          accessTokenFactory: () async {
            return await _getAccessToken(apiService) ?? '';
          },
          skipNegotiation: false,
          logger: null,
        ),
      )
      .build();

  // 重新注册事件监听
  _hubConnection.onclose(({error}) => _onConnectionClose(error));
  _hubConnection.on("ReceiveMessage", (message) {
    Logger().i("Received: $message");
  });
}

4. 检查WebSocket协商配置

如果服务器部署在反向代理(如Nginx)后,可能需要启用协商机制,将skipNegotiation改为false,让SignalR自动选择合适的传输方式。

额外建议

  • 增加Token有效性校验:在_getAccessToken中检查token是否过期,提前触发刷新。
  • 调整重试间隔:采用指数退避策略(比如1s、3s、5s、10s),避免频繁请求服务器。
  • 增加用户提示:重试失败后,在UI上显示连接失败的提示,允许用户手动触发重试。

内容的提问来源于stack exchange,提问作者Mahammad Azimzada

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 04:44:50