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

Flutter WebSocket多数据发送时数据流混叠问题求助

解决WebSocket连续发送数据混叠的简单方案

你的核心问题是连续发送的多条消息在接收端被合并,导致无法区分。之前用ASCII空字符没解决,大概率是因为空字符可能出现在消息内容里,或者接收端的拆分逻辑没处理好。下面给几个简单高效的解决办法:

方案1:用JSON序列化每条消息(最易实现)

把每条消息封装成独立的JSON对象,再用换行符分隔(确保每条JSON单独占一行),接收端按行拆分后逐个解析JSON,就能精准区分不同类型的消息。

发送代码修改:

import 'dart:convert';

void sendData(CurrentUser user) {
  // 发送用户名称
  socket?.writeln(jsonEncode({
    'type': 'name',
    'value': user.name
  }));
  
  // 发送对象数量
  socket?.writeln(jsonEncode({
    'type': 'amount',
    'value': user.objects.length
  }));
  
  // 发送每个对象信息
  for(var object in user.objects) {
    socket?.writeln(jsonEncode({
      'type': 'object',
      'value': 'ObjectInfoHere' // 替换成你的对象实际数据
    }));
  }
}

接收端处理逻辑示例:

socket?.listen((data) {
  // 按换行拆分所有收到的消息
  List<String> messages = data.toString().split('\n');
  for (String msg in messages) {
    if (msg.isEmpty) continue;
    // 解析JSON
    Map<String, dynamic> parsed = jsonDecode(msg);
    switch(parsed['type']) {
      case 'name':
        // 处理名称
        break;
      case 'amount':
        // 处理数量
        break;
      case 'object':
        // 处理对象
        break;
    }
  }
});

方案2:给每条消息加长度前缀(最可靠)

先发送消息的字节长度(用固定长度的二进制格式,比如4字节大端整数),再发送消息内容。接收端先读取长度,再读取对应长度的字节,完全不会出现混叠问题。

发送代码修改:

import 'dart:convert';
import 'dart:typed_data';

void sendData(CurrentUser user) {
  // 发送名称消息
  _sendMessage(socket, 'name|${user.name}');
  
  // 发送数量消息
  _sendMessage(socket, 'amount|${user.objects.length}');
  
  // 发送每个对象消息
  for(var object in user.objects) {
    _sendMessage(socket, 'object|ObjectInfoHere');
  }
}

// 封装发送逻辑:先发长度,再发内容
void _sendMessage(WebSocket? socket, String content) {
  if (socket == null) return;
  List<int> contentBytes = utf8.encode(content);
  // 将长度转换成4字节大端格式
  Uint8List lengthBytes = Uint8List(4);
  ByteData byteData = lengthBytes.buffer.asByteData();
  byteData.setUint32(0, contentBytes.length, Endian.big);
  // 先发送长度,再发送内容
  socket.add(lengthBytes);
  socket.add(contentBytes);
}

接收端处理逻辑示例:

List<int> buffer = [];

socket?.listen((data) {
  buffer.addAll(data);
  // 循环处理缓冲区中的完整消息
  while (buffer.length >= 4) {
    // 先读取4字节的长度
    ByteData byteData = Uint8List.fromList(buffer.sublist(0,4)).buffer.asByteData();
    int msgLength = byteData.getUint32(0, Endian.big);
    // 如果缓冲区中的数据足够一条消息
    if (buffer.length >= 4 + msgLength) {
      // 提取消息内容
      List<int> msgBytes = buffer.sublist(4, 4 + msgLength);
      String msg = utf8.decode(msgBytes);
      // 处理消息(比如按|拆分类型和数据)
      List<String> parts = msg.split('|');
      switch(parts[0]) {
        case 'name':
          // 处理名称
          break;
        case 'amount':
          // 处理数量
          break;
        case 'object':
          // 处理对象
          break;
      }
      // 移除已处理的部分
      buffer = buffer.sublist(4 + msgLength);
    } else {
      // 数据不够,等待下一次接收
      break;
    }
  }
});

方案3:使用唯一分隔符(适合内容可控场景)

选一个绝对不会出现在消息内容里的字符串作为分隔符(比如###MESSAGE_END###),每条消息末尾加上这个分隔符,接收端收到数据后按分隔符拆分即可。

发送代码修改:

void sendData(CurrentUser user) {
  const separator = '###MESSAGE_END###';
  socket?.write('name|${user.name}$separator');
  socket?.write('amount|${user.objects.length}$separator');
  for(var object in user.objects) {
    socket?.write('object|ObjectInfoHere$separator');
  }
}

接收端处理逻辑示例:

String buffer = '';

socket?.listen((data) {
  buffer += data.toString();
  const separator = '###MESSAGE_END###';
  // 拆分所有完整消息
  List<String> messages = buffer.split(separator);
  // 最后一个元素可能是不完整的消息,放回缓冲区
  buffer = messages.removeLast();
  // 处理每条完整消息
  for(String msg in messages) {
    if (msg.isEmpty) continue;
    List<String> parts = msg.split('|');
    switch(parts[0]) {
      case 'name':
        // 处理名称
        break;
      case 'amount':
        // 处理数量
        break;
      case 'object':
        // 处理对象
        break;
    }
  }
});

为什么之前的空字符没用?

ASCII空字符\u0000可能出现在某些字符串内容里(比如用户名称包含特殊字符时),或者接收端的拆分逻辑没有正确处理缓冲区中连续的空字符,导致拆分失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 08:07:53