在 Dart 中通过套接字传输文件的更有效方法?



我正在尝试构建一个颤振应用程序,以通过本地网络将文件发送到任何设备。我正在使用套接字来发送和接收数据。传输成功,但整个过程非常耗费资源(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仍然很陌生,任何帮助将不胜感激,谢谢。

@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)中,没有相应的close方法用于openRead。到达流末尾后,文件句柄将自动关闭。在快速连续读取许多文件时,我遇到了一些问题,即使每个文件都被读取到最后一个文件之前,导致"打开的文件太多"错误。

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

最新更新