【问题标题】:A more efficient way to transfer files over a socket in Dart?在 Dart 中通过套接字传输文件的更有效方法?
【发布时间】:2021-11-19 05:19:39
【问题描述】:

我正在尝试构建一个颤振应用程序以通过我的本地网络将文件发送到任何设备。我正在使用套接字发送和接收数据。传输成功,但整个过程非常耗费资源(RAM)(我相信是因为 Uint8List 和在服务器端和客户端创建的构建器。)

因此,我只能发送大小为 100-200 MB 的文件而不会耗尽内存。 我想知道是否可以通过优化代码或任何其他文件传输方法来更有效地发送数据。

服务器端代码:

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

void main() async {
  final server = await ServerSocket.bind('localhost', 2714);
  server.listen((client) {
    handleClient(client);
  });
}

void handleClient(Socket client) async {
  File file = File('1.zip');
  Uint8List bytes = await file.readAsBytesSync();
  print("Connection from:"
      "${client.remoteAddress.address}:${client.remotePort}");
  client.listen((Uint8List data) async {
    await Future.delayed(Duration(seconds: 1));
    final request = String.fromCharCodes(data);
    if (request == 'Send Data') {
      client.add(bytes);
    }
    client.close();
  });
}

客户端代码:

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

int size = 0;
void main() async {
  final socket = await Socket.connect('localhost', 2714);
  print("Connected to:" '${socket.remoteAddress.address}:${socket.remotePort}');
  socket.write('Send Data');
  await socket.listen((Uint8List data) async {
    await Future.delayed(Duration(seconds: 1));
    dataHandler(data);
    size = size + data.lengthInBytes;
    print(size);
    print("ok: data written");
  });
  await Future.delayed(Duration(seconds: 20));
  socket.close();
  socket.destroy();
}

BytesBuilder builder = new BytesBuilder(copy: false);
void dataHandler(Uint8List data) {
  builder.add(data);

  if (builder.length <= 1049497288) {
    // file size
    Uint8List dt = builder.toBytes();

    writeToFile(
        dt.buffer.asUint8List(0, dt.buffer.lengthInBytes), '1(recieved).zip');
  }
}

Future<void> writeToFile(Uint8List data, String path) {
  // final buffer = data.buffer;
  return new File(path).writeAsBytes(data);
}

我对 Dart 和 Flutter 还是很陌生,任何帮助将不胜感激,谢谢。

【问题讨论】:

  • await File('1.zip').openRead().pipe(socket); print('file copied');
  • 非常感谢@pskink,Diego 的解决方案完全解决了这个问题。

标签: flutter dart


【解决方案1】:

@pskink 为您提供了大部分答案,但您遇到问题是对的,因为您正在将整个文件读入服务器端的内存,然后在写入之前将所有接收到的字节累积到内存中客户端。

您可以将数据从磁盘流式传输到服务器端的套接字,并从套接字流式传输到客户端的磁盘。完整代码如下:

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

void main() async {
  final server = await ServerSocket.bind('localhost', 2714);
  server.listen((client) async {
      await File('1.zip').openRead().pipe(client);
  });
}
import 'dart:io';
import 'dart:typed_data';

void main() async {
  var socket = await Socket.connect('localhost', 2714);
  try {
    print(
        "Connected to:" '${socket.remoteAddress.address}:${socket.remotePort}');
    socket.write('Send Data');

    var file = File('1_received.zip').openWrite();
    try {
      await socket.map(toIntList).pipe(file);
    } finally {
      file.close();
    }
  } finally {
    socket.destroy();
  }
}

List<int> toIntList(Uint8List source) {
  return List.from(source);
}

File.openReadFile.openWrite 和套接字都是异步流。详情请见https://dart.dev/tutorials/language/streams

pipe 调用隐藏了这里的所有工作,但在其他情况下,您可以使用带有 async for (var element in stream) 语句的流。

这里要记住一件事,在await File('1.zip').openRead().pipe(client) 中,openRead 没有对应的close 方法。一旦到达流的末尾,文件句柄就会自动关闭。我在快速连续读取多个文件时遇到了一些问题,即使每个文件都在打开下一个文件之前读取到最后,导致“打开的文件过多”错误。

我没有进一步调查,因为我正在读取小文件并切换到使用 readAsBytesSync,但有可能句柄仅在 GC 期间关闭,或者有其他原因导致它们没有立即释放,这造成了限制您可以在短时间内以这种方式阅读的文件数量。

【讨论】:

  • 非常感谢,我对代码进行了一些修改,使其有点可用,但是这个解决方案完全解决了问题。
  • 文件('1.zip').openRead().pipe(client);如何检查进度..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-19
相关资源
最近更新 更多