writing/reading 字节数 to/from Dart 中的套接字数据
writing/reading number of bytes to/from socket data in Dart
在下面的 server/client 套接字代码中,服务器尝试发送从 0 到 1023 的整数。客户端接收到正确数量的整数,但它们在 255 处换行。这是为什么以及正确的方法是什么?
* 服务器 *
import 'dart:async';
import 'dart:io';
main() async {
final server = await ServerSocket.bind('127.0.0.1', 4041);
server.listen((Socket socket) {
print('Got connected ${socket.remoteAddress}');
for(int i=0; i<1024; i++) {
socket.add([i]);
}
socket.close();
print('Closed ${socket.remoteAddress}');
});
}
* 客户端 *
import 'dart:async';
import 'dart:io';
main() async {
final client = await Socket.connect('127.0.0.1', 4041);
client.listen(
(var data) => print('Got $data'),
onDone: () { print('Done'); client.close(); },
onError: (e) { print('Got error $e'); client.close(); });
print('main done');
}
此外,感觉 Socket api 有点不同 - 在支持流式读取方面比其他语言更 Dartish。但是,如果您只想读取 N 字节怎么办?你如何做到这一点?我看到 take 方法 (Stream take(int count)) 但我不清楚 count 正在占用,特别是在读取了多少字节方面。因为 Socket 实现了 Stream< List < int > > 并且因为 int 并不是真正固定大小的实体(我想??),你怎么知道已经读取了多少字节?
这对我有用,我不确定这是最优雅的解决方案
import 'dart:io';
import 'dart:async';
import 'dart:typed_data';
main() async {
final server = await ServerSocket.bind('127.0.0.1', 4041);
server.listen((Socket socket) {
print('Got connected ${socket.remoteAddress}');
var toByte = new StreamTransformer<List<int>, Uint8List>.fromHandlers(
handleData: (data, sink) {
sink.add(new Uint64List.fromList(data).buffer.asUint8List());
});
var streamController = new StreamController<List<int>>();
streamController.stream.transform(toByte).pipe(socket);
for (int i = 0; i < 1024; i++) {
streamController.add([i]);
}
streamController.close();
print('Closed ${socket.remoteAddress}');
});
}
import 'dart:io';
import 'dart:async';
main() async {
final Socket client = await Socket.connect('127.0.0.1', 4041);
var fromByte = new StreamTransformer<List<int>, List<int>>.fromHandlers(
handleData: (data, sink) {
sink.add(data.buffer.asInt64List());
});
client.transform(fromByte).listen((e) => e.forEach(print));
print('main done');
}
原尝试:
import 'dart:io';
import 'dart:typed_data';
main() async {
final server = await ServerSocket.bind('127.0.0.1', 4041);
server.listen((Socket socket) {
print('Got connected ${socket.remoteAddress}');
for(int i=0; i<1024; i++) {
socket.add(new Uint64List.fromList([i]).buffer.asUint8List());
}
socket.close();
print('Closed ${socket.remoteAddress}');
});
}
import 'dart:io';
import 'dart:typed_data';
main() async {
final Socket client = await Socket.connect('127.0.0.1', 4041);
client.listen(
(var data) {
var ints = new Uint8List.fromList(data).buffer.asInt64List();
ints.forEach((i) => print('Got $i'));
},
onDone: () { print('Done'); client.close(); },
onError: (e) { print('Got error $e'); client.close(); });
print('main done');
}
如果您不需要 Int64 的范围,您也可以使用 Int32、Int16 或某些 UIntXX 类型或任何最适合您的值范围的类型,以节省一些冗余传输的 0 字节。
我怀疑使用此处解释的转换器 https://www.dartlang.org/articles/converters-and-codecs/ could be used to do it more elegantly. This seems similar https://gist.github.com/xxgreg/9104926 但我不得不承认我无法将这些示例应用于您的用例。
更新
我按照上面链接的文章中显示的那样工作
import 'dart:convert';
import 'dart:typed_data';
class IntConverter extends Converter<List<int>, List<int>> {
const IntConverter();
List<int> convert(List<int> data) {
if (data is Uint8List) {
return data.buffer.asInt64List();
} else {
return new Uint64List.fromList(data).buffer.asUint8List();
}
}
IntSink startChunkedConversion(sink) {
return new IntSink(sink);
}
}
class IntSink extends ChunkedConversionSink<List<int>> {
final _converter;
// fales when this type is used
// final ChunkedConversionSink<List<int>> _outSink;
final _outSink;
IntSink(this._outSink) : _converter = new IntConverter();
void add(List<int> data) {
_outSink.add(_converter.convert(data));
}
void close() {
_outSink.close();
}
}
import 'dart:io';
import 'dart:typed_data';
main() async {
final server = await ServerSocket.bind('127.0.0.1', 4041);
server.listen((Socket socket) {
print('Got connected ${socket.remoteAddress}');
for (int i = 0; i < 1024; i++) {
socket.add(new Uint64List.fromList([i]).buffer.asUint8List());
}
socket.close();
print('Closed ${socket.remoteAddress}');
});
}
import 'dart:io';
import 'byte_converter.dart';
main() async {
final Socket client = await Socket.connect('127.0.0.1', 4041);
client.transform(new IntConverter()).listen((e) => e.forEach(print));
print('main done');
}
需要明确的一件事是,尽管 dart:io
中的大多数方法都在 List<int>
上运行,但 Dart 中所有 IO 的 基本假设 是这些列表是字节值的列表。文档可能不太准确。
这意味着您可以在发送字节时使用您喜欢的任何 List
实现 - 包括类型化数据,例如
var socket = ...
socket.add([1, 2, 3]);
socket.add(new List<int>.generate(3, (int index) => index * index);
socket.add('ASCII'.codeUnits);
socket.add(UTF8.encode('Søren'));
var typedData = new Uint8List(8);
var data = new ByteData.view(typedData.buffer);
data.setInt32(0, 256 * 256 - 1);
data.setInt32(4, 256 * 256 - 1, Endianness.LITTLE_ENDIAN);
socket.add(typedData)
从套接字接收数据时,Socket
流上传送的数据是从 OS 传送的字节块。您无法预测当时收到了多少字节。如果您希望能够读取特定数量的字节,一种选择是实现一个字节 reader,它具有如下方法:
Future<List<int>> read(int numBytes);
这个字节 reader 然后可以用 List<int>
的流初始化并处理帧。你可以做的是有一个缓冲区(可能使用 dart:io
中的 BytesBuilder
),它在收到数据时收集数据。当 read
被调用时, Future
当有足够的字节可用时完成。这样一个字节reader也可以在缓冲数据量达到一定限制时暂停流,并在读取数据时再次恢复。
在下面的 server/client 套接字代码中,服务器尝试发送从 0 到 1023 的整数。客户端接收到正确数量的整数,但它们在 255 处换行。这是为什么以及正确的方法是什么?
* 服务器 *
import 'dart:async';
import 'dart:io';
main() async {
final server = await ServerSocket.bind('127.0.0.1', 4041);
server.listen((Socket socket) {
print('Got connected ${socket.remoteAddress}');
for(int i=0; i<1024; i++) {
socket.add([i]);
}
socket.close();
print('Closed ${socket.remoteAddress}');
});
}
* 客户端 *
import 'dart:async';
import 'dart:io';
main() async {
final client = await Socket.connect('127.0.0.1', 4041);
client.listen(
(var data) => print('Got $data'),
onDone: () { print('Done'); client.close(); },
onError: (e) { print('Got error $e'); client.close(); });
print('main done');
}
此外,感觉 Socket api 有点不同 - 在支持流式读取方面比其他语言更 Dartish。但是,如果您只想读取 N 字节怎么办?你如何做到这一点?我看到 take 方法 (Stream take(int count)) 但我不清楚 count 正在占用,特别是在读取了多少字节方面。因为 Socket 实现了 Stream< List < int > > 并且因为 int 并不是真正固定大小的实体(我想??),你怎么知道已经读取了多少字节?
这对我有用,我不确定这是最优雅的解决方案
import 'dart:io';
import 'dart:async';
import 'dart:typed_data';
main() async {
final server = await ServerSocket.bind('127.0.0.1', 4041);
server.listen((Socket socket) {
print('Got connected ${socket.remoteAddress}');
var toByte = new StreamTransformer<List<int>, Uint8List>.fromHandlers(
handleData: (data, sink) {
sink.add(new Uint64List.fromList(data).buffer.asUint8List());
});
var streamController = new StreamController<List<int>>();
streamController.stream.transform(toByte).pipe(socket);
for (int i = 0; i < 1024; i++) {
streamController.add([i]);
}
streamController.close();
print('Closed ${socket.remoteAddress}');
});
}
import 'dart:io';
import 'dart:async';
main() async {
final Socket client = await Socket.connect('127.0.0.1', 4041);
var fromByte = new StreamTransformer<List<int>, List<int>>.fromHandlers(
handleData: (data, sink) {
sink.add(data.buffer.asInt64List());
});
client.transform(fromByte).listen((e) => e.forEach(print));
print('main done');
}
原尝试:
import 'dart:io';
import 'dart:typed_data';
main() async {
final server = await ServerSocket.bind('127.0.0.1', 4041);
server.listen((Socket socket) {
print('Got connected ${socket.remoteAddress}');
for(int i=0; i<1024; i++) {
socket.add(new Uint64List.fromList([i]).buffer.asUint8List());
}
socket.close();
print('Closed ${socket.remoteAddress}');
});
}
import 'dart:io';
import 'dart:typed_data';
main() async {
final Socket client = await Socket.connect('127.0.0.1', 4041);
client.listen(
(var data) {
var ints = new Uint8List.fromList(data).buffer.asInt64List();
ints.forEach((i) => print('Got $i'));
},
onDone: () { print('Done'); client.close(); },
onError: (e) { print('Got error $e'); client.close(); });
print('main done');
}
如果您不需要 Int64 的范围,您也可以使用 Int32、Int16 或某些 UIntXX 类型或任何最适合您的值范围的类型,以节省一些冗余传输的 0 字节。
我怀疑使用此处解释的转换器 https://www.dartlang.org/articles/converters-and-codecs/ could be used to do it more elegantly. This seems similar https://gist.github.com/xxgreg/9104926 但我不得不承认我无法将这些示例应用于您的用例。
更新
我按照上面链接的文章中显示的那样工作
import 'dart:convert';
import 'dart:typed_data';
class IntConverter extends Converter<List<int>, List<int>> {
const IntConverter();
List<int> convert(List<int> data) {
if (data is Uint8List) {
return data.buffer.asInt64List();
} else {
return new Uint64List.fromList(data).buffer.asUint8List();
}
}
IntSink startChunkedConversion(sink) {
return new IntSink(sink);
}
}
class IntSink extends ChunkedConversionSink<List<int>> {
final _converter;
// fales when this type is used
// final ChunkedConversionSink<List<int>> _outSink;
final _outSink;
IntSink(this._outSink) : _converter = new IntConverter();
void add(List<int> data) {
_outSink.add(_converter.convert(data));
}
void close() {
_outSink.close();
}
}
import 'dart:io';
import 'dart:typed_data';
main() async {
final server = await ServerSocket.bind('127.0.0.1', 4041);
server.listen((Socket socket) {
print('Got connected ${socket.remoteAddress}');
for (int i = 0; i < 1024; i++) {
socket.add(new Uint64List.fromList([i]).buffer.asUint8List());
}
socket.close();
print('Closed ${socket.remoteAddress}');
});
}
import 'dart:io';
import 'byte_converter.dart';
main() async {
final Socket client = await Socket.connect('127.0.0.1', 4041);
client.transform(new IntConverter()).listen((e) => e.forEach(print));
print('main done');
}
需要明确的一件事是,尽管 dart:io
中的大多数方法都在 List<int>
上运行,但 Dart 中所有 IO 的 基本假设 是这些列表是字节值的列表。文档可能不太准确。
这意味着您可以在发送字节时使用您喜欢的任何 List
实现 - 包括类型化数据,例如
var socket = ...
socket.add([1, 2, 3]);
socket.add(new List<int>.generate(3, (int index) => index * index);
socket.add('ASCII'.codeUnits);
socket.add(UTF8.encode('Søren'));
var typedData = new Uint8List(8);
var data = new ByteData.view(typedData.buffer);
data.setInt32(0, 256 * 256 - 1);
data.setInt32(4, 256 * 256 - 1, Endianness.LITTLE_ENDIAN);
socket.add(typedData)
从套接字接收数据时,Socket
流上传送的数据是从 OS 传送的字节块。您无法预测当时收到了多少字节。如果您希望能够读取特定数量的字节,一种选择是实现一个字节 reader,它具有如下方法:
Future<List<int>> read(int numBytes);
这个字节 reader 然后可以用 List<int>
的流初始化并处理帧。你可以做的是有一个缓冲区(可能使用 dart:io
中的 BytesBuilder
),它在收到数据时收集数据。当 read
被调用时, Future
当有足够的字节可用时完成。这样一个字节reader也可以在缓冲数据量达到一定限制时暂停流,并在读取数据时再次恢复。