Dart中SignalR服务端协商超时TimeoutException问题求助
解决方案
1. 调整协商与请求超时时间
你的错误是协商阶段2秒超时,默认的HttpConnectionOptions.timeout为2秒,完全无法适配大量数据的协商与传输需求。需要手动设置更长的超时阈值:
final httpConnectionOptions = HttpConnectionOptions( accessTokenFactory: () => SharedPreferenceService().loginWithToken(), headers: defaultHeaders, logger: transportProtLogger, logMessageContent: true, timeout: const Duration(seconds: 30), // 延长协商超时时间 );
同时,调用invoke方法时单独设置请求超时,避免数据传输过程中超时:
await hubConnection.invoke( 'GetAllBigStuff', timeout: const Duration(minutes: 5), // 根据实际数据量调整 ).then((value) => fixation = value as List);
2. 修正KeepAlive配置时机
你当前在连接启动后设置keepAliveIntervalInMilliseconds,这不会生效——必须在hubConnection.start()之前完成配置:
final hubConnection = HubConnectionBuilder() .withUrl( 'http://10.10/home', options: httpConnectionOptions, ) .build(); // 移至start()之前配置 hubConnection.keepAliveIntervalInMilliseconds = 10 * 60 * 60 * 1000; await hubConnection.start();
3. 强制使用WebSocket传输
默认情况下SignalR会自动协商传输方式,可能切换到长轮询(对大量数据传输不友好)。直接强制使用WebSocket,跳过不必要的协商步骤:
final httpConnectionOptions = HttpConnectionOptions( accessTokenFactory: () => SharedPreferenceService().loginWithToken(), headers: defaultHeaders, logger: transportProtLogger, logMessageContent: true, timeout: const Duration(seconds: 30), transport: HttpTransportType.webSockets, // 强制使用WebSocket );
4. 分批次获取数据(核心优化)
如果数据量极大,一次性拉取极易超时或导致连接崩溃。建议后端将接口修改为分页/分批次模式,客户端循环获取:
List fixation = []; int page = 1; const pageSize = 200; bool hasMore = true; while (hasMore) { final batch = await hubConnection.invoke<List>( 'GetBigStuffByPage', args: [page, pageSize], timeout: const Duration(minutes: 2), ); if (batch == null || batch.isEmpty) { hasMore = false; } else { fixation.addAll(batch); page++; } }
5. 添加自动重连机制
在HubConnectionBuilder中启用自动重连,避免意外断开后无法恢复连接:
final hubConnection = HubConnectionBuilder() .withUrl( 'http://10.10/home', options: httpConnectionOptions, ) .withAutomaticReconnect() // 启用自动重连 .build();
修改后的完整代码示例
Future<List> fetchAllBugStuff() async { Logger.root.level = Level.ALL; Logger.root.onRecord.listen((LogRecord rec) { print('${rec.level.name}: ${rec.time}: ${rec.message}'); }); final transportProtLogger = Logger("SignalR - transport"); Logger? logger; final httpConnectionOptions = HttpConnectionOptions( accessTokenFactory: () => SharedPreferenceService().loginWithToken(), headers: defaultHeaders, logger: transportProtLogger, logMessageContent: true, timeout: const Duration(seconds: 30), transport: HttpTransportType.webSockets, ); final hubConnection = HubConnectionBuilder() .withUrl( 'http://10.10/home', options: httpConnectionOptions, ) .withAutomaticReconnect() .build(); hubConnection.keepAliveIntervalInMilliseconds = 10 * 60 * 60 * 1000; hubConnection.onclose(({error}) { Logger.root.level = Level.SHOUT; Logger.root.onRecord.listen((LogRecord rec) { print('${rec.level.name}: ${rec.error}: ${rec.message}'); }); logger?.finer(error); }); await hubConnection.start(); List fixation = []; if (hubConnection.state == HubConnectionState.Connected) { // 若后端未调整接口,使用带超时的单次请求 await hubConnection.invoke( 'GetAllBigStuff', timeout: const Duration(minutes: 5), ).then((value) => fixation = value as List); // 若后端已支持分页,替换为以下循环逻辑 // int page = 1; // const pageSize = 200; // bool hasMore = true; // while (hasMore) { // final batch = await hubConnection.invoke<List>( // 'GetBigStuffByPage', // args: [page, pageSize], // timeout: const Duration(minutes: 2), // ); // if (batch == null || batch.isEmpty) { // hasMore = false; // } else { // fixation.addAll(batch); // page++; // } // } } return fixation; }
内容的提问来源于stack exchange,提问作者user16357470
相关产品推荐
相关产品推荐

