flutter开发TCP客户端时,收不到服务端发送的数据

问题:flutter开发TCP客户端时,收不到服务端发送的数据,主要表现为 if (buff.length >= totalLen) 这条判断无法执行,导致运行不到 onData!(this, cmd);所以,客户端收不到服务端的数据。而且我看网上接收socket消息时都是这么处理的,所以不知道怎么处理了(如果有flutter开发TCP的源码可以提供就更感谢了,最好是源码)

部分代码:

  _onData(Uint8List event) async {
    debugPrint('----------> $event  type=${event.runtimeType}');
    // buff = event;
    buff += event;
    print("TCP客户端接收到的数据是:${buff}");
    debugPrint('当前的缓冲区长度为: ${buff.length}');

    while (buff.length > 2) {
      int packLen = 0;
      List<int> lenBuf = buff.sublist(0, 2);
      Uint8List tmp = Uint8List.fromList(lenBuf);
      ByteData tmpByteData = ByteData.sublistView(tmp);
      packLen = tmpByteData.getUint16(0);

      debugPrint('lenBuf长度: $lenBuf');
      debugPrint('packLen长度: $packLen');

      var totalLen = packLen + 2;
      debugPrint('totalLen长度: $totalLen');

      if (buff.length >= totalLen) {
        List<int> curFrame = buff.sublist(2, totalLen);
        List<int> packed = buff.sublist(2, 2 + packLen);

        debugPrint("数据满足条件 ${buff.length}");
        buff.replaceRange(0, totalLen, []);

        debugPrint("json解码数据长度 ${packed.length}");
        var decoded = utf8.decode(packed);
        String cmd = jsonDecode(decoded);
        debugPrint("json------------>  data= $cmd");
        onData!(this, cmd);

        debugPrint("数据剩余长度 ${buff.length}");
      } else {
        debugPrint("数据长度不足 ${buff.length}");
        break;
      }
    }
  }

debug页面:
I/flutter (24579): ----------> [49, 50, 49, 50, 49, 50, 49, 50] type=Uint8List
I/flutter (24579): TCP客户端接收到的数据是:[49, 50, 49, 50, 49, 50, 49, 50, 49, 50, 49, 50, 49, 50, 49, 50]
I/flutter (24579): 当前的缓冲区长度为: 16
I/flutter (24579): lenBuf长度: [49, 50]
I/flutter (24579): packLen长度: 12594
I/flutter (24579): totalLen长度: 12596
I/flutter (24579): 数据长度不足 16


如果取消判断限制,则提示:
E/flutter (24579): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: RangeError (end): Invalid value: Not in inclusive range 2..8: 12596
E/flutter (24579): #0 RangeError.checkValidRange (dart:core/errors.dart:363:9)
E/flutter (24579): #1 List.sublist (dart:core-patch/growable_array.dart:84:38)
E/flutter (24579): #2 SocketClient._onData (package:tcpsocket/tcpsocket.dart:170:35)
E/flutter (24579): #3 SocketClient.connect.. (package:tcpsocket/tcpsocket.dart:89:9)


如果持续累加,知道判断条件满足,同样会报上述错误


编译环境:Android studio

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
有以下几种可能导致接收不到服务端发送的数据:

  1. TCP连接是否建立成功

在客户端连接服务端之前需要检查TCP连接是否建立成功,如果连接失败则无法接收服务端发送的数据。

  1. 数据是否被粘包

客户端可能会一次性接收到多个数据包,这些数据会被放到缓冲区中,如果没有将数据解析出来就会导致粘包问题。

  1. 数据长度判断错误

在判断数据包长度的时候,如果判断条件不正确也会导致无法接收服务端发送的数据。

根据你提供的代码,可能存在以下问题:

  1. 可能存在粘包问题,客户端可能会一次性接收到多个数据包,这些数据会被放到缓冲区中,在处理缓冲区中的数据时需要考虑到这个问题。

  2. 对于UTF-8编码的字符串的解析需要使用dart:utf包中的utf8.decode方法,否则可能导致解析失败。

下面提供一份flutter开发TCP客户端的参考代码,仅供参考:

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

typedef OnDataCallback = void Function(Uint8List);

class TcpClient {
  String host;
  int port;
  Socket? _socket;
  OnDataCallback? onData;

  TcpClient({
    required this.host,
    required this.port,
    this.onData,
  });

  Future<void> connect() async {
    try {
      _socket = await Socket.connect(
        host,
        port,
      );
      _socket!.listen(_receive);

      print('Connected to: ${_socket!.remoteAddress.address}:${_socket!.remotePort}');
    } on IOException catch (e) {
      print('Error: ${e.toString()}');
    }
  }

