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
相关产品推荐
相关产品推荐

