在Dart中写入/读取套接字数据的字节数

时间:2015-03-16 15:49:17

标签: sockets dart

在以下服务器/客户端套接字代码中,服务器尝试从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 字节呢?你是如何实现这一目标的?我看到采用方法( Stream take(int count))但我不清楚 count 正在采取什么,特别是在术语上已读取的字节数。因为Socket实现了 Stream&lt;列表&lt; int&gt; &gt; 因为int实际上不是一个固定大小的实体(我想??),你怎么知道已经读取了多少字节?

2 个答案:

答案 0 :(得分:3)

这对我有用,我不确定这是最优雅的解决方案

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/这样解释的转换器可以更优雅地使用它。这似乎相似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');
}

答案 1 :(得分:3)

要明确的一点是,尽管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传送的字节块。您不能预测当时接收的字节数。如果您希望能够读取特定数量的字节,一个选项是实现一个字节读取器,它具有如下方法:

Future<List<int>> read(int numBytes);

然后可以使用List<int>流初始化此字节阅读器并处理框架。你可以做的是有一个缓冲区(可能使用dart:io中的BytesBuilder)收集收到的数据。调用read时,Future在有足够的字节可用时完成。这样的字节读取器也可以在缓冲数据量达到一定限度时暂停流,并在读取数据时再次恢复。