  void _receive(Uint8List event) {
    if (onData != null) {
      onData!(event);
    }
  }

  void send(List<int> data) {
    if (_socket != null && _socket!.write(data) == null) {
      print('Error sending data');
    }
  }

  void close() {
    if (_socket != null) {
      _socket!.close();
    }
  }
}

在使用该代码进行测试时,可以使用如下方式测试:

final client = TcpClient(
  host: '192.168.0.1',
  port: 2323,
  onData: (data) {
    final receivedMessage = Utf8Decoder().convert(data);
    print('Received: $receivedMessage');
  },
);

await client.connect();

const message = 'Hello, world!\r\n';
client.send(Utf8Encoder().convert(message));

// Close the connection after 5 seconds
await Future.delayed(Duration(seconds: 5));
client.close();

当然,以上代码仅仅是一份参考代码,实际上还需要根据具体情况进行异常处理、数据解析等等。如果你还有疑问,可以提出具体问题,我将在我的能力范围内为你解答。
如果我的回答解决了您的问题,请采纳!

小魔女参考了bing和GPT部分内容调写:
解决方案:
在flutter开发TCP客户端时,如果收不到服务端发送的数据,主要是由于bufflength >= totalLen这条判断无法执行,导致运行不到onData(this, cmd);,从而导致客户端收不到服务端的数据。

可以尝试以下方法来解决这个问题:

1、检查服务端的发送数据是否正确,确认服务端发送的数据是否符合协议格式,以及是否有丢包现象。

2、检查客户端的接收逻辑是否正确,确认接收到的数据是否符合协议格式,以及是否有丢包现象。

3、检查客户端的接收逻辑是否正确,确认bufflength >= totalLen这条判断是否正确,以及是否有丢包现象。

4、检查客户端的接收逻辑是否正确,确认onData(this, cmd);这条语句是否正确,以及是否有丢包现象。

5、检查客户端的接收逻辑是否正确,确认packLen是否正确,以及是否有丢包现象。

6、检查客户端的接收逻辑是否正确,确认lenBuf是否正确,以及是否有丢包现象。

7、检查客户端的接收逻辑是否正确,确认tmp是否正确,以及是否有丢包现象。

8、检查客户端的接收逻辑是否正确,确认tmpByteData是否正确,以及是否有丢包现象。

9、检查客户端的接收逻辑是否正确,确认buff是否正确,以及是否有丢包现象。

10、检查客户端的接收逻辑是否正确,确认while循环是否正确,以及是否有丢包现象。

以上就是解决flutter开发TCP客户端时,收不到服务端发送的数据的可能的解决方案,希望能够帮助到大家。
回答不易,记得采纳呀。

“Devil组”引证GPT后的撰写:
根据提供的代码和问题描述,可能存在以下问题:

  • 数据长度问题:根据日志,数据长度是 16,而 packLen 的值是 12594,totalLen 的值是 12596。这似乎是数据长度计算错误导致的。可能需要检查发送数据的格式和内容,确保数据长度的计算是正确的。
  • 数据处理问题:即使数据长度计算正确,也可能存在数据处理问题。在您的代码中,当 buff.length >= totalLen 时,才会处理数据。但是,如果数据包长度不足 totalLen,则不会处理数据,而是跳出循环。这可能会导致部分数据被丢失,因此建议在跳出循环之前,先将未处理的数据存储到另一个缓冲区中,以便下一次数据到来时能够继续处理。
  • 字符编码问题:代码中使用了 utf8.decode() 将字节流解码为字符串,这可能会引起字符编码问题。如果服务端发送的数据采用了其他编码格式,解码时可能会出现错误。因此,建议在解码之前,先确认数据采用了什么编码格式,然后使用相应的编码方式进行解码。
  • 在处理 TCP 数据时,建议将数据处理逻辑分解为多个方法,以便更好地管理和测试代码。

以下是一个示例代码,可以作为参考:

class SocketClient {
  Socket? _socket;
  Uint8List _buffer = Uint8List(0);

  // 初始化 socket 连接
  Future<void> connect(String host, int port) async {
    _socket = await Socket.connect(host, port);
    _socket!.listen(_onData);
  }

  // 发送数据
  Future<void> send(String data) async {
    if (_socket == null) return;

    var encoded = utf8.encode(data);
    var len = encoded.length + 2;
    var header = ByteData(2);
    header.setUint16(0, len);
    var packet = Uint8List.fromList(header.buffer.asUint8List() + encoded);
    await _socket!.add(packet);
  }

