无法捕获TimeoutException:Stomp WebSocket连接超时控制异常排查
问题分析与解决方案
你的问题核心在于错误处理的覆盖不完整,不管是用Completer的第一个实现,还是用async/await的第二个实现,都没有同时兼顾「超时异常」和「WebSocket自身异常」的捕获。
为什么第一个实现抓不到超时异常?
当你调用onOpenEvent.timeout(config.connectionTimeout)后,这个Future如果触发超时,会直接抛出TimeoutException,但你只给它绑定了.then()处理成功场景,没有绑定错误处理逻辑。这个未被处理的错误会直接冒泡成全局未捕获异常,而不会传递给completer.completeError()。同时,webSocket.onError的处理只覆盖了WebSocket自身的错误,完全没处理超时的情况。
为什么第二个实现抓不到WebSocket异常?
webSocket.onOpen.first只会等待连接成功的事件,当WebSocket连接失败时,错误会通过webSocket.onError流发出,而这个流的错误没有被你监听,会直接变成未捕获异常,不会被外层的try/catch捕获。
正确的实现方案(推荐用async/await)
这个方案能同时捕获超时异常和WebSocket自身的所有错误,还能避免资源泄漏:
Future<WebSocketChannel> connect(StompConfig config) async { final webSocket = WebSocket(config.url)..binaryType = BinaryType.list.value; // 把WebSocket的错误转换成Future的异常,和连接成功事件竞争 final socketErrorFuture = webSocket.onError.first.then((err) { throw WebSocketChannelException.from(err); }); final openEventFuture = webSocket.onOpen.first; // 合并两个Future,再加上超时控制 Future<void> connectFuture; if (config.connectionTimeout.inMilliseconds > 0) { connectFuture = Future.any([openEventFuture, socketErrorFuture]) .timeout(config.connectionTimeout); } else { connectFuture = Future.any([openEventFuture, socketErrorFuture]); } try { await connectFuture; return HtmlWebSocketChannel(webSocket); } catch (err) { // 连接失败时主动关闭WebSocket,避免资源泄漏 if (webSocket.readyState != WebSocket.CLOSED) { await webSocket.close(); } // 把异常重新抛出,让调用方捕获 rethrow; } }
如果坚持用Completer的实现
需要给超时的Future绑定错误处理,同时避免重复触发completer:
Future<WebSocketChannel> connect(StompConfig config) { final completer = Completer<HtmlWebSocketChannel>(); final webSocket = WebSocket(config.url)..binaryType = BinaryType.list.value; var onOpenEvent = webSocket.onOpen.first; if (config.connectionTimeout.inMilliseconds > 0) { onOpenEvent = onOpenEvent.timeout(config.connectionTimeout); } // 同时处理连接成功和超时错误 onOpenEvent.then((value) { completer.complete(HtmlWebSocketChannel(webSocket)); }).catchError((err) { if (!completer.isCompleted) { completer.completeError(err); webSocket.close(); } }); // 处理WebSocket自身的错误,确保只在completer未完成时触发 webSocket.onError.first.then((err) { if (!completer.isCompleted) { completer.completeError(WebSocketChannelException.from(err)); webSocket.close(); } }); return completer.future; }
调用方式不变
你的调用代码不需要修改,外层的try/catch现在能捕获所有类型的异常:
try { _channel = await platform.connect(config); } catch (err) { print('Caught error: $err'); }
内容的提问来源于stack exchange,提问作者puelo
相关产品推荐
相关产品推荐

