Flutter中TCP连接接收数据返回遇Future已完成错误的解决咨询
问题分析
你遇到的"Future already completed"错误,核心原因是**Completer被多次调用complete方法**。结合你的控制台输出,触发场景主要有两种:
- 设备返回的数据被TCP拆分为多个数据包,
socket.listen的回调被多次执行,每次都尝试完成已完成的completer - 虽然socket已收到数据并完成
completer,但后续的onDone或onError回调仍尝试调用completer.complete
另外控制台同时输出设备数据和Timeout,说明超时逻辑与socket数据接收存在时序冲突——不必要的延迟可能导致命令发送时机过晚,超时倒计时提前启动,进而引发后续异常。
修复后的代码
Future<String?> sendAndReceiveCommand(String value) async { Socket? socket; StreamSubscription? subscription; final completer = Completer<String>(); StringBuffer answerBuffer = StringBuffer(); try { // 解析目标IP地址 final ipParts = '192.168.4.1'.split('.').map((part) => int.parse(part)).toList(); final ipAddress = InternetAddress.fromRawAddress(Uint8List.fromList(ipParts)); // 建立TCP连接 socket = await Socket.connect(ipAddress, 80, timeout: const Duration(seconds: 10)); socket.setOption(SocketOption.tcpNoDelay, true); // 监听socket数据流 subscription = socket.listen( (data) { final chunk = ascii.decode(data); answerBuffer.write(chunk); print('Received chunk: $chunk'); // 根据设备返回数据的结束标识判断接收完成(示例为03d结尾) if (chunk.endsWith('03d')) { if (!completer.isCompleted) { completer.complete(answerBuffer.toString()); // 完成后立即取消监听并销毁socket,阻断后续回调 subscription?.cancel(); socket?.destroy(); } } }, onDone: () { if (!completer.isCompleted) { completer.complete(answerBuffer.isEmpty ? 'Connection closed' : answerBuffer.toString()); } socket?.destroy(); }, onError: (e) { if (!completer.isCompleted) { completer.completeError(e); } socket?.destroy(); }, ); // 发送指令(移除不必要的延迟,除非设备明确要求) final command = 'Value:02d11d61d03d*'; socket.add(ascii.encode(command)); await socket.flush(); // 等待结果或触发超时 final response = await completer.future.timeout( const Duration(seconds: 5), onTimeout: () { subscription?.cancel(); socket?.destroy(); return 'Timeout'; }, ); return response; } catch (e) { // 异常分支统一清理资源 subscription?.cancel(); socket?.destroy(); return 'Error: ${e.toString()}'; } }
关键修复点
- 杜绝重复完成Completer:每次调用
completer.complete前检查isCompleted状态,完成后立即取消socket监听、销毁连接,阻断后续回调触发 - 处理TCP分块数据:用
StringBuffer拼接所有收到的数据包,避免单次数据覆盖导致内容不完整 - 明确接收结束条件:根据设备返回数据的特征(如示例中的
03d结尾)判断接收完成,而非收到第一块数据就结束 - 全分支资源清理:在异常、超时、正常完成的所有分支中,都执行取消监听、销毁socket操作,避免资源泄漏
- 移除冗余延迟:删除不必要的
await Future.delayed,避免超时逻辑提前触发引发冲突
内容的提问来源于stack exchange,提问作者Yekta Mirkan
相关产品推荐
相关产品推荐