  // 处理数据
  void _onData(Uint8List data) {
    _buffer += data;

    while (_buffer.length >= 2) {
      var header = ByteData.sublistView(_buffer.sublist(0, 2));
      var len = header.getUint16(0);
      var totalLen = len + 2;

      if (_buffer.length < totalLen) break;

      var packet = utf8.decode(_buffer.sublist(2, totalLen));
      _buffer = _buffer.sublist(totalLen);

      _handlePacket(packet);
    }
  }

  // 处理单个数据包
  void _handlePacket(String packet) {
    // TODO: 处理数据包
  }
}


将数据处理逻辑分解为 connect、send、_onData、_handlePacket 四个方法。

该回答引用ChatGPT

根据您提供的代码和错误信息,可以看出问题出现在数据包的长度计算上。具体来说,服务端发送的数据包长度包括了2个字节的包头长度信息和数据内容的长度,但是客户端在解析数据包时只考虑了数据内容的长度而忽略了包头的长度信息。

在代码中,以下语句用于读取包头信息并计算数据包总长度:


List<int> lenBuf = buff.sublist(0, 2);
Uint8List tmp = Uint8List.fromList(lenBuf);
ByteData tmpByteData = ByteData.sublistView(tmp);
packLen = tmpByteData.getUint16(0);
var totalLen = packLen + 2;

这里读取了数据包前2个字节,并将其转换为16位的整数值packLen,然后用packLen加上2计算数据包的总长度totalLen。然而,这个计算忽略了包头的2个字节长度,因此实际上数据包的总长度应该再加上2,即:


var totalLen = packLen + 2 + 2; // 包头长度为2个字节

修改这个计算后,您的代码就应该能正确地处理服务端发送的数据包了。如果仍然无法接收到数据,请确认服务端发送的数据包是否按照约定的格式发送,并尝试在客户端打印更多的调试信息以便进一步排查问题。

参考GPT和自己的思路,根据代码和调试信息,您的问题似乎是在接收TCP数据时无法正确处理分包情况,导致数据无法正确解析。具体来说,您使用了一个循环来不断读取 TCP 数据并检查数据长度是否足够,但是在某些情况下,循环无法判断出数据是否足够,导致数据解析错误或者无法处理。

为了解决这个问题,您可以考虑使用一个缓冲区来暂存接收到的 TCP 数据,然后根据 TCP 数据包的协议规定来解析出完整的数据包。具体来说,您可以使用以下步骤来处理 TCP 数据:

1 创建一个缓冲区,用于暂存接收到的 TCP 数据。

2 在每次接收到 TCP 数据时,将数据添加到缓冲区的末尾。

3 判断缓冲区中是否包含完整的数据包,如果是,则从缓冲区中读取出完整的数据包,并将剩余的数据保留在缓冲区中。

4 如果缓冲区中不包含完整的数据包,则继续等待下一个 TCP 数据包的到来,并将其添加到缓冲区末尾。

下面是一个示例代码,用于演示如何使用缓冲区来处理 TCP 数据:

import 'dart:convert';
import 'dart:io';

class TcpClient {
  late Socket _socket;
  final String _host;
  final int _port;
  final int _maxPacketSize;

  List<int> _buffer = [];

  TcpClient(this._host, this._port, {int maxPacketSize = 1024})
      : _maxPacketSize = maxPacketSize;

  Future<void> connect() async {
    _socket = await Socket.connect(_host, _port);
    _socket.listen(_onData, onError: _onError, onDone: _onDone);
  }

  Future<void> send(String message) async {
    final encoded = utf8.encode(message);
    final length = encoded.length + 2;
    final packet = Uint8List(length);
    final byteData = ByteData.view(packet.buffer);
    byteData.setUint16(0, encoded.length);
    packet.setRange(2, length, encoded);
    _socket.add(packet);
  }

  void _onData(List<int> data) {
    _buffer.addAll(data);
    while (_buffer.length > 2) {
      final packetLength =
          ByteData.view(Uint8List.fromList(_buffer.sublist(0, 2)).buffer)
              .getUint16(0);
      final totalLength = packetLength + 2;
      if (_buffer.length >= totalLength) {
        final packetData = _buffer.sublist(2, totalLength);
        _buffer.removeRange(0, totalLength);
        final message = utf8.decode(packetData);
        _onMessageReceived(message);
      } else {
        break;
      }
    }
  }

  void _onError(error, StackTrace stackTrace) {
    print('Socket error: $error');
  }

  void _onDone() {
    print('Socket done');
  }

  void _onMessageReceived(String message) {
    print('Received message: $message');
}

void main() async {
final client = TcpClient('localhost', 12345);

await client.connect();
await client.send('Hello, server!');
}

题主,你这儿好好看下:

img