如何用Flutter Completer串联Future实现Socket逐字节请求?
解决方案:用Completer实现逐字节发送+回显确认
你的思路是对的,用Completer可以精准控制发送节奏,避免Future.delayed的不确定性。原代码的问题主要在于Completer列表的索引管理容易混乱,以及字节的字符串比较逻辑不可靠,下面是修正后的实现方案:
核心调整点
- 不再维护Completer列表,而是每次发送单个字节时创建一个临时Completer,等待回显后完成它
- 直接比较字节数值而非字符串,避免格式差异导致的匹配失败
- 用单独变量跟踪当前等待确认的Completer,而非依赖全局索引
修正后的代码示例
Completer<bool>? _currentAckCompleter; int? _expectedByte; // 记录当前等待回显的字节值 @override void send(dynamic data) async { if (data is! List<int>) { throw ArgumentError("data must be a list of bytes"); } for (var byte in data) { final completer = Completer<bool>(); _currentAckCompleter = completer; _expectedByte = byte; try { // 发送单个字节 await _udpSocket.send([byte], ipAddress!, portNumber!); // 等待回显确认 await completer.future; } catch (e) { // 发送或确认失败,终止后续发送并抛出异常 _currentAckCompleter = null; _expectedByte = null; rethrow; } finally { // 清理当前状态 _currentAckCompleter = null; _expectedByte = null; } } } @override Future<bool> connect() async { // ... 原有的连接逻辑 ... reader.listen((Uint8List data) { // 假设回显是单个字节,取第一个字节判断 if (data.isNotEmpty && _expectedByte != null && data[0] == _expectedByte) { _currentAckCompleter?.complete(true); } // 原有的缓冲区处理逻辑保留 buffer.addAll(data); controller.sink.add(buffer.sublist(0, buffer.length - 2)); }); // ... 原有的连接成功返回逻辑 ... return true; }
额外建议
- 超时处理:如果长时间未收到回显,建议添加超时逻辑,避免无限等待:
// 在send方法的await completer.future处替换为: await Future.any([ completer.future, Future.delayed(const Duration(seconds: 5)).then((_) => throw TimeoutException("Ack timeout")) ]); - UDP可靠性优化:UDP本身无连接不可靠,若场景要求必达,建议在应用层添加重传机制,比如确认超时后重新发送当前字节
- 并发安全:如果存在多个并发
send请求,需要加锁或队列处理,避免状态混乱
内容的提问来源于stack exchange,提问作者saguado
相关产品推荐
相关产品推荐

