You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter Socket监听数据丢失问题:如何实现无损接收完整数据?

Socket数据接收不完整的问题分析与修复

问题根源

TCP Socket是流式传输协议,数据会被操作系统拆分成多个TCP分片发送,socket.listen的回调会被多次触发,每次只返回一部分数据。你的代码每次收到分片就直接用message = serverResponse覆盖原有内容,完全没有拼接之前的分片,自然只能拿到最后一段数据。

修复方案

需要维护一个缓冲区来累计所有收到的分片数据,直到根据通信协议判断出完整的消息后再处理。以下是具体实现(以换行符\n作为完整消息的结束标志为例,你可以根据实际协议调整判断逻辑):

// 定义全局或类级别的缓冲区,用来拼接所有收到的数据片段
final StringBuffer _messageBuffer = StringBuffer();

void listenSocket() async {
  final socket = await Socket.connect("10.0.0.3", 5000);

  if (kDebugMode) {
    print('Connected to: ${socket.remoteAddress.address}:${socket.remotePort}');
  }

  socket.listen(
    (Uint8List data) {
      final fragment = String.fromCharCodes(data);
      // 将新收到的分片追加到缓冲区
      _messageBuffer.write(fragment);

      if (kDebugMode) {
        print('Received fragment: $fragment');
        print('Current buffer content: ${_messageBuffer.toString()}');
      }

      // 根据协议判断完整消息,这里假设用换行符\n作为结束标志
      while (_messageBuffer.toString().contains('\n')) {
        final fullMessage = _messageBuffer.toString().split('\n').first;
        // 从缓冲区移除已处理的完整消息和换行符
        final remaining = _messageBuffer.toString().substring(fullMessage.length + 1);
        _messageBuffer.clear();
        _messageBuffer.write(remaining);

        if (kDebugMode) {
          print('Got full message: $fullMessage');
        }
        message = fullMessage; // 这里才是完整的消息内容
      }
    },
    onError: (error) {
      if (kDebugMode) {
        print(error);
      }
      socket.destroy();
    },
    onDone: () {
      if (kDebugMode) {
        print('Server left.');
        // 连接关闭时,检查缓冲区是否有剩余未处理的数据
        if (_messageBuffer.isNotEmpty) {
          print('Remaining data in buffer: ${_messageBuffer.toString()}');
          message = _messageBuffer.toString();
        }
      }
      socket.destroy();
    },
  );
}

关键注意事项

  • 选择合适的缓冲区:用StringBuffer比直接拼接字符串更高效,避免频繁创建字符串对象
  • 适配你的通信协议:
    • 如果是固定长度消息:累计收到的字节数达到指定长度后,取出完整数据
    • 如果是自定义分隔符:比如用||作为结束标志,就检查缓冲区是否包含该字符串
    • 如果是带长度前缀的协议:先读取前N字节作为长度值,再读取对应长度的数据
  • 处理连接关闭的边界情况:onDone回调触发时,缓冲区可能还有未处理的剩余数据,要记得处理这部分内容,避免丢失

内容的提问来源于stack exchange,提问作者jancooth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 09:23:18