From cf804bc08c7eff1550079d7d90d81e5500793b4d Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Sat, 15 Jul 2023 11:48:21 +0300 Subject: [PATCH 01/18] Fix paths and tests --- lib/src/file/download_file_manager.dart | 5 +++-- lib/src/file/state_file.dart | 4 ++-- pubspec.yaml | 23 ++++++++++++----------- test/torrent_client_test.dart | 13 ++++++++----- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/lib/src/file/download_file_manager.dart b/lib/src/file/download_file_manager.dart index dedd51a..ca954bc 100644 --- a/lib/src/file/download_file_manager.dart +++ b/lib/src/file/download_file_manager.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:developer'; +import 'dart:io'; import 'package:torrent_model/torrent_model.dart'; import '../peer/peer_base.dart'; @@ -47,8 +48,8 @@ class DownloadFileManager { Future _init(String directory) async { var lastc = directory.substring(directory.length - 1); - if (lastc != '\\' || lastc != '/') { - directory = directory + '\\'; + if (lastc != Platform.pathSeparator) { + directory = directory + Platform.pathSeparator; } _initFileMap(directory); return this; diff --git a/lib/src/file/state_file.dart b/lib/src/file/state_file.dart index cdd56e5..c45baba 100644 --- a/lib/src/file/state_file.dart +++ b/lib/src/file/state_file.dart @@ -56,8 +56,8 @@ class StateFile { Future init(String directoryPath, Torrent metainfo) async { var lastc = directoryPath.substring(directoryPath.length - 1); - if (lastc != '\\' || lastc != '/') { - directoryPath = directoryPath + '\\\\'; + if (lastc != Platform.pathSeparator) { + directoryPath = directoryPath + Platform.pathSeparator; } _bitfieldFile = File('${directoryPath}${metainfo.infoHash}.bt.state'); diff --git a/pubspec.yaml b/pubspec.yaml index a2787b8..176e617 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,27 +2,28 @@ name: torrent_task description: BitTorrent download client package written by pure Dart language. version: 0.3.0 repository: https://github.com/eclipseglory/torrent_task -issue_tracker : https://github.com/eclipseglory/torrent_task/issues +issue_tracker: https://github.com/eclipseglory/torrent_task/issues publish_to: https://pub.dev/ environment: - sdk: '>=2.10.0 <3.0.0' + sdk: ">=2.10.0 <3.0.0" dependencies: -# path: ^1.7.0 - torrent_model : '>=1.0.3 <2.0.0' - torrent_tracker: '>=1.3.12 <2.0.0' - - # torrent_tracker: + # path: ^1.7.0 + torrent_model: ">=1.0.3 <2.0.0" + torrent_tracker: ">=1.3.12 <2.0.0" + + # torrent_tracker: # path: '../../dd/torrent_tracker' - dht_dart: '>=0.0.7 <1.0.0' - dartorrent_common: '>=1.0.3 <2.0.0' - bencode_dart : ^1.0.2 - utp: '>= 1.0.1 < 2.0.0' + dht_dart: ">=0.0.7 <1.0.0" + dartorrent_common: ">=1.0.3 <2.0.0" + bencode_dart: ^1.0.2 + utp: ">=1.0.1 <2.0.0" # utp: # path: '../../dd/utp' dev_dependencies: pedantic: ^1.9.0 test: ^1.14.4 + meta: 1.2.4 diff --git a/test/torrent_client_test.dart b/test/torrent_client_test.dart index 95648c6..866670b 100644 --- a/test/torrent_client_test.dart +++ b/test/torrent_client_test.dart @@ -161,11 +161,12 @@ void main() { group('StateFile Test - ', () { var directory = 'test'; - // var torrent = await Torrent.parse('$directory/sample3.torrent'); Torrent torrent; setUpAll(() async { - torrent = await Torrent.parse('$directory/test4.torrent'); - var f = File('$directory/${torrent.infoHash}.bt.state'); + torrent = await Torrent.parse( + '$directory${Platform.pathSeparator}test4.torrent'); + var f = File( + '$directory${Platform.pathSeparator}${torrent.infoHash}.bt.state'); if (await f.exists()) await f.delete(); }); test('Write/Read StateFile', () async { @@ -204,7 +205,8 @@ void main() { await stateFile.close(); await stateFile.close(); //关闭两次会怎样? - var f = File('$directory/${torrent.infoHash}.bt.state'); + var f = File( + '$directory${Platform.pathSeparator}${torrent.infoHash}.bt.state'); var locker = Completer(); var data = []; f.openRead().listen((event) { @@ -240,7 +242,8 @@ void main() { test('Delete StateFile', () async { var stateFile = await StateFile.getStateFile(directory, torrent); - var t = File('$directory/${torrent.infoHash}.bt.state'); + var t = File( + '$directory${Platform.pathSeparator}${torrent.infoHash}.bt.state'); assert(await t.exists()); await stateFile.delete(); await stateFile.delete(); //删除两次 From 21474b1df0f8234aba71640f2113eee38d6d7c51 Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Mon, 17 Jul 2023 13:07:12 +0300 Subject: [PATCH 02/18] migrate to nullsafety --- example/lsd_example.dart | 5 +- example/torrent_task_comin_example.dart | 13 +-- example/torrent_task_example.dart | 14 +-- lib/src/file/download_file.dart | 58 ++++++----- lib/src/file/download_file_manager.dart | 41 ++++---- lib/src/file/state_file.dart | 73 +++++++------- lib/src/lsd/lsd.dart | 26 ++--- lib/src/metadata/metadata_downloader.dart | 55 +++++------ lib/src/peer/bitfield.dart | 10 +- lib/src/peer/congestion_control.dart | 18 ++-- lib/src/peer/extended_proccessor.dart | 6 +- lib/src/peer/holepunch.dart | 17 ++-- lib/src/peer/peer.dart | 109 +++++++++++---------- lib/src/peer/peer_event_dispatcher.dart | 4 +- lib/src/peer/peers_manager.dart | 39 ++++---- lib/src/peer/pex.dart | 11 ++- lib/src/peer/speed_calculator.dart | 12 +-- lib/src/piece/base_piece_selector.dart | 2 +- lib/src/piece/piece.dart | 11 +-- lib/src/piece/piece_manager.dart | 8 +- lib/src/piece/piece_provider.dart | 2 +- lib/src/piece/piece_selector.dart | 2 +- lib/src/task.dart | 112 +++++++++++----------- lib/src/utils.dart | 2 +- lib/torrent_task.dart | 2 +- pubspec.yaml | 22 ++--- test/holepunch_test.dart | 1 + test/peer_communicate.dart | 8 +- test/torrent_client_test.dart | 108 ++++++++++----------- 29 files changed, 395 insertions(+), 396 deletions(-) create mode 100644 test/holepunch_test.dart diff --git a/example/lsd_example.dart b/example/lsd_example.dart index 52314bc..f0986e1 100644 --- a/example/lsd_example.dart +++ b/example/lsd_example.dart @@ -6,9 +6,8 @@ import 'package:torrent_task/torrent_task.dart'; void main(List args) async { print(await getTorrenTaskVersion()); - exit(1); - var torrentFile = 'example/12.torrent'; - var savePath = 'g:/bttest'; + var torrentFile = 'example${Platform.pathSeparator}test4.torrent'; + var savePath = 'tmp'; var model = await Torrent.parse(torrentFile); var infoHash = model.infoHash; var lsd = LSD(infoHash, 'daa231dfa'); diff --git a/example/torrent_task_comin_example.dart b/example/torrent_task_comin_example.dart index e929ba2..892f14d 100644 --- a/example/torrent_task_comin_example.dart +++ b/example/torrent_task_comin_example.dart @@ -7,12 +7,13 @@ import 'package:torrent_task/torrent_task.dart'; /// This example is for connect local Future main() async { - var model = await Torrent.parse('example/test8.torrent'); + var model = + await Torrent.parse('example${Platform.pathSeparator}test4.torrent'); // 不获取peers model.announces.clear(); - var task = TorrentTask.newTask(model, 'g:/bttest5/'); - Timer timer; - Timer timer1; + var task = TorrentTask.newTask(model, 'tmp${Platform.pathSeparator}test'); + Timer? timer; + Timer? timer1; task.onFileComplete((filepath) { print('$filepath downloaded complete'); }); @@ -31,7 +32,7 @@ Future main() async { timer = Timer.periodic(Duration(seconds: 2), (timer) { try { print( - 'Downloaed: ${task.downloaded / (1024 * 1024)} mb , ${((task.downloaded / model.length) * 100).toStringAsFixed(2)}%'); + 'Downloaed: ${task.downloaded ?? 0 / (1024 * 1024)} mb , ${((task.downloaded ?? 0 / model.length) * 100).toStringAsFixed(2)}%'); } finally {} }); @@ -51,6 +52,6 @@ Future main() async { // task.resume(); // }); // 自己下载自己 - task.addPeer(CompactAddress(InternetAddress.tryParse('192.168.0.24'), 57331), + task.addPeer(CompactAddress(InternetAddress.tryParse('192.168.0.24')!, 57331), PeerType.UTP); } diff --git a/example/torrent_task_example.dart b/example/torrent_task_example.dart index 1b9cea7..59984dc 100644 --- a/example/torrent_task_example.dart +++ b/example/torrent_task_example.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:developer'; import 'dart:io'; import 'package:dartorrent_common/dartorrent_common.dart'; @@ -7,13 +8,13 @@ import 'package:torrent_task/torrent_task.dart'; void main() async { try { - var torrentFile = 'example/test4.torrent'; - var savePath = 'g:/bttest'; + var torrentFile = 'example${Platform.pathSeparator}test4.torrent'; + var savePath = 'tmp${Platform.pathSeparator}test'; var model = await Torrent.parse(torrentFile); // model.announces.clear(); var task = TorrentTask.newTask(model, savePath); - Timer timer; - Timer timer1; + Timer? timer; + Timer? timer1; var startTime = DateTime.now().millisecondsSinceEpoch; task.onTaskComplete(() { print( @@ -33,8 +34,9 @@ void main() async { task.startAnnounceUrl(element, model.infoHashBuffer); }); }); - - model.nodes?.forEach((element) { + log('Adding dht nodes'); + model.nodes.forEach((element) { + log('dht node $element'); task.addDHTNode(element); }); print(map); diff --git a/lib/src/file/download_file.dart b/lib/src/file/download_file.dart index 413a151..3895427 100644 --- a/lib/src/file/download_file.dart +++ b/lib/src/file/download_file.dart @@ -15,15 +15,15 @@ class DownloadFile { final int length; - File _file; + File? _file; - RandomAccessFile _writeAcces; + RandomAccessFile? _writeAcces; - RandomAccessFile _readAccess; + RandomAccessFile? _readAccess; - StreamController _sc; + StreamController? _sc; - StreamSubscription _ss; + StreamSubscription? _ss; DownloadFile(this.filePath, this.start, this.length); @@ -45,7 +45,7 @@ class DownloadFile { int position, List block, int start, int end) async { _writeAcces ??= await getRandomAccessFile(WRITE); var completer = Completer(); - _sc.add({ + _sc?.add({ 'type': WRITE, 'position': position, 'block': block, @@ -59,7 +59,7 @@ class DownloadFile { Future> requestRead(int position, int length) async { _readAccess ??= await getRandomAccessFile(READ); var completer = Completer>(); - _sc.add({ + _sc?.add({ 'type': READ, 'position': position, 'length': length, @@ -72,7 +72,7 @@ class DownloadFile { /// /// 每次只处理一个请求。`Stream`在进入该方法后通过`StreamSubscription`暂停通道信息读取,直到处理完一条请求后才恢复 void _processRequest(event) async { - _ss.pause(); + _ss?.pause(); if (event['type'] == WRITE) { await _write(event); } @@ -82,10 +82,10 @@ class DownloadFile { if (event['type'] == FLUSH) { await _flush(event); } - _ss.resume(); + _ss?.resume(); } - void _write(event) async { + Future _write(event) async { Completer completer = event['completer']; try { int position = event['position']; @@ -94,8 +94,8 @@ class DownloadFile { List block = event['block']; _writeAcces = await getRandomAccessFile(WRITE); - _writeAcces = await _writeAcces.setPosition(position); - _writeAcces = await _writeAcces.writeFrom(block, start, end); + _writeAcces = await _writeAcces?.setPosition(position); + _writeAcces = await _writeAcces?.writeFrom(block, start, end); completer.complete(true); } catch (e) { log('Write file error:', error: e, name: runtimeType.toString()); @@ -107,15 +107,15 @@ class DownloadFile { Future requestFlush() async { _writeAcces ??= await getRandomAccessFile(WRITE); var completer = Completer(); - _sc.add({'type': FLUSH, 'completer': completer}); + _sc?.add({'type': FLUSH, 'completer': completer}); return completer.future; } - void _flush(event) async { + Future _flush(event) async { Completer completer = event['completer']; try { _writeAcces = await getRandomAccessFile(WRITE); - await _writeAcces.flush(); + await _writeAcces?.flush(); completer.complete(true); } catch (e) { log('Flush error:', error: e, name: runtimeType.toString()); @@ -123,7 +123,7 @@ class DownloadFile { } } - void _read(event) async { + Future _read(event) async { Completer completer = event['completer']; try { int position = event['position']; @@ -141,21 +141,19 @@ class DownloadFile { /// /// 获取对应文件,如果文件不存在会创建一个新文件 - Future _getOrCreateFile() async { - if (filePath == null) return null; + Future _getOrCreateFile() async { _file ??= File(filePath); - var exists = await _file.exists(); - if (!exists) { - _file = await _file.create(recursive: true); - var access = await _file.open(mode: FileMode.write); - access = await access.truncate(length); - await access.close(); + var exists = await _file?.exists(); + if (exists != null && !exists) { + _file = await _file?.create(recursive: true); + var access = await _file?.open(mode: FileMode.write); + access = await access?.truncate(length); + await access?.close(); } return _file; } - Future get exists { - if (filePath == null) return null; + Future? get exists { return File(filePath).exists(); } @@ -163,15 +161,15 @@ class DownloadFile { var file = await _getOrCreateFile(); var access; if (type == WRITE) { - _writeAcces ??= await file.open(mode: FileMode.writeOnlyAppend); + _writeAcces ??= await file?.open(mode: FileMode.writeOnlyAppend); access = _writeAcces; } else if (type == READ) { - _readAccess ??= await file.open(mode: FileMode.read); + _readAccess ??= await file?.open(mode: FileMode.read); access = _readAccess; } if (_sc == null) { _sc = StreamController(); - _ss = _sc.stream.listen(_processRequest); + _ss = _sc?.stream.listen(_processRequest); } return access; } @@ -224,5 +222,5 @@ class DownloadFile { } @override - int get hashCode => filePath?.hashCode; + int get hashCode => filePath.hashCode; } diff --git a/lib/src/file/download_file_manager.dart b/lib/src/file/download_file_manager.dart index ca954bc..483c5b1 100644 --- a/lib/src/file/download_file_manager.dart +++ b/lib/src/file/download_file_manager.dart @@ -19,9 +19,9 @@ class DownloadFileManager { final Set _files = {}; - List> _piece2fileMap; + List?>? _piece2fileMap; - final Map> _file2pieceMap = {}; + final Map?> _file2pieceMap = {}; final List _subPieceCompleteHandles = []; @@ -36,7 +36,7 @@ class DownloadFileManager { /// TODO /// - 没有建立文件读取缓存 DownloadFileManager(this.metainfo, this._stateFile) { - _piece2fileMap = List(_stateFile.bitfield.piecesNum); + _piece2fileMap = List.filled(_stateFile.bitfield.piecesNum, null); } static Future createFileManager( @@ -63,7 +63,7 @@ class DownloadFileManager { bool get isAllComplete { return _stateFile.bitfield.piecesNum == - _stateFile.bitfield.completedPieces.length; + _stateFile.bitfield.completedPieces?.length; } int get piecesNumber => _stateFile.bitfield.piecesNum; @@ -98,7 +98,7 @@ class DownloadFileManager { }); } - int get downloaded => _stateFile?.downloaded; + int get downloaded => _stateFile.downloaded; /// 该方法看似只将缓冲区内容写入磁盘,实际上 /// 每当缓存写入后都会认为该[pieceIndex]对应`Piece`已经完成,则会去移除 @@ -108,7 +108,7 @@ class DownloadFileManager { var flushed = {}; for (var i = 0; i < pieceIndices.length; i++) { var pieceIndex = pieceIndices.elementAt(i); - var fs = _piece2fileMap[pieceIndex]; + var fs = _piece2fileMap?[pieceIndex]; if (fs == null || fs.isEmpty) continue; for (var i = 0; i < fs.length; i++) { var file = fs[i]; @@ -126,7 +126,7 @@ class DownloadFileManager { } var msg = - '已下载:${d / (1024 * 1024)} mb , 完成度 ${((d / metainfo.length) * 10000).toInt() / 100} %'; + 'downloaded:${d / (1024 * 1024)} mb , 完成度 ${((d / metainfo.length) * 10000).toInt() / 100} %'; log(msg, name: runtimeType.toString()); return true; } @@ -161,11 +161,12 @@ class DownloadFileManager { } if (fe.remainder(metainfo.pieceLength) == 0) endPiece--; for (var pieceIndex = startPiece; pieceIndex <= endPiece; pieceIndex++) { - var l = _piece2fileMap[pieceIndex]; + var l = _piece2fileMap?[pieceIndex]; if (l == null) { l = []; - _piece2fileMap[pieceIndex] = l; - if (!localHave(pieceIndex)) pieces.add(pieceIndex); + _piece2fileMap?[pieceIndex] = l; + var local_have = localHave(pieceIndex); + if (local_have != null && !local_have) pieces.add(pieceIndex); } l.add(df); } @@ -197,7 +198,7 @@ class DownloadFileManager { } void readFile(int pieceIndex, int begin, int length) { - var tempFiles = _piece2fileMap[pieceIndex]; + var tempFiles = _piece2fileMap?[pieceIndex]; var ps = pieceIndex * metainfo.pieceLength + begin; var pe = ps + length; if (tempFiles == null || tempFiles.isEmpty) return; @@ -225,7 +226,7 @@ class DownloadFileManager { /// 该`Sub Piece`是来自于[pieceIndex]对应的`Piece`,内容为[block],起始位置是[begin]。 /// 该类不会去验证写入的Sub Piece是否重复,重复内容直接覆盖之前内容 void writeFile(int pieceIndex, int begin, List block) { - var tempFiles = _piece2fileMap[pieceIndex]; + var tempFiles = _piece2fileMap?[pieceIndex]; var ps = pieceIndex * metainfo.pieceLength + begin; var blockSize = block.length; var pe = ps + blockSize; @@ -252,7 +253,7 @@ class DownloadFileManager { return; } - Map _mapDownloadFilePosition( + Map? _mapDownloadFilePosition( int pieceStart, int pieceEnd, int length, DownloadFile tempFile) { var fs = tempFile.start; var fe = fs + tempFile.length; @@ -276,7 +277,7 @@ class DownloadFileManager { } Future close() async { - await _stateFile?.close(); + await _stateFile.close(); for (var i = 0; i < _files.length; i++) { var file = _files.elementAt(i); await file.close(); @@ -285,16 +286,16 @@ class DownloadFileManager { } void _clean() { - _subPieceCompleteHandles?.clear(); - _subPieceFailedHandles?.clear(); - _subPieceReadHandles?.clear(); - _fileCompleteHandles?.clear(); - _file2pieceMap?.clear(); + _subPieceCompleteHandles.clear(); + _subPieceFailedHandles.clear(); + _subPieceReadHandles.clear(); + _fileCompleteHandles.clear(); + _file2pieceMap.clear(); _piece2fileMap = null; } Future delete() async { - await _stateFile?.delete(); + await _stateFile.delete(); for (var i = 0; i < _files.length; i++) { var file = _files.elementAt(i); await file.delete(); diff --git a/lib/src/file/state_file.dart b/lib/src/file/state_file.dart index c45baba..3bc1a52 100644 --- a/lib/src/file/state_file.dart +++ b/lib/src/file/state_file.dart @@ -10,12 +10,12 @@ const DOWNLOADED_TYPE = 'downloaded'; const UPLOADED_TYPE = 'uploaded'; /// -/// 下载状态保存文件 +/// Download state save file /// -/// 文件内容:``,其中``是一个64位整数, -/// 文件名:`.bt.state` +/// Document Content:``,where``is a 64-bit integer, +/// file name:`.bt.state` class StateFile { - Bitfield _bitfield; + late Bitfield _bitfield; bool _closed = false; @@ -25,13 +25,13 @@ class StateFile { StateFile(this.metainfo); - RandomAccessFile _access; + RandomAccessFile? _access; - File _bitfieldFile; + File? _bitfieldFile; - StreamSubscription _ss; + StreamSubscription? _ss; - StreamController _sc; + StreamController? _sc; bool get isClosed => _closed; @@ -45,11 +45,14 @@ class StateFile { Bitfield get bitfield => _bitfield; int get downloaded { - var _downloaded = bitfield.completedPieces.length * metainfo.pieceLength; - if (bitfield.completedPieces.contains(bitfield.piecesNum - 1)) { - _downloaded -= metainfo.pieceLength - metainfo.lastPriceLength; + if (bitfield != null && bitfield.completedPieces != null) { + var _downloaded = bitfield.completedPieces!.length * metainfo.pieceLength; + if (bitfield.completedPieces!.contains(bitfield.piecesNum - 1)) { + _downloaded -= metainfo.pieceLength - metainfo.lastPieceLength; + } + return _downloaded; } - return _downloaded; + return 0; } int get uploaded => _uploaded; @@ -61,16 +64,16 @@ class StateFile { } _bitfieldFile = File('${directoryPath}${metainfo.infoHash}.bt.state'); - var exists = await _bitfieldFile.exists(); - if (!exists) { - _bitfieldFile = await _bitfieldFile.create(recursive: true); + var exists = await _bitfieldFile?.exists(); + if (exists != null && !exists) { + _bitfieldFile = await _bitfieldFile?.create(recursive: true); _bitfield = Bitfield.createEmptyBitfield(metainfo.pieces.length); _uploaded = 0; - var acc = await _bitfieldFile.open(mode: FileMode.writeOnly); - acc = await acc.truncate(_bitfield.length + 8); - await acc.close(); + var acc = await _bitfieldFile?.open(mode: FileMode.writeOnly); + acc = await acc?.truncate(_bitfield.length + 8); + await acc?.close(); } else { - var bytes = await _bitfieldFile.readAsBytes(); + var bytes = await _bitfieldFile!.readAsBytes(); var piecesNum = metainfo.pieces.length; var bitfieldBufferLength = piecesNum ~/ 8; if (bitfieldBufferLength * 8 != piecesNum) bitfieldBufferLength++; @@ -79,13 +82,13 @@ class StateFile { _uploaded = view.getUint64(_bitfield.length); } - return _bitfieldFile; + return _bitfieldFile!; } Future update(int index, {bool have = true, int uploaded = 0}) async { _access = await getAccess(); var completer = Completer(); - _sc.add({ + _sc?.add({ 'type': 'single', 'index': index, 'uploaded': uploaded, @@ -96,10 +99,10 @@ class StateFile { } Future updateAll(List indices, - {List have, int uploaded = 0}) async { + {List? have, int uploaded = 0}) async { _access = await getAccess(); var completer = Completer(); - _sc.add({ + _sc?.add({ 'type': 'all', 'indices': indices, 'uploaded': uploaded, @@ -121,22 +124,22 @@ class StateFile { } _bitfield.setBit(index, have); } else { - if (_uploaded == uploaded) return false; + if (_uploaded == uploaded) return; } _uploaded = uploaded; try { var access = await getAccess(); if (index != -1) { var i = index ~/ 8; - await access.setPosition(i); - await access.writeByte(_bitfield.buffer[i]); + await access?.setPosition(i); + await access?.writeByte(_bitfield.buffer[i]); } - await access.setPosition(_bitfield.buffer.length); + await access?.setPosition(_bitfield.buffer.length); var data = Uint8List(8); var d = ByteData.view(data.buffer); d.setUint64(0, uploaded); - access = await access.writeFrom(data); - await access.flush(); + access = await access?.writeFrom(data); + await access?.flush(); c.complete(true); } catch (e) { log('Update bitfield piece:[$index],uploaded:$uploaded error :', @@ -161,21 +164,21 @@ class StateFile { } void _processRequest(event) async { - _ss.pause(); + _ss?.pause(); // if (event['type'] == 'all') { // await _updateAll(event); // } if (event['type'] == 'single') { await _update(event); } - _ss.resume(); + _ss?.resume(); } - Future getAccess() async { + Future getAccess() async { if (_access == null) { - _access = await _bitfieldFile.open(mode: FileMode.writeOnlyAppend); + _access = await _bitfieldFile?.open(mode: FileMode.writeOnlyAppend); _sc = StreamController(); - _ss = _sc.stream.listen(_processRequest, onError: (e) => print(e)); + _ss = _sc?.stream.listen(_processRequest, onError: (e) => print(e)); } return _access; } @@ -197,7 +200,7 @@ class StateFile { } } - Future delete() async { + Future delete() async { await close(); var r = _bitfieldFile?.delete(); _bitfieldFile = null; diff --git a/lib/src/lsd/lsd.dart b/lib/src/lsd/lsd.dart index c8c64ae..384b43e 100644 --- a/lib/src/lsd/lsd.dart +++ b/lib/src/lsd/lsd.dart @@ -21,11 +21,11 @@ class LSD { bool get isClosed => _closed; - RawDatagramSocket _socket; + RawDatagramSocket? _socket; final String _infoHashHex; - int port; + int? port; final String _peerId; @@ -34,16 +34,18 @@ class LSD { LSD(this._infoHashHex, this._peerId); - Timer _timer; + Timer? _timer; void start() async { _socket ??= await RawDatagramSocket.bind(InternetAddress.anyIPv4, LSD_PORT); - _socket.listen((event) { + _socket?.listen((event) { if (event == RawSocketEvent.read) { - var datagram = _socket.receive(); - var datas = datagram.data; - var str = String.fromCharCodes(datas); - _processReceive(str, datagram.address); + var datagram = _socket?.receive(); + if (datagram != null) { + var datas = datagram.data; + var str = String.fromCharCodes(datas); + _processReceive(str, datagram.address); + } } }, onDone: () {}, onError: (e) {}); await _announce(); @@ -89,18 +91,18 @@ class LSD { } } - void _announce() async { + Future _announce() async { _timer?.cancel(); var message = _createMessage(); await _sendMessage(message); _timer = Timer(Duration(seconds: 5 * 60), () => _announce()); } - Future _sendMessage(String message, [Completer completer]) { + Future? _sendMessage(String message, [Completer? completer]) { if (_socket == null) return null; completer ??= Completer(); - var success = _socket.send(message.codeUnits, LSD_HOST, LSD_PORT) > 0; - if (!success) { + var success = _socket?.send(message.codeUnits, LSD_HOST, LSD_PORT); + if (success != null && !(success > 0)) { Timer.run(() => _sendMessage(message, completer)); } else { completer.complete(); diff --git a/lib/src/metadata/metadata_downloader.dart b/lib/src/metadata/metadata_downloader.dart index 24e8378..dbc48b3 100644 --- a/lib/src/metadata/metadata_downloader.dart +++ b/lib/src/metadata/metadata_downloader.dart @@ -20,25 +20,25 @@ class MetadataDownloader final Set data)> _handlers = {}; final List IGNORE_IPS = [ - InternetAddress.tryParse('0.0.0.0'), - InternetAddress.tryParse('127.0.0.1') + InternetAddress.tryParse('0.0.0.0')!, + InternetAddress.tryParse('127.0.0.1')! ]; - InternetAddress localExtenelIP; + InternetAddress? localExtenelIP; - int _metaDataSize; + int? _metaDataSize; - int _metaDataBlockNum; + int? _metaDataBlockNum; - int get metaDataSize => _metaDataSize; + int? get metaDataSize => _metaDataSize; - String _localPeerId; + late String _localPeerId; - List _infoHashBuffer; + late List _infoHashBuffer; List get infoHashBuffer => _infoHashBuffer; - String _infoHashString; + final String _infoHashString; final Set _activePeers = {}; @@ -48,13 +48,13 @@ class MetadataDownloader final Set _incomingAddress = {}; - DHT _dht; + final DHT _dht = DHT(); bool _running = false; - int _E; + final int _E = 'e'.codeUnits[0]; - List _infoDatas; + List _infoDatas = []; final Queue _metaDataPieces = Queue(); @@ -62,14 +62,11 @@ class MetadataDownloader final Map _requestTimeout = {}; - MetadataDownloader(String infoHashString) { - _E = 'e'.codeUnits[0]; - _infoHashString = infoHashString; + MetadataDownloader(this._infoHashString) { _localPeerId = generatePeerId(); - _infoHashBuffer = hexString2Buffer(infoHashString); + _infoHashBuffer = hexString2Buffer(_infoHashString)!; assert(_infoHashBuffer.isNotEmpty && _infoHashBuffer.length == 20, 'Info Hash String is incorrect'); - _dht = DHT(); } void startDownload() { @@ -122,7 +119,7 @@ class MetadataDownloader /// Usually [socket] is null , unless this peer was incoming connection, but /// this type peer was managed by [TorrentTask] , user don't need to know that. void addNewPeerAddress(CompactAddress address, - [PeerType type = PeerType.TCP, Socket socket]) { + [PeerType type = PeerType.TCP, dynamic socket]) { if (!_running) return; if (address == null) return; if (address.address == localExtenelIP) return; @@ -133,7 +130,7 @@ class MetadataDownloader } } if (_peersAddress.add(address)) { - Peer peer; + Peer? peer; if (type == PeerType.TCP) { peer = Peer.newTCPPeer(_localPeerId, address, _infoHashBuffer, 0, socket); @@ -210,12 +207,12 @@ class MetadataDownloader if (name == 'handshake') { if (data['metadata_size'] != null && _metaDataSize == null) { _metaDataSize = data['metadata_size']; - _infoDatas = List.filled(_metaDataSize, 0); - _metaDataBlockNum = _metaDataSize ~/ (16 * 1024); - if (_metaDataBlockNum * (16 * 1024) != _metaDataSize) { - _metaDataBlockNum++; + _infoDatas = List.filled(_metaDataSize!, 0); + _metaDataBlockNum = _metaDataSize! ~/ (16 * 1024); + if (_metaDataBlockNum! * (16 * 1024) != _metaDataSize) { + _metaDataBlockNum = _metaDataBlockNum! + 1; } - for (var i = 0; i < _metaDataBlockNum; i++) { + for (var i = 0; i < _metaDataBlockNum!; i++) { _metaDataPieces.add(i); } } @@ -279,14 +276,14 @@ class MetadataDownloader void _pieceDownloadComplete(int piece, int start, List bytes) async { // 防止多次调用 - if (_completedPieces.length >= _metaDataBlockNum || + if (_completedPieces.length >= _metaDataBlockNum! || _completedPieces.contains(piece)) { return; } var started = piece * 16 * 1024; List.copyRange(_infoDatas, started, bytes, start); _completedPieces.add(piece); - if (_completedPieces.length >= _metaDataBlockNum) { + if (_completedPieces.length >= _metaDataBlockNum!) { // 此时就停止,然后抛出事件 await stop(); _handlers.forEach((h) { @@ -298,14 +295,14 @@ class MetadataDownloader } } - Peer _randomAvalidatedPeer() { + Peer? _randomAvalidatedPeer() { if (_avalidatedPeers.isEmpty) return null; var n = _avalidatedPeers.length; var index = randomInt(n); return _avalidatedPeers.elementAt(index); } - void _requestMetaData([Peer peer]) { + void _requestMetaData([Peer? peer]) { if (_metaDataPieces.isNotEmpty) { peer ??= _randomAvalidatedPeer(); if (peer == null) return; @@ -315,7 +312,7 @@ class MetadataDownloader _metaDataPieces.add(piece); _requestMetaData(); }); - _requestTimeout[peer.remotePeerId] = timer; + _requestTimeout[peer.remotePeerId!] = timer; peer.sendExtendMessage('ut_metadata', msg); } } diff --git a/lib/src/peer/bitfield.dart b/lib/src/peer/bitfield.dart index f65fdcd..a604584 100644 --- a/lib/src/peer/bitfield.dart +++ b/lib/src/peer/bitfield.dart @@ -9,7 +9,7 @@ class Bitfield { final int piecesNum; final Uint8List buffer; - List _completedIndex; + List? _completedIndex; Bitfield(this.piecesNum, this.buffer); bool getBit(int index) { @@ -30,7 +30,7 @@ class Bitfield { var orNum = BASE_NUM >> b; if (bit) { _completedIndex = completedPieces; - _completedIndex.add(index); + _completedIndex?.add(index); buffer[i] = buffer[i] | orNum; } else { _completedIndex?.remove(index); @@ -81,13 +81,13 @@ class Bitfield { for (var j = 0; j < 8; j++) { var index = i * 8 + j; if (getBit(index)) { - _completedIndex.add(index); + _completedIndex?.add(index); } } } } } - return _completedIndex; + return _completedIndex!; } int get length => buffer.length; @@ -105,7 +105,7 @@ class Bitfield { } static Bitfield copyFrom(int piecesNum, List list, - [int offset = 0, int end]) { + [int offset = 0, int? end]) { var b = piecesNum ~/ 8; if (b * 8 != piecesNum) b++; var mybuffer = Uint8List(b); diff --git a/lib/src/peer/congestion_control.dart b/lib/src/peer/congestion_control.dart index 000c85a..d3ff15c 100644 --- a/lib/src/peer/congestion_control.dart +++ b/lib/src/peer/congestion_control.dart @@ -20,11 +20,11 @@ mixin CongestionControl { // 初始是10秒 double _rto = 10000000; - double _srtt; + double? _srtt; - double _rttvar; + double? _rttvar; - Timer _timeout; + Timer? _timeout; int _allowWindowSize = DEFAULT_REQUEST_LENGTH; @@ -52,10 +52,10 @@ mixin CongestionControl { _srtt = rtt.toDouble(); _rttvar = rtt / 2; } else { - _rttvar = (1 - 0.25) * _rttvar + 0.25 * (_srtt - rtt).abs(); - _srtt = (1 - 0.125) * _srtt + 0.125 * rtt; + _rttvar = (1 - 0.25) * _rttvar! + 0.25 * (_srtt! - rtt).abs(); + _srtt = (1 - 0.125) * _srtt! + 0.125 * rtt; } - _rto = _srtt + max(100000, 4 * _rttvar); + _rto = _srtt! + max(100000, 4 * _rttvar!); // 不到1秒,就设置为1秒 _rto = max(_rto, 1000000); } @@ -108,20 +108,20 @@ mixin CongestionControl { void ackRequest(List> requests) { if (requests.isEmpty) return; var downloaded = 0; - int minRtt; + int? minRtt; requests.forEach((request) { // 重发后收到的不管 if (request == null || request[4] != 0) return; var now = DateTime.now().microsecondsSinceEpoch; var rtt = now - request[3]; minRtt ??= rtt; - minRtt = min(minRtt, rtt); + minRtt = min(minRtt!, rtt); updateRTO(rtt); downloaded += request[2]; }); if (downloaded == 0 || minRtt == null) return; var artt = minRtt; - var delay_factor = (CCONTROL_TARGET - artt) / CCONTROL_TARGET; + var delay_factor = (CCONTROL_TARGET - artt!) / CCONTROL_TARGET; var window_factor = downloaded / _allowWindowSize; var scaled_gain = MAX_CWND_INCREASE_REQUESTS_PER_RTT * delay_factor * window_factor; diff --git a/lib/src/peer/extended_proccessor.dart b/lib/src/peer/extended_proccessor.dart index e563874..ad10090 100644 --- a/lib/src/peer/extended_proccessor.dart +++ b/lib/src/peer/extended_proccessor.dart @@ -6,7 +6,7 @@ import 'package:bencode_dart/bencode_dart.dart'; mixin ExtendedProcessor { final Map _extendedEventMap = {}; int _id = 1; - Map _rawMap; + Map? _rawMap; final Map _localExtended = {}; Map get localExtened { @@ -35,9 +35,9 @@ mixin ExtendedProcessor { _id++; } - int getExtendedEventId(String name) { + int? getExtendedEventId(String name) { if (_rawMap != null) { - return _rawMap[name]; + return _rawMap![name]; } return null; } diff --git a/lib/src/peer/holepunch.dart b/lib/src/peer/holepunch.dart index 52c458a..1d6184c 100644 --- a/lib/src/peer/holepunch.dart +++ b/lib/src/peer/holepunch.dart @@ -21,7 +21,7 @@ mixin Holepunch { ]; List getRendezvousMessage(CompactAddress address) { - List message; + List message = List.empty(); if (address.address.type == InternetAddressType.IPv4) { message = List.filled(12, 0); List.copyRange(message, 2, address.toBytes()); @@ -46,7 +46,7 @@ mixin Holepunch { var type = data[0]; var iptype = data[1]; var offset = 0; - CompactAddress ip; + CompactAddress? ip; try { if (iptype == 0) { ip = CompactAddress.parseIPv4Address(data, 2); @@ -58,10 +58,11 @@ mixin Holepunch { } catch (e) { // do nothing } - var err; + if (ip == null) return; + int err; if (type == 0x02) { var e = Uint8List(4); - // 有些客户端返回的error不到4位: + // Some clients return less than 4 errors: if (data.length < offset + 4) { var start = offset + 4 - data.length; List.copyRange(e, start, data, offset); @@ -70,24 +71,24 @@ mixin Holepunch { } err = ByteData.view(e.buffer).getUint32(0); if (err >= 1000) { - err = e[0]; // 有些客户端把错误码放在第一位 + err = e[0]; // Some clients put the error code first } err--; var errMsg = 'Unknown error'; if (err >= 0) { errMsg = ERROR_MSG[err]; } - Timer.run(() => holePunchError(errMsg, ip)); + Timer.run(() => holePunchError(errMsg, ip!)); return; } if (type == 0x00) { - Timer.run(() => holePunchRendezvous(ip)); + Timer.run(() => holePunchRendezvous(ip!)); return; } if (type == 0x01) { - Timer.run(() => holePunchConnect(ip)); + Timer.run(() => holePunchConnect(ip!)); return; } } diff --git a/lib/src/peer/peer.dart b/lib/src/peer/peer.dart index c66f0ef..8dbdc42 100644 --- a/lib/src/peer/peer.dart +++ b/lib/src/peer/peer.dart @@ -85,20 +85,20 @@ abstract class Peer int countdownTime = 150; String get id { - return address?.toContactEncodingString(); + return address.toContactEncodingString(); } /// 下载项目的piece总数 final int _piecesNum; /// 远程的Bitfield - Bitfield _remoteBitfield; + Bitfield? _remoteBitfield; /// 该peer是否已经disposed bool _disposed = false; /// 倒计时关闭Timer - Timer _countdownTimer; + Timer? _countdownTimer; /// 对方是否choke了我,初始默认true bool _chokeMe = true; @@ -125,7 +125,7 @@ abstract class Peer /// Local Peer Id final String _localPeerId; // 本机的peer id。发送消息会用到 - String _remotePeerId; + String? _remotePeerId; /// has this peer send handshake message already? bool _handShaked = false; @@ -134,7 +134,7 @@ abstract class Peer bool _bitfieldSended = false; /// 远程数据接受,监听subcription - StreamSubscription _streamChunk; + StreamSubscription? _streamChunk; /// 从通道中获取数据的buffer List _cacheBuffer = []; @@ -169,7 +169,7 @@ abstract class Peer int reqq; - int remoteReqq; + int? remoteReqq; /// /// [_id] 是用于区分不同Peer的Id,和[_localPeerId]不同,[_localPeerId]是bt协议中的Peer_id。 @@ -186,21 +186,21 @@ abstract class Peer } factory Peer.newTCPPeer(String localPeerId, CompactAddress address, - List infoHashBuffer, int piecesNum, Socket socket, + List infoHashBuffer, int piecesNum, Socket? socket, {bool enableExtend = true, bool enableFast = true}) { return _TCPPeer(localPeerId, address, infoHashBuffer, piecesNum, socket, enableExtend: enableExtend, enableFast: enableFast); } factory Peer.newUTPPeer(String localPeerId, CompactAddress address, - List infoHashBuffer, int piecesNum, Socket socket, + List infoHashBuffer, int piecesNum, UTPSocket? socket, {bool enableExtend = true, bool enableFast = true}) { return _UTPPeer(localPeerId, address, infoHashBuffer, piecesNum, socket, enableExtend: enableExtend, enableFast: enableFast); } /// 远程的Bitfield - Bitfield get remoteBitfield => _remoteBitfield; + Bitfield? get remoteBitfield => _remoteBitfield; /// 是否已经发送local bitfield给对方 bool get bitfieldSended => _bitfieldSended; @@ -210,11 +210,11 @@ abstract class Peer /// 如果具备完整的torrent文件,那它就是一个seeder bool get isSeeder { if (_remoteBitfield == null) return false; - if (_remoteBitfield.haveAll()) return true; + if (_remoteBitfield!.haveAll()) return true; return false; } - String get remotePeerId => _remotePeerId; + String? get remotePeerId => _remotePeerId; String get localPeerId => _localPeerId; @@ -237,8 +237,8 @@ abstract class Peer } } - bool remoteHave(int index) { - return _remoteBitfield.getBit(index); + bool? remoteHave(int index) { + return _remoteBitfield?.getBit(index); } bool get interestedMe => _interestedMe; @@ -253,7 +253,7 @@ abstract class Peer /// 远程所有的已完成Piece List get remoteCompletePieces { if (_remoteBitfield == null) return []; - return _remoteBitfield.completedPieces; + return _remoteBitfield!.completedPieces ?? []; } /// Connect remote peer @@ -262,7 +262,7 @@ abstract class Peer _init(); var _stream = await connectRemote(timeout); startSpeedCalculator(); - _streamChunk = _stream.listen(_processReceiveData, onDone: () { + _streamChunk = _stream?.listen(_processReceiveData, onDone: () { _log('Connection is closed $address'); dispose(BadException('远程关闭了连接')); }, onError: (e) { @@ -296,7 +296,7 @@ abstract class Peer remoteEnableFastPeer = false; } - List removeRequest(int index, int begin, int length) { + List? removeRequest(int index, int begin, int length) { var request = _removeRequestFromBuffer(index, begin, length); return request; } @@ -312,7 +312,7 @@ abstract class Peer bool addRequest(int index, int begin, int length) { var maxCount = currentWindow; // maxCount = oldCount; - if (remoteReqq != null) maxCount = min(remoteReqq, maxCount); + if (remoteReqq != null) maxCount = min(remoteReqq!, maxCount); if (_requestBuffer.length >= maxCount) return false; _requestBuffer .add([index, begin, length, DateTime.now().microsecondsSinceEpoch, 0]); @@ -357,8 +357,8 @@ abstract class Peer var lengthBuffer = Uint8List(4); List.copyRange(lengthBuffer, 0, _cacheBuffer, start, 4); var length = ByteData.view(lengthBuffer.buffer).getInt32(0, Endian.big); - List piecesMessage; - List haveMessages; + List? piecesMessage; + List? haveMessages; while (_cacheBuffer.length - start - 4 >= length) { if (length == 0) { Timer.run(() => _processMessage(null, null)); @@ -386,10 +386,10 @@ abstract class Peer length = ByteData.view(lengthBuffer.buffer).getInt32(0, Endian.big); } if (piecesMessage != null && piecesMessage.isNotEmpty) { - Timer.run(() => _processReceivePieces(piecesMessage)); + Timer.run(() => _processReceivePieces(piecesMessage!)); } if (haveMessages != null && haveMessages.isNotEmpty) { - Timer.run(() => _processHave(haveMessages)); + Timer.run(() => _processHave(haveMessages!)); } if (start != 0) _cacheBuffer = _cacheBuffer.sublist(start); } @@ -410,7 +410,7 @@ abstract class Peer return true; } - void _processMessage(int id, Uint8List message) { + void _processMessage(int? id, Uint8List? message) { if (id == null) { _log('process keep alive $address'); fireKeepAlive(); @@ -440,11 +440,11 @@ abstract class Peer // return; // have message case ID_BITFIELD: // log('process bitfield from $address'); - initRemoteBitfield(message); + if (message != null) initRemoteBitfield(message); return; // bitfield message case ID_REQUEST: _log('process request from ${address}'); - _processRemoteRequest(message); + if (message != null) _processRemoteRequest(message); return; // request message // case ID_PIECE: // _log('process pices : $address'); @@ -452,12 +452,14 @@ abstract class Peer // return; // pices message case ID_CANCEL: _log('process cancel : $address'); - _processCancel(message); + if (message != null) _processCancel(message); return; // cancel message case ID_PORT: _log('process port : $address'); - var port = ByteData.view(message.buffer).getUint16(0); - _processPortChange(port); + if (message != null) { + var port = ByteData.view(message.buffer).getUint16(0); + _processPortChange(port); + } return; // port message case OP_HAVE_ALL: _log('process have all : $address'); @@ -469,20 +471,22 @@ abstract class Peer return; case OP_SUGGEST_PIECE: _log('process suggest pieces : $address'); - _processSuggestPiece(message); + if (message != null) _processSuggestPiece(message); return; case OP_REJECT_REQUEST: _log('process reject request : $address'); - _processRejectRequest(message); + if (message != null) _processRejectRequest(message); return; case OP_ALLOW_FAST: _log('process allow fast : $address'); - _processAllowFast(message); + if (message != null) _processAllowFast(message); return; case ID_EXTENDED: - var extid = message[0]; - message = message.sublist(1); - processExtendMessage(extid, message); + if (message != null) { + var extid = message[0]; + message = message.sublist(1); + processExtendMessage(extid, message); + } return; } } @@ -492,7 +496,7 @@ abstract class Peer /// 从requestbuffer中将request删除 /// /// 每当得到了piece回应或者request超时,都会调用此方法 - List _removeRequestFromBuffer(int index, int begin, int length) { + List? _removeRequestFromBuffer(int index, int begin, int length) { var i = _findRequestIndexFromBuffer(index, begin, length); if (i != -1) { return _requestBuffer.removeAt(i); @@ -557,13 +561,14 @@ abstract class Peer dispose('Remote disabled fast extension but receive \'have all\''); return; } - for (var i = 0; i < _remoteBitfield.buffer.length - 1; i++) { - _remoteBitfield.buffer[i] = 255; + if (_remoteBitfield == null) return; + for (var i = 0; i < _remoteBitfield!.buffer.length - 1; i++) { + _remoteBitfield?.buffer[i] = 255; } - var index = _remoteBitfield.buffer.length - 1; + var index = _remoteBitfield!.buffer.length - 1; index = index * 8; - for (var i = index; i < _remoteBitfield.piecesNum; i++) { - _remoteBitfield.setBit(i, true); + for (var i = index; i < _remoteBitfield!.piecesNum; i++) { + _remoteBitfield?.setBit(i, true); } fireRemoteHaveAll(); } @@ -706,7 +711,7 @@ abstract class Peer /// 更新远程Bitfield void updateRemoteBitfield(int index, bool have) { - _remoteBitfield.setBit(index, have); + _remoteBitfield?.setBit(index, have); } void initRemoteBitfield(Uint8List bitfield) { @@ -733,7 +738,7 @@ abstract class Peer } } - String _parseRemotePeerId(dynamic data) { + String? _parseRemotePeerId(dynamic data) { if (data is List) { return String.fromCharCodes(data.sublist(48, 68)); } @@ -745,12 +750,12 @@ abstract class Peer /// [timeout] defaul value is 30 seconds /// Different type peer use different protocol , such as TCP,uTP, /// so this method should be implemented by sub-class - Future connectRemote(int timeout); + Future connectRemote(int timeout); /// Send message to remote /// /// this method will transform the [message] and id to be the peer protocol message bytes - void sendMessage(int id, [List message]) { + void sendMessage(int? id, [List? message]) { if (isDisposed) return; if (id == null) { // it's keep alive @@ -763,7 +768,7 @@ abstract class Peer _startToCountdown(); } - List _createByteMessage(int id, List message) { + List _createByteMessage(int id, List? message) { var length = 0; if (message != null) length = message.length; length = length + 1; @@ -845,7 +850,7 @@ abstract class Peer return false; } } - int requestIndex; + int? requestIndex; for (var i = 0; i < _remoteRequestBuffer.length; i++) { var r = _remoteRequestBuffer[i]; if (r[0] == index && r[1] == begin) { @@ -1174,7 +1179,7 @@ class TCPConnectException implements Exception { } class _TCPPeer extends Peer { - Socket _socket; + Socket? _socket; _TCPPeer(String localPeerId, CompactAddress address, List infoHashBuffer, int piecesNum, this._socket, {bool enableExtend = true, bool enableFast = true}) @@ -1184,13 +1189,13 @@ class _TCPPeer extends Peer { localEnableFastPeer: enableFast); @override - Future connectRemote(int timeout) async { + Future connectRemote(int? timeout) async { timeout ??= 30; try { _socket ??= await Socket.connect(address.address, address.port, timeout: Duration(seconds: timeout)); return _socket; - } catch (e) { + } on Exception catch (e) { throw TCPConnectException(e); } } @@ -1223,8 +1228,8 @@ class _TCPPeer extends Peer { /// actually , one UTPSocketClient should maintain several uTP socket(uTP peer), /// this class need to improve. class _UTPPeer extends Peer { - UTPSocketClient _client; - UTPSocket _socket; + UTPSocketClient? _client; + UTPSocket? _socket; _UTPPeer(String localPeerId, CompactAddress address, List infoHashBuffer, int piecesNum, this._socket, {bool enableExtend = true, bool enableFast = true}) @@ -1234,10 +1239,10 @@ class _UTPPeer extends Peer { localEnableFastPeer: enableFast); @override - Future connectRemote(int timeout) async { + Future connectRemote(int timeout) async { if (_socket != null) return _socket; _client ??= UTPSocketClient(); - _socket = await _client.connect(address.address, address.port); + _socket = await _client?.connect(address.address, address.port); return _socket; } diff --git a/lib/src/peer/peer_event_dispatcher.dart b/lib/src/peer/peer_event_dispatcher.dart index e849c2a..68ac302 100644 --- a/lib/src/peer/peer_event_dispatcher.dart +++ b/lib/src/peer/peer_event_dispatcher.dart @@ -57,7 +57,7 @@ mixin PeerEventDispatcher { }); } - void fireHandshakeEvent(String remotePeerId, dynamic data) { + void fireHandshakeEvent(String? remotePeerId, dynamic data) { var fSet = _handleFunctions[PEER_EVENT_HANDSHAKE]; fSet?.forEach((f) { Timer.run(() => f(this, remotePeerId, data)); @@ -92,7 +92,7 @@ mixin PeerEventDispatcher { }); } - void fireBitfield(final Bitfield bitfield) { + void fireBitfield(final Bitfield? bitfield) { var fSet = _handleFunctions[PEER_EVENT_BITFIELD]; fSet?.forEach((f) { Timer.run(() => f(this, bitfield)); diff --git a/lib/src/peer/peers_manager.dart b/lib/src/peer/peers_manager.dart index 48f8976..1842b68 100644 --- a/lib/src/peer/peers_manager.dart +++ b/lib/src/peer/peers_manager.dart @@ -25,8 +25,8 @@ const MAX_UPLOADED_NOTIFY_SIZE = 1024 * 1024 * 10; // 10 mb /// - 没有处理对外的Suggest Piece/Fast Allow class PeersManager with Holepunch, PEX { final List IGNORE_IPS = [ - InternetAddress.tryParse('0.0.0.0'), - InternetAddress.tryParse('127.0.0.1') + InternetAddress.tryParse('0.0.0.0')!, + InternetAddress.tryParse('127.0.0.1')! ]; bool _disposed = false; @@ -39,7 +39,7 @@ class PeersManager with Holepunch, PEX { final Set _incomingAddress = {}; - InternetAddress localExtenelIP; + InternetAddress? localExtenelIP; /// 写入磁盘的缓存最大值 int maxWriteBufferSize; @@ -56,9 +56,9 @@ class PeersManager with Holepunch, PEX { int _downloaded = 0; - int _startedTime; + int? _startedTime; - int _endTime; + int? _endTime; int _uploadedNotifySize = 0; @@ -72,7 +72,7 @@ class PeersManager with Holepunch, PEX { bool _paused = false; - Timer _keepAliveTimer; + Timer? _keepAliveTimer; final List _pausedRequest = []; @@ -83,9 +83,6 @@ class PeersManager with Holepunch, PEX { PeersManager(this._localPeerId, this._pieceManager, this._pieceProvider, this._fileManager, this._metaInfo, [this.maxWriteBufferSize = MAX_WRITE_BUFFER_SIZE]) { - assert(_pieceManager != null && - _pieceProvider != null && - _fileManager != null); // hook FileManager and PieceManager _fileManager.onSubPieceWriteComplete(_processSubPieceWriteComplte); _fileManager.onSubPieceReadComplete(readSubPieceComplete); @@ -128,9 +125,9 @@ class PeersManager with Holepunch, PEX { /// the end time is when manager was disposed. int get liveTime { if (_startedTime == null) return 0; - var passed = DateTime.now().millisecondsSinceEpoch - _startedTime; + var passed = DateTime.now().millisecondsSinceEpoch - _startedTime!; if (_endTime != null) { - passed = _endTime - _startedTime; + passed = _endTime! - _startedTime!; } return passed; } @@ -251,8 +248,8 @@ class PeersManager with Holepunch, PEX { /// /// Usually [socket] is null , unless this peer was incoming connection, but /// this type peer was managed by [TorrentTask] , user don't need to know that. - void addNewPeerAddress(CompactAddress address, - [PeerType type = PeerType.TCP, Socket socket]) { + void addNewPeerAddress(CompactAddress? address, + [PeerType type = PeerType.TCP, dynamic socket]) { if (address == null) return; if (address.address == localExtenelIP) return; if (socket != null) { @@ -262,7 +259,7 @@ class PeersManager with Holepunch, PEX { } } if (_peersAddress.add(address)) { - Peer peer; + Peer? peer; if (type == PeerType.TCP) { peer = Peer.newTCPPeer(_localPeerId, address, _metaInfo.infoHashBuffer, _metaInfo.pieces.length, socket); @@ -446,10 +443,10 @@ class PeersManager with Holepunch, PEX { } var peer = source as Peer; - Piece piece; + Piece? piece; if (pieceIndex != -1) { piece = _pieceProvider[pieceIndex]; - if (!piece.haveAvalidateSubPiece()) { + if (piece != null && !piece.haveAvalidateSubPiece()) { piece = _pieceManager.selectPiece(peer.id, peer.remoteCompletePieces, _pieceProvider, peer.remoteSuggestPieces); } @@ -459,7 +456,7 @@ class PeersManager with Holepunch, PEX { } if (piece == null) return; - var subIndex = piece.popSubPiece(); + var subIndex = piece.popSubPiece()!; var size = DEFAULT_REQUEST_LENGTH; // block大小现算 var begin = subIndex * size; if ((begin + size) > piece.byteLength) { @@ -497,7 +494,7 @@ class PeersManager with Holepunch, PEX { var peer = source as Peer; _pausedRemoteRequest[peer.id] ??= []; var pausedRequest = _pausedRemoteRequest[peer.id]; - pausedRequest.add([source, index, begin, length]); + pausedRequest?.add([source, index, begin, length]); return; } var peer = source as Peer; @@ -514,7 +511,7 @@ class PeersManager with Holepunch, PEX { _processBitfieldUpdate(source, null); } - void _processBitfieldUpdate(dynamic source, Bitfield bitfield) { + void _processBitfieldUpdate(dynamic source, Bitfield? bitfield) { var peer = source as Peer; if (bitfield != null) { if (peer.interestedRemote) return; @@ -604,7 +601,7 @@ class PeersManager with Holepunch, PEX { } void _sendKeepAliveToAll() { - _activePeers?.forEach((peer) { + _activePeers.forEach((peer) { Timer.run(() => _keepAlive(peer)); }); } @@ -654,7 +651,7 @@ class PeersManager with Holepunch, PEX { } Future disposeAllSeeder([dynamic reason]) async { - _activePeers?.forEach((peer) async { + _activePeers.forEach((peer) async { if (peer.isSeeder) { await peer.dispose(reason); } diff --git a/lib/src/peer/pex.dart b/lib/src/peer/pex.dart index 8b3a487..f04b28d 100644 --- a/lib/src/peer/pex.dart +++ b/lib/src/peer/pex.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:io'; +import 'dart:typed_data'; import 'package:bencode_dart/bencode_dart.dart'; import 'package:dartorrent_common/dartorrent_common.dart'; @@ -17,7 +18,7 @@ const pex_flag_supports_holepunch = 0x08; const pex_flag_reachable = 0x10; mixin PEX { - Timer _timer; + Timer? _timer; final Set _lastUTPEX = {}; @@ -61,7 +62,7 @@ mixin PEX { } dynamic parsePEXDatas(dynamic source, List message) { - var datas = decode(message); + var datas = decode(Uint8List.fromList(message)); _parseAdded(source, datas); _parseAdded(source, datas, 'added6', InternetAddressType.IPv6); } @@ -74,7 +75,7 @@ mixin PEX { if (added is! List) { added = _convert(added); } - List ips; + List? ips; try { if (type == InternetAddressType.IPv4) { ips = CompactAddress.parseIPv4Addresses(added); @@ -111,7 +112,7 @@ mixin PEX { if (f & pex_flag_reachable == pex_flag_reachable) { opts['reachable'] = true; } - Timer.run(() => addPEXPeer(source, ips[i], opts)); + Timer.run(() => addPEXPeer(source, ips?[i], opts)); } } } @@ -120,7 +121,7 @@ mixin PEX { void addPEXPeer(dynamic source, CompactAddress address, Map options); - List _convert(List added) { + List? _convert(List added) { var intList = []; for (var i = 0; i < added.length; i++) { var n = added[i]; diff --git a/lib/src/peer/speed_calculator.dart b/lib/src/peer/speed_calculator.dart index 391e165..6ae2bff 100644 --- a/lib/src/peer/speed_calculator.dart +++ b/lib/src/peer/speed_calculator.dart @@ -12,7 +12,7 @@ mixin SpeedCalculator { if (_downloadedHistory.isEmpty) return 0.0; var now = DateTime.now().microsecondsSinceEpoch; var d = 0; - int s; + int? s; for (var i = 0; i < _downloadedHistory.length;) { var dd = _downloadedHistory[i]; if ((now - dd[1]) > RECORD_TIME) { @@ -25,7 +25,7 @@ mixin SpeedCalculator { } } if (d == 0) return 0.0; - var passed = now - s; + var passed = now - s!; if (passed == 0) return 0.0; return (d / 1024) / (passed / 1000000); } @@ -45,16 +45,16 @@ mixin SpeedCalculator { } /// 从连接开始,直到peer销毁之前所持续时间 - int get livingTime { + int? get livingTime { if (_startTime == null) return null; var e = _endTime; e ??= DateTime.now().microsecondsSinceEpoch; - return e - _startTime; + return e - _startTime!; } - int _startTime; + int? _startTime; - int _endTime; + int? _endTime; int _downloaded = 0; diff --git a/lib/src/piece/base_piece_selector.dart b/lib/src/piece/base_piece_selector.dart index a3ce671..63a769e 100644 --- a/lib/src/piece/base_piece_selector.dart +++ b/lib/src/piece/base_piece_selector.dart @@ -14,7 +14,7 @@ import 'piece_selector.dart'; /// - 在可用`Peer`数量都相同的情况下,选用`Sub Piece`数量最少的 class BasePieceSelector implements PieceSelector { @override - Piece selectPiece( + Piece? selectPiece( String remotePeerId, List piecesIndexList, PieceProvider provider, [bool random = false]) { // random = true; diff --git a/lib/src/piece/piece.dart b/lib/src/piece/piece.dart index 9535661..660e4b9 100644 --- a/lib/src/piece/piece.dart +++ b/lib/src/piece/piece.dart @@ -11,7 +11,7 @@ class Piece { final Set _avalidatePeers = {}; - Queue _subPiecesQueue; + late Queue _subPiecesQueue; final Set _downloadedSubPieces = {}; @@ -86,7 +86,6 @@ class Piece { return _writtingSubPieces.add(subindex); } - bool subPieceWriteComplete(int begin) { var subindex = begin ~/ DEFAULT_REQUEST_LENGTH; // _subPiecesQueue.remove(subindex); // 有这可能? @@ -103,7 +102,7 @@ class Piece { /// ///当子Piece被弹出栈用于下载,或者子Piece已经下载完成,那么就视为该Piece已经不再包含该子Piece bool containsSubpiece(int subIndex) { - return subPieceQueue?.contains(subIndex); + return subPieceQueue.contains(subIndex); } bool containsAvalidatePeer(String id) { @@ -111,7 +110,7 @@ class Piece { } bool removeSubpiece(int subIndex) { - return subPieceQueue?.remove(subIndex); + return subPieceQueue.remove(subIndex); } bool addAvalidatePeer(String id) { @@ -126,7 +125,7 @@ class Piece { _avalidatePeers.clear(); } - int popSubPiece() { + int? popSubPiece() { if (subPieceQueue.isNotEmpty) return subPieceQueue.removeFirst(); return null; } @@ -139,7 +138,7 @@ class Piece { return true; } - int popLastSubPiece() { + int? popLastSubPiece() { if (subPieceQueue.isNotEmpty) return subPieceQueue.removeLast(); return null; } diff --git a/lib/src/piece/piece_manager.dart b/lib/src/piece/piece_manager.dart index 63d11c9..598044e 100644 --- a/lib/src/piece/piece_manager.dart +++ b/lib/src/piece/piece_manager.dart @@ -34,7 +34,7 @@ class PieceManager implements PieceProvider { for (var i = 0; i < metaInfo.pieces.length; i++) { var byteLength = metaInfo.pieceLength; if (i == metaInfo.pieces.length - 1) { - byteLength = metaInfo.lastPriceLength; + byteLength = metaInfo.lastPieceLength; } var piece = Piece(metaInfo.pieces[i], i, byteLength); if (!bitfield.getBit(i)) _pieces[i] = piece; @@ -63,8 +63,8 @@ class PieceManager implements PieceProvider { } } - Piece selectPiece(String remotePeerId, List remoteHavePieces, - PieceProvider provider, final Set suggestPieces) { + Piece? selectPiece(String remotePeerId, List remoteHavePieces, + PieceProvider provider, final Set? suggestPieces) { // 查看当前下载piece中是否可以使用该peer var avalidatePiece = []; // 优先下载Suggest Pieces @@ -133,7 +133,7 @@ class PieceManager implements PieceProvider { } @override - Piece operator [](index) { + Piece? operator [](index) { return _pieces[index]; } diff --git a/lib/src/piece/piece_provider.dart b/lib/src/piece/piece_provider.dart index d3ccd5d..6b88bc3 100644 --- a/lib/src/piece/piece_provider.dart +++ b/lib/src/piece/piece_provider.dart @@ -3,7 +3,7 @@ import 'piece.dart'; abstract class PieceProvider { // Piece getPiece(int index); - Piece operator [](int index); + Piece? operator [](int index); int get length; } diff --git a/lib/src/piece/piece_selector.dart b/lib/src/piece/piece_selector.dart index 9632e85..4e2eb5c 100644 --- a/lib/src/piece/piece_selector.dart +++ b/lib/src/piece/piece_selector.dart @@ -12,7 +12,7 @@ abstract class PieceSelector { /// 该方法通过[provider]以及[piecesIndexList]获取对应的`Piece`对象,并在[piecesIndexList] /// 集合中进行筛选。 /// - Piece selectPiece( + Piece? selectPiece( String remotePeerId, List piecesIndexList, PieceProvider provider, [bool first = false]); } diff --git a/lib/src/task.dart b/lib/src/task.dart index 2efed44..5a1abc1 100644 --- a/lib/src/task.dart +++ b/lib/src/task.dart @@ -52,7 +52,7 @@ abstract class TorrentTask { int get utpPeerCount; /// Downloaded total bytes length - int get downloaded; + int? get downloaded; /// Downloaded percent double get progress; @@ -117,19 +117,19 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { final Set _pauseHandlers = {}; - TorrentAnnounceTracker _tracker; + TorrentAnnounceTracker? _tracker; - DHT _dht; + DHT? _dht = DHT(); - LSD _lsd; + LSD? _lsd; - StateFile _stateFile; + StateFile? _stateFile; - PieceManager _pieceManager; + PieceManager? _pieceManager; - DownloadFileManager _fileManager; + DownloadFileManager? _fileManager; - PeersManager _peersManager; + PeersManager? _peersManager; final Torrent _metaInfo; @@ -137,9 +137,9 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { final Set _peerIds = {}; - String _peerId; // 这个是生成的本地peer的id,和Peer类的id是两回事 + late String _peerId; // 这个是生成的本地peer的id,和Peer类的id是两回事 - ServerSocket _serverSocket; + ServerSocket? _serverSocket; final Set _cominIp = {}; @@ -152,7 +152,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { @override double get averageDownloadSpeed { if (_peersManager != null) { - return _peersManager.averageDownloadSpeed; + return _peersManager!.averageDownloadSpeed; } else { return 0.0; } @@ -161,7 +161,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { @override double get averageUploadSpeed { if (_peersManager != null) { - return _peersManager.averageUploadSpeed; + return _peersManager!.averageUploadSpeed; } else { return 0.0; } @@ -170,7 +170,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { @override double get currentDownloadSpeed { if (_peersManager != null) { - return _peersManager.currentDownloadSpeed; + return _peersManager!.currentDownloadSpeed; } else { return 0.0; } @@ -179,40 +179,40 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { @override double get uploadSpeed { if (_peersManager != null) { - return _peersManager.uploadSpeed; + return _peersManager!.uploadSpeed; } else { return 0.0; } } - String _infoHashString; + late String _infoHashString; - Timer _dhtRepeatTimer; + Timer? _dhtRepeatTimer; Future _init(Torrent model, String savePath) async { - _dht = DHT(); _lsd = LSD(model.infoHash, _peerId); _infoHashString = String.fromCharCodes(model.infoHashBuffer); _tracker ??= TorrentAnnounceTracker(this); _stateFile ??= await StateFile.getStateFile(savePath, model); _pieceManager ??= PieceManager.createPieceManager( - BasePieceSelector(), model, _stateFile.bitfield); + BasePieceSelector(), model, _stateFile!.bitfield); _fileManager ??= await DownloadFileManager.createFileManager( - model, savePath, _stateFile); + model, savePath, _stateFile!); _peersManager ??= PeersManager( - _peerId, _pieceManager, _pieceManager, _fileManager, model); - return _peersManager; + _peerId, _pieceManager!, _pieceManager!, _fileManager!, model); + return _peersManager!; } @override void addPeer(CompactAddress address, - [PeerType type = PeerType.TCP, Socket socket]) { - _peersManager.addNewPeerAddress(address, type, socket); + [PeerType type = PeerType.TCP, Socket? socket]) { + _peersManager?.addNewPeerAddress(address, type, socket); } void _whenTaskDownloadComplete() async { - await _peersManager.disposeAllSeeder('Download complete,disconnect seeder'); - await _tracker.complete(); + await _peersManager + ?.disposeAllSeeder('Download complete,disconnect seeder'); + await _tracker?.complete(); _fireTaskComplete(); } @@ -220,7 +220,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { _fireFileComplete(filePath); } - void _processTrackerPeerEvent(Tracker source, PeerEvent event) { + void _processTrackerPeerEvent(Tracker source, PeerEvent? event) { if (event == null) return; var ps = event.peers; if (ps != null && ps.isNotEmpty) { @@ -235,7 +235,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { } void _processNewPeerFound(CompactAddress url) { - _peersManager.addNewPeerAddress(url); + _peersManager?.addNewPeerAddress(url); } void _processDHTPeer(CompactAddress peer, String infoHash) { @@ -255,7 +255,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { } log('incoming connect: ${socket.remoteAddress.address}:${socket.remotePort}', name: runtimeType.toString()); - _peersManager.addNewPeerAddress( + _peersManager?.addNewPeerAddress( CompactAddress(socket.address, socket.port), PeerType.TCP, socket); } @@ -284,38 +284,38 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { // 进入的peer: _serverSocket ??= await ServerSocket.bind(InternetAddress.anyIPv4, 0); await _init(_metaInfo, _savePath); - _serverSocket.listen(_hookInPeer); + _serverSocket?.listen(_hookInPeer); // _utpServer ??= await ServerUTPSocket.bind(InternetAddress.anyIPv4, 0); // _utpServer.listen(_hookUTP); // print(_utpServer.port); var map = {}; map['name'] = _metaInfo.name; - map['tcp_socket'] = _serverSocket.port; - map['comoplete_pieces'] = List.from(_stateFile.bitfield.completedPieces); - map['total_pieces_num'] = _stateFile.bitfield.piecesNum; - map['downloaded'] = _stateFile.downloaded; - map['uploaded'] = _stateFile.uploaded; + map['tcp_socket'] = _serverSocket?.port; + map['comoplete_pieces'] = List.from(_stateFile!.bitfield.completedPieces); + map['total_pieces_num'] = _stateFile!.bitfield.piecesNum; + map['downloaded'] = _stateFile!.downloaded; + map['uploaded'] = _stateFile!.uploaded; map['total_length'] = _metaInfo.length; // 主动访问的peer: - _tracker.onPeerEvent(_processTrackerPeerEvent); - _peersManager.onAllComplete(_whenTaskDownloadComplete); - _fileManager.onFileComplete(_whenFileDownloadComplete); + _tracker?.onPeerEvent(_processTrackerPeerEvent); + _peersManager?.onAllComplete(_whenTaskDownloadComplete); + _fileManager?.onFileComplete(_whenFileDownloadComplete); - _lsd.onLSDPeer(_processLSDPeerEvent); - _lsd.port = _serverSocket.port; - _lsd.start(); + _lsd?.onLSDPeer(_processLSDPeerEvent); + _lsd?.port = _serverSocket?.port; + _lsd?.start(); - _dht.announce( - String.fromCharCodes(_metaInfo.infoHashBuffer), _serverSocket.port); - _dht.onNewPeer(_processDHTPeer); + _dht?.announce( + String.fromCharCodes(_metaInfo.infoHashBuffer), _serverSocket!.port); + _dht?.onNewPeer(_processDHTPeer); // ignore: unawaited_futures - _dht.bootstrap(); - if (_fileManager.isAllComplete) { + _dht?.bootstrap(); + if (_fileManager != null && _fileManager!.isAllComplete) { // ignore: unawaited_futures - _tracker.complete(); + _tracker?.complete(); } else { - _tracker.runTrackers(_metaInfo.announces, _metaInfo.infoHashBuffer, + _tracker?.runTrackers(_metaInfo.announces, _metaInfo.infoHashBuffer, event: EVENT_STARTED); } return map; @@ -324,7 +324,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { @override Future stop([bool force = false]) async { await _tracker?.stop(force); - var tempHandler = Set.from(_stopHandlers); + Set? tempHandler = Set.from(_stopHandlers); await dispose(); tempHandler.forEach((element) { Timer.run(() => element()); @@ -368,7 +368,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { var map = { 'downloaded': _stateFile?.downloaded, 'uploaded': _stateFile?.uploaded, - 'left': _metaInfo.length - _stateFile.downloaded, + 'left': _metaInfo.length - _stateFile!.downloaded, 'numwant': 50, 'compact': 1, 'peerId': _peerId, @@ -440,7 +440,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { } @override - int get downloaded => _fileManager?.downloaded; + int? get downloaded => _fileManager?.downloaded; @override double get progress { @@ -466,7 +466,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { @override int get allPeersNumber { if (_peersManager != null) { - return _peersManager.peersNumber; + return _peersManager!.peersNumber; } else { return 0; } @@ -480,7 +480,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { @override int get connectedPeersNumber { if (_peersManager != null) { - return _peersManager.connectedPeersNumber; + return _peersManager!.connectedPeersNumber; } else { return 0; } @@ -489,7 +489,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { @override int get seederNumber { if (_peersManager != null) { - return _peersManager.seederNumber; + return _peersManager!.seederNumber; } else { return 0; } @@ -499,21 +499,21 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { @override double get utpDownloadSpeed { if (_peersManager == null) return 0.0; - return _peersManager.utpDownloadSpeed; + return _peersManager!.utpDownloadSpeed; } // TODO debug: @override double get utpUploadSpeed { if (_peersManager == null) return 0.0; - return _peersManager.utpUploadSpeed; + return _peersManager!.utpUploadSpeed; } // TODO debug: @override int get utpPeerCount { if (_peersManager == null) return 0; - return _peersManager.utpPeerCount; + return _peersManager!.utpPeerCount; } @override diff --git a/lib/src/utils.dart b/lib/src/utils.dart index d0bc0bb..90e7f5c 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -11,7 +11,7 @@ String generatePeerId([String prefix = ID_PREFIX]) { return id; } -List hexString2Buffer(String hexStr) { +List? hexString2Buffer(String hexStr) { // ignore: prefer_is_empty if (hexStr.isEmpty || hexStr.length.remainder(2) != 0) return null; var size = hexStr.length ~/ 2; diff --git a/lib/torrent_task.dart b/lib/torrent_task.dart index dc025e7..21d0191 100644 --- a/lib/torrent_task.dart +++ b/lib/torrent_task.dart @@ -11,7 +11,7 @@ export 'src/peer/peer_base.dart'; const ID_PREFIX = '-DT0201-'; /// 当前版本号 -Future getTorrenTaskVersion() async { +Future getTorrenTaskVersion() async { var file = File('pubspec.yaml'); if (await file.exists()) { var lines = await file.readAsLines(); diff --git a/pubspec.yaml b/pubspec.yaml index 176e617..ffafbcc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,24 +6,16 @@ issue_tracker: https://github.com/eclipseglory/torrent_task/issues publish_to: https://pub.dev/ environment: - sdk: ">=2.10.0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: - # path: ^1.7.0 - torrent_model: ">=1.0.3 <2.0.0" - torrent_tracker: ">=1.3.12 <2.0.0" - - # torrent_tracker: - # path: '../../dd/torrent_tracker' - - dht_dart: ">=0.0.7 <1.0.0" - dartorrent_common: ">=1.0.3 <2.0.0" - bencode_dart: ^1.0.2 - utp: ">=1.0.1 <2.0.0" - # utp: - # path: '../../dd/utp' + torrent_model: ^1.0.4 + torrent_tracker: ^1.3.13 + dht_dart: ^0.0.8 + dartorrent_common: ^1.0.4 + bencode_dart: ^1.0.3 + utp: ^1.0.1 dev_dependencies: pedantic: ^1.9.0 test: ^1.14.4 - meta: 1.2.4 diff --git a/test/holepunch_test.dart b/test/holepunch_test.dart new file mode 100644 index 0000000..ab73b3a --- /dev/null +++ b/test/holepunch_test.dart @@ -0,0 +1 @@ +void main() {} diff --git a/test/peer_communicate.dart b/test/peer_communicate.dart index 8b021fc..9ea1466 100644 --- a/test/peer_communicate.dart +++ b/test/peer_communicate.dart @@ -7,7 +7,7 @@ import 'package:dartorrent_common/dartorrent_common.dart'; import 'package:torrent_task/torrent_task.dart'; void main() async { - ServerSocket serverSocket; + ServerSocket? serverSocket; int serverPort; var infoBuffer = randomBytes(20); var piecesNum = 20; @@ -140,7 +140,7 @@ void main() async { var pid = generatePeerId(); var peer = Peer.newTCPPeer( pid, - CompactAddress(InternetAddress.tryParse('127.0.0.1'), serverPort), + CompactAddress(InternetAddress.tryParse('127.0.0.1')!, serverPort), infoBuffer, piecesNum, null); @@ -197,8 +197,8 @@ void main() async { print('come out destroyed : $reason'); await serverSocket?.close(); serverSocket = null; - var callAll = callMap.values - .fold(true, (previousValue, element) => (previousValue && element)); + var callAll = callMap.values.fold( + true, (previousValue, element) => (previousValue && element)); assert(callAll); }); print('connect to : ${peer.address}'); diff --git a/test/torrent_client_test.dart b/test/torrent_client_test.dart index 866670b..6f05351 100644 --- a/test/torrent_client_test.dart +++ b/test/torrent_client_test.dart @@ -11,7 +11,7 @@ import 'package:torrent_task/torrent_task.dart'; void main() { group('Bitfield test - ', () { - Bitfield bitfield; + Bitfield? bitfield; var pieces = 123; // 不要给8的倍数 setUp(() { bitfield = Bitfield.createEmptyBitfield(pieces); @@ -21,8 +21,8 @@ void main() { var c = pieces ~/ 8; if (c * 8 != pieces) c++; print('check buffer length'); - assert(bitfield.buffer.length == c); - bitfield.buffer.forEach((element) { + assert(bitfield!.buffer.length == c); + bitfield!.buffer.forEach((element) { assert(element == 0); }); }); @@ -33,38 +33,38 @@ void main() { for (var i = 0; i < pieces; i++) { var index = t.nextInt(pieces * 2); if (index >= pieces) { - bitfield.setBit(index, true); - assert(bitfield.getBit(index) == false); - bitfield.setBit(index, false); - assert(bitfield.getBit(index) == false); + bitfield!.setBit(index, true); + assert(bitfield!.getBit(index) == false); + bitfield!.setBit(index, false); + assert(bitfield!.getBit(index) == false); } else { randomIndex.add(index); - bitfield.setBit(index, true); - assert(bitfield.getBit(index) == true); + bitfield!.setBit(index, true); + assert(bitfield!.getBit(index) == true); } } var indexList = randomIndex.toList(); indexList.sort((a, b) => a - b); - var list = bitfield.completedPieces; + var list = bitfield!.completedPieces; print('Check completed index list...'); for (var i = 0; i < list.length; i++) { indexList.remove(list[i]); } assert(indexList.isEmpty); - assert(bitfield.haveCompletePiece()); + assert(bitfield!.haveCompletePiece()); print('Check bitfield value...'); list.forEach((index) { - assert(bitfield.getBit(index)); + assert(bitfield!.getBit(index)); }); var tempList = []; tempList.addAll(list); tempList.forEach((index) { - bitfield.setBit(index, false); + bitfield!.setBit(index, false); }); print('Clean all bitfield...'); - bitfield.buffer.forEach((element) { + bitfield!.buffer.forEach((element) { assert(element == 0); }); }); @@ -75,27 +75,27 @@ void main() { for (var i = 0; i < pieces; i++) { var index = t.nextInt(pieces * 2); if (index >= pieces) { - bitfield.setBit(index, true); - assert(bitfield.getBit(index) == false); - bitfield.setBit(index, false); - assert(bitfield.getBit(index) == false); + bitfield!.setBit(index, true); + assert(bitfield!.getBit(index) == false); + bitfield!.setBit(index, false); + assert(bitfield!.getBit(index) == false); } else { randomIndex.add(index); - bitfield.setBit(index, true); - assert(bitfield.getBit(index) == true); + bitfield!.setBit(index, true); + assert(bitfield!.getBit(index) == true); } } - bitfield.completedPieces; - bitfield.setBit(t.nextInt(pieces * 2), true); - var length = bitfield.completedPieces.length; - bitfield.setBit(bitfield.completedPieces.last, true); - assert(length == bitfield.completedPieces.length); - bitfield.setBit(bitfield.completedPieces.first, true); - assert(length == bitfield.completedPieces.length); - var first = bitfield.completedPieces.first; - bitfield.setBit(bitfield.completedPieces.first, false); - assert(!bitfield.completedPieces.contains(first)); + bitfield!.completedPieces; + bitfield!.setBit(t.nextInt(pieces * 2), true); + var length = bitfield!.completedPieces.length; + bitfield!.setBit(bitfield!.completedPieces.last, true); + assert(length == bitfield!.completedPieces.length); + bitfield!.setBit(bitfield!.completedPieces.first, true); + assert(length == bitfield!.completedPieces.length); + var first = bitfield!.completedPieces.first; + bitfield!.setBit(bitfield!.completedPieces.first, false); + assert(!bitfield!.completedPieces.contains(first)); }); }); @@ -107,9 +107,10 @@ void main() { totalsize = totalsize + remain; var p = Piece('aaaaaaa', 0, totalsize); var size = DEFAULT_REQUEST_LENGTH; - var subIndex = await p.popSubPiece(); + var subIndex = p.popSubPiece(); subIndex = p.popLastSubPiece(); - var begin = subIndex * size; + assert(subIndex != null); + var begin = subIndex! * size; if ((begin + size) > p.byteLength) { size = p.byteLength - begin; assert(remain == size); @@ -128,12 +129,11 @@ void main() { group('test same piece find - ', () { var pieces = 123; // 不要给8的倍数 - var bitfieldList = List(10); + List bitfieldList = []; // 模拟多个peer的bitfield: setUp(() { - for (var i = 0; i < bitfieldList.length; i++) { - bitfieldList[i] = Bitfield.createEmptyBitfield(pieces); - } + bitfieldList = + List.generate(10, (index) => Bitfield.createEmptyBitfield(pieces)); bitfieldList.forEach((bitfield) { var t = Random(); @@ -161,29 +161,29 @@ void main() { group('StateFile Test - ', () { var directory = 'test'; - Torrent torrent; + Torrent? torrent; setUpAll(() async { torrent = await Torrent.parse( '$directory${Platform.pathSeparator}test4.torrent'); var f = File( - '$directory${Platform.pathSeparator}${torrent.infoHash}.bt.state'); + '$directory${Platform.pathSeparator}${torrent!.infoHash}.bt.state'); if (await f.exists()) await f.delete(); }); test('Write/Read StateFile', () async { - var stateFile = await StateFile.getStateFile(directory, torrent); - var b = torrent.pieces.length ~/ 8; - if (b * 8 != torrent.pieces.length) b++; + var stateFile = await StateFile.getStateFile(directory, torrent!); + var b = torrent!.pieces.length ~/ 8; + if (b * 8 != torrent!.pieces.length) b++; assert(stateFile.bitfield.length == b); - assert(stateFile.bitfield.piecesNum == torrent.pieces.length); + assert(stateFile.bitfield.piecesNum == torrent!.pieces.length); assert(!stateFile.bitfield.haveCompletePiece()); await stateFile.close(); // 测试建立空文件后读取内容 - stateFile = await StateFile.getStateFile(directory, torrent); - b = torrent.pieces.length ~/ 8; - if (b * 8 != torrent.pieces.length) b++; + stateFile = await StateFile.getStateFile(directory, torrent!); + b = torrent!.pieces.length ~/ 8; + if (b * 8 != torrent!.pieces.length) b++; assert(stateFile.bitfield.length == b); - assert(stateFile.bitfield.piecesNum == torrent.pieces.length); + assert(stateFile.bitfield.piecesNum == torrent!.pieces.length); assert(!stateFile.bitfield.haveCompletePiece()); assert(stateFile.downloaded == 0); assert(stateFile.uploaded == 0); @@ -206,7 +206,7 @@ void main() { await stateFile.close(); await stateFile.close(); //关闭两次会怎样? var f = File( - '$directory${Platform.pathSeparator}${torrent.infoHash}.bt.state'); + '$directory${Platform.pathSeparator}${torrent!.infoHash}.bt.state'); var locker = Completer(); var data = []; f.openRead().listen((event) { @@ -225,12 +225,12 @@ void main() { }, onError: (e) => locker.complete()); await locker.future; - stateFile = await StateFile.getStateFile(directory, torrent); - b = torrent.pieces.length ~/ 8; - if (b * 8 != torrent.pieces.length) b++; + stateFile = await StateFile.getStateFile(directory, torrent!); + b = torrent!.pieces.length ~/ 8; + if (b * 8 != torrent!.pieces.length) b++; assert(stateFile.bitfield.length == b); - var sd = stateFile.bitfield.completedPieces.length * torrent.pieceLength; - sd = sd - (torrent.pieceLength - torrent.lastPriceLength); + var sd = stateFile.bitfield.completedPieces.length * torrent!.pieceLength; + sd = sd - (torrent!.pieceLength - torrent!.lastPieceLength); assert(stateFile.downloaded == sd); print('download: $sd'); assert(stateFile.uploaded == 987654321); @@ -241,9 +241,9 @@ void main() { }); test('Delete StateFile', () async { - var stateFile = await StateFile.getStateFile(directory, torrent); + var stateFile = await StateFile.getStateFile(directory, torrent!); var t = File( - '$directory${Platform.pathSeparator}${torrent.infoHash}.bt.state'); + '$directory${Platform.pathSeparator}${torrent!.infoHash}.bt.state'); assert(await t.exists()); await stateFile.delete(); await stateFile.delete(); //删除两次 From ea0ca98205419e3b0bd3ea9af46288e38510f9d6 Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Mon, 17 Jul 2023 15:39:30 +0300 Subject: [PATCH 03/18] Linting --- analysis_options.yaml | 29 ++++-- example/lsd_example.dart | 1 - example/metadata_example.dart | 10 +-- example/torrent_task_example.dart | 24 +++-- lib/src/file/download_file.dart | 10 +-- lib/src/file/download_file_manager.dart | 21 +++-- lib/src/file/state_file.dart | 13 ++- lib/src/lsd/lsd.dart | 10 +-- lib/src/metadata/metadata_downloader.dart | 16 ++-- lib/src/peer/bitfield.dart | 4 +- lib/src/peer/congestion_control.dart | 30 +++---- lib/src/peer/extended_proccessor.dart | 14 ++- lib/src/peer/peer.dart | 34 ++++---- lib/src/peer/peers_manager.dart | 102 +++++++++++----------- lib/src/peer/pex.dart | 24 ++--- lib/src/peer/speed_calculator.dart | 8 +- lib/src/piece/base_piece_selector.dart | 9 +- lib/src/piece/piece.dart | 6 +- lib/src/piece/piece_manager.dart | 4 +- lib/src/task.dart | 30 +++---- lib/src/utils.dart | 1 - pubspec.yaml | 4 +- test/peer_communicate.dart | 4 +- test/torrent_client_test.dart | 24 ++--- 24 files changed, 215 insertions(+), 217 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index a686c1b..f274664 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,14 +1,29 @@ -# Defines a default set of lint rules enforced for -# projects at Google. For details and rationale, -# see https://github.com/dart-lang/pedantic#enabled-lints. -include: package:pedantic/analysis_options.yaml +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml +# Uncomment the following section to specify additional rules. -# For lint rules and documentation, see http://dart-lang.github.io/linter/lints. -# Uncomment to specify additional rules. # linter: # rules: # - camel_case_types -analyzer: +# analyzer: # exclude: # - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/example/lsd_example.dart b/example/lsd_example.dart index f0986e1..02bcc4f 100644 --- a/example/lsd_example.dart +++ b/example/lsd_example.dart @@ -7,7 +7,6 @@ import 'package:torrent_task/torrent_task.dart'; void main(List args) async { print(await getTorrenTaskVersion()); var torrentFile = 'example${Platform.pathSeparator}test4.torrent'; - var savePath = 'tmp'; var model = await Torrent.parse(torrentFile); var infoHash = model.infoHash; var lsd = LSD(infoHash, 'daa231dfa'); diff --git a/example/metadata_example.dart b/example/metadata_example.dart index 314a255..a13b62e 100644 --- a/example/metadata_example.dart +++ b/example/metadata_example.dart @@ -18,7 +18,7 @@ void main(List args) async { metadata.onDownloadComplete((data) { var msg = decode(Uint8List.fromList(data)); print('complete , info : $msg'); - tracker?.stop(true); + tracker.stop(true); }); var u8List = Uint8List.fromList(metadata.infoHashBuffer); @@ -26,14 +26,14 @@ void main(List args) async { tracker.onPeerEvent((source, event) { if (event == null) return; var peers = event.peers; - peers.forEach((element) { + for (var element in peers) { metadata.addNewPeerAddress(element); - }); + } }); // ignore: unawaited_futures findPublicTrackers().listen((alist) { - alist.forEach((element) { + for (var element in alist) { tracker.runTracker(element, u8List); - }); + } }); } diff --git a/example/torrent_task_example.dart b/example/torrent_task_example.dart index 59984dc..919db0d 100644 --- a/example/torrent_task_example.dart +++ b/example/torrent_task_example.dart @@ -30,30 +30,26 @@ void main() async { // ignore: unawaited_futures findPublicTrackers().listen((alist) { - alist.forEach((element) { + for (var element in alist) { task.startAnnounceUrl(element, model.infoHashBuffer); - }); + } }); log('Adding dht nodes'); - model.nodes.forEach((element) { + for (var element in model.nodes) { log('dht node $element'); task.addDHTNode(element); - }); + } print(map); timer = Timer.periodic(Duration(seconds: 2), (timer) async { var progress = '${(task.progress * 100).toStringAsFixed(2)}%'; - var ads = - '${((task.averageDownloadSpeed) * 1000 / 1024).toStringAsFixed(2)}'; - var aps = - '${((task.averageUploadSpeed) * 1000 / 1024).toStringAsFixed(2)}'; - var ds = - '${((task.currentDownloadSpeed) * 1000 / 1024).toStringAsFixed(2)}'; - var ps = '${((task.uploadSpeed) * 1000 / 1024).toStringAsFixed(2)}'; + var ads = ((task.averageDownloadSpeed) * 1000 / 1024).toStringAsFixed(2); + var aps = ((task.averageUploadSpeed) * 1000 / 1024).toStringAsFixed(2); + var ds = ((task.currentDownloadSpeed) * 1000 / 1024).toStringAsFixed(2); + var ps = ((task.uploadSpeed) * 1000 / 1024).toStringAsFixed(2); - var utpd = - '${((task.utpDownloadSpeed) * 1000 / 1024).toStringAsFixed(2)}'; - var utpu = '${((task.utpUploadSpeed) * 1000 / 1024).toStringAsFixed(2)}'; + var utpd = ((task.utpDownloadSpeed) * 1000 / 1024).toStringAsFixed(2); + var utpu = ((task.utpUploadSpeed) * 1000 / 1024).toStringAsFixed(2); var utpc = task.utpPeerCount; var active = task.connectedPeersNumber; diff --git a/lib/src/file/download_file.dart b/lib/src/file/download_file.dart index 3895427..09ce668 100644 --- a/lib/src/file/download_file.dart +++ b/lib/src/file/download_file.dart @@ -159,7 +159,7 @@ class DownloadFile { Future getRandomAccessFile(String type) async { var file = await _getOrCreateFile(); - var access; + RandomAccessFile? access; if (type == WRITE) { _writeAcces ??= await file?.open(mode: FileMode.writeOnlyAppend); access = _writeAcces; @@ -171,7 +171,7 @@ class DownloadFile { _sc = StreamController(); _ss = _sc?.stream.listen(_processRequest); } - return access; + return access!; } Future close() async { @@ -214,9 +214,9 @@ class DownloadFile { } @override - bool operator ==(n) { - if (n is DownloadFile) { - return n.filePath == filePath; + bool operator ==(other) { + if (other is DownloadFile) { + return other.filePath == filePath; } return false; } diff --git a/lib/src/file/download_file_manager.dart b/lib/src/file/download_file_manager.dart index 483c5b1..24fd464 100644 --- a/lib/src/file/download_file_manager.dart +++ b/lib/src/file/download_file_manager.dart @@ -63,21 +63,21 @@ class DownloadFileManager { bool get isAllComplete { return _stateFile.bitfield.piecesNum == - _stateFile.bitfield.completedPieces?.length; + _stateFile.bitfield.completedPieces.length; } int get piecesNumber => _stateFile.bitfield.piecesNum; void _subPieceWriteComplete(int pieceIndex, int begin, int length) { - _subPieceCompleteHandles.forEach((handle) { + for (var handle in _subPieceCompleteHandles) { Timer.run(() => handle(pieceIndex, begin, length)); - }); + } } void _subPieceWriteFailed(int pieceIndex, int begin, int length) { - _subPieceFailedHandles.forEach((handle) { + for (var handle in _subPieceFailedHandles) { Timer.run(() => handle(pieceIndex, begin, length)); - }); + } } Future updateBitfield(int index, [bool have = true]) { @@ -93,9 +93,9 @@ class DownloadFileManager { } void _subPieceReadComplete(int pieceIndex, int begin, List block) { - _subPieceReadHandles.forEach((h) { + for (var h in _subPieceReadHandles) { Timer.run(() => h(pieceIndex, begin, block)); - }); + } } int get downloaded => _stateFile.downloaded; @@ -140,9 +140,9 @@ class DownloadFileManager { } void _fireFileComplete(String path) { - _fileCompleteHandles.forEach((element) { + for (var element in _fileCompleteHandles) { Timer.run(() => element(path)); - }); + } } void _initFileMap(String directory) { @@ -165,8 +165,7 @@ class DownloadFileManager { if (l == null) { l = []; _piece2fileMap?[pieceIndex] = l; - var local_have = localHave(pieceIndex); - if (local_have != null && !local_have) pieces.add(pieceIndex); + if (localHave(pieceIndex)) pieces.add(pieceIndex); } l.add(df); } diff --git a/lib/src/file/state_file.dart b/lib/src/file/state_file.dart index 3bc1a52..a8d0fa7 100644 --- a/lib/src/file/state_file.dart +++ b/lib/src/file/state_file.dart @@ -45,14 +45,11 @@ class StateFile { Bitfield get bitfield => _bitfield; int get downloaded { - if (bitfield != null && bitfield.completedPieces != null) { - var _downloaded = bitfield.completedPieces!.length * metainfo.pieceLength; - if (bitfield.completedPieces!.contains(bitfield.piecesNum - 1)) { - _downloaded -= metainfo.pieceLength - metainfo.lastPieceLength; - } - return _downloaded; + var downloaded = bitfield.completedPieces.length * metainfo.pieceLength; + if (bitfield.completedPieces.contains(bitfield.piecesNum - 1)) { + downloaded -= metainfo.pieceLength - metainfo.lastPieceLength; } - return 0; + return downloaded; } int get uploaded => _uploaded; @@ -63,7 +60,7 @@ class StateFile { directoryPath = directoryPath + Platform.pathSeparator; } - _bitfieldFile = File('${directoryPath}${metainfo.infoHash}.bt.state'); + _bitfieldFile = File('$directoryPath${metainfo.infoHash}.bt.state'); var exists = await _bitfieldFile?.exists(); if (exists != null && !exists) { _bitfieldFile = await _bitfieldFile?.create(recursive: true); diff --git a/lib/src/lsd/lsd.dart b/lib/src/lsd/lsd.dart index 384b43e..8f17776 100644 --- a/lib/src/lsd/lsd.dart +++ b/lib/src/lsd/lsd.dart @@ -61,16 +61,16 @@ class LSD { void _fireLSDPeerEvent(InternetAddress address, int port, String infoHash) { var add = CompactAddress(address, port); - _peerHandlers.forEach((element) { + for (var element in _peerHandlers) { Timer.run(() => element(add, infoHash)); - }); + } } void _processReceive(String str, InternetAddress source) { var strs = str.split('\r\n'); if (strs[0] != ANNOUNCE_FIREST_LINE) return; - var port; - var infoHash; + int? port; + String? infoHash; for (var i = 1; i < strs.length; i++) { var element = strs[i]; if (element.startsWith('Port:')) { @@ -124,7 +124,7 @@ class LSD { /// ///\r\n String _createMessage() { - return '${ANNOUNCE_FIREST_LINE}Host: ${LSD_HOST_STRING}Port: ${port}\r\nInfohash: ${_infoHashHex}\r\ncookie: dt-client${_peerId}\r\n\r\n\r\n'; + return '${ANNOUNCE_FIREST_LINE}Host: ${LSD_HOST_STRING}Port: $port\r\nInfohash: ${_infoHashHex}\r\ncookie: dt-client${_peerId}\r\n\r\n\r\n'; } void close() { diff --git a/lib/src/metadata/metadata_downloader.dart b/lib/src/metadata/metadata_downloader.dart index dbc48b3..0897ac8 100644 --- a/lib/src/metadata/metadata_downloader.dart +++ b/lib/src/metadata/metadata_downloader.dart @@ -52,7 +52,7 @@ class MetadataDownloader bool _running = false; - final int _E = 'e'.codeUnits[0]; + final int E = 'e'.codeUnits[0]; List _infoDatas = []; @@ -82,10 +82,10 @@ class MetadataDownloader _running = false; await _dht.stop(); var fs = []; - _activePeers.forEach((peer) { + for (var peer in _activePeers) { unHookPeer(peer); fs.add(peer.dispose()); - }); + } _activePeers.clear(); _avalidatedPeers.clear(); _peersAddress.clear(); @@ -121,7 +121,6 @@ class MetadataDownloader void addNewPeerAddress(CompactAddress address, [PeerType type = PeerType.TCP, dynamic socket]) { if (!_running) return; - if (address == null) return; if (address.address == localExtenelIP) return; if (socket != null) { // 说明是主动连接的peer,目前只允许一个ip连一次 @@ -166,7 +165,6 @@ class MetadataDownloader } void unHookPeer(Peer peer) { - if (peer == null) return; peer.offDispose(_processPeerDispose); peer.offHandShake(_processPeerHandshake); peer.offConnect(_peerConnected); @@ -239,11 +237,11 @@ class MetadataDownloader } void parseMetaDataMessage(Peer peer, Uint8List data) { - var index; + int? index; var remotePeerId = peer.remotePeerId; try { for (var i = 0; i < data.length; i++) { - if (data[i] == _E && data[i + 1] == _E) { + if (data[i] == E && data[i + 1] == E) { index = i + 1; break; } @@ -286,11 +284,11 @@ class MetadataDownloader if (_completedPieces.length >= _metaDataBlockNum!) { // 此时就停止,然后抛出事件 await stop(); - _handlers.forEach((h) { + for (var h in _handlers) { Timer.run(() { h(_infoDatas); }); - }); + } return; } } diff --git a/lib/src/peer/bitfield.dart b/lib/src/peer/bitfield.dart index a604584..41caa2e 100644 --- a/lib/src/peer/bitfield.dart +++ b/lib/src/peer/bitfield.dart @@ -98,9 +98,9 @@ class Bitfield { var str = element.toRadixString(2); var l = str.length; for (var i = 0; i < 8 - l; i++) { - str = '0' + str; + str = '0$str'; } - return previousValue + str + '-'; + return '$previousValue$str-'; }); } diff --git a/lib/src/peer/congestion_control.dart b/lib/src/peer/congestion_control.dart index d3ff15c..393404a 100644 --- a/lib/src/peer/congestion_control.dart +++ b/lib/src/peer/congestion_control.dart @@ -61,10 +61,10 @@ mixin CongestionControl { } void fireRequestTimeoutEvent(List> requests) { - if (requests == null || requests.isEmpty) return; - _handles.forEach((f) { + if (requests.isEmpty) return; + for (var f in _handles) { Timer.run(() => f(this, requests)); - }); + } } List> get currentRequestBuffer; @@ -76,7 +76,7 @@ mixin CongestionControl { void startRequestDataTimeout([int times = 0]) { _timeout?.cancel(); var requests = currentRequestBuffer; - if (requests == null || requests.isEmpty) return; + if (requests.isEmpty) return; _timeout = Timer(Duration(microseconds: _rto.toInt()), () { if (requests.isEmpty) return; if (times + 1 >= 5) { @@ -93,9 +93,9 @@ mixin CongestionControl { if (requests.isEmpty) break; first = requests.first; } - timeoutR.forEach((request) { + for (var request in timeoutR) { orderResendRequest(request[0], request[1], request[2], request[4]); - }); + } times++; _rto *= 2; @@ -109,24 +109,24 @@ mixin CongestionControl { if (requests.isEmpty) return; var downloaded = 0; int? minRtt; - requests.forEach((request) { + for (var request in requests) { // 重发后收到的不管 - if (request == null || request[4] != 0) return; + if (request[4] != 0) continue; var now = DateTime.now().microsecondsSinceEpoch; var rtt = now - request[3]; minRtt ??= rtt; - minRtt = min(minRtt!, rtt); + minRtt = min(minRtt, rtt); updateRTO(rtt); downloaded += request[2]; - }); + } if (downloaded == 0 || minRtt == null) return; var artt = minRtt; - var delay_factor = (CCONTROL_TARGET - artt!) / CCONTROL_TARGET; - var window_factor = downloaded / _allowWindowSize; - var scaled_gain = - MAX_CWND_INCREASE_REQUESTS_PER_RTT * delay_factor * window_factor; + var delayFactor = (CCONTROL_TARGET - artt) / CCONTROL_TARGET; + var windowFactor = downloaded / _allowWindowSize; + var scaledGain = + MAX_CWND_INCREASE_REQUESTS_PER_RTT * delayFactor * windowFactor; - _allowWindowSize += scaled_gain.toInt(); + _allowWindowSize += scaledGain.toInt(); _allowWindowSize = max(DEFAULT_REQUEST_LENGTH, _allowWindowSize); _allowWindowSize = min(MAX_WINDOW, _allowWindowSize); } diff --git a/lib/src/peer/extended_proccessor.dart b/lib/src/peer/extended_proccessor.dart index ad10090..ad94c68 100644 --- a/lib/src/peer/extended_proccessor.dart +++ b/lib/src/peer/extended_proccessor.dart @@ -55,20 +55,18 @@ mixin ExtendedProcessor { } void _fireExtendedEvent(String name, dynamic data) { - _eventHandler.forEach((element) { + for (var element in _eventHandler) { Timer.run(() => element(this, name, data)); - }); + } } void processExtendHandshake(dynamic data) { var m = data['m'] as Map; _rawMap = m; - if (m != null) { - m.forEach((key, value) { - if (value == 0) return; - _extendedEventMap[value] = key; - }); - } + m.forEach((key, value) { + if (value == 0) return; + _extendedEventMap[value] = key; + }); _fireExtendedEvent('handshake', data); } diff --git a/lib/src/peer/peer.dart b/lib/src/peer/peer.dart index 8dbdc42..a72a302 100644 --- a/lib/src/peer/peer.dart +++ b/lib/src/peer/peer.dart @@ -9,8 +9,6 @@ import 'package:dartorrent_common/dartorrent_common.dart'; import 'package:torrent_task/torrent_task.dart'; import 'package:utp/utp.dart'; -import '../utils.dart'; -import 'bitfield.dart'; import 'peer_event_dispatcher.dart'; import 'congestion_control.dart'; import 'speed_calculator.dart'; @@ -253,16 +251,16 @@ abstract class Peer /// 远程所有的已完成Piece List get remoteCompletePieces { if (_remoteBitfield == null) return []; - return _remoteBitfield!.completedPieces ?? []; + return _remoteBitfield!.completedPieces; } /// Connect remote peer Future connect([int timeout = DEFAULT_CONNECT_TIMEOUT]) async { try { _init(); - var _stream = await connectRemote(timeout); + var stream = await connectRemote(timeout); startSpeedCalculator(); - _streamChunk = _stream?.listen(_processReceiveData, onDone: () { + _streamChunk = stream?.listen(_processReceiveData, onDone: () { _log('Connection is closed $address'); dispose(BadException('远程关闭了连接')); }, onError: (e) { @@ -443,7 +441,7 @@ abstract class Peer if (message != null) initRemoteBitfield(message); return; // bitfield message case ID_REQUEST: - _log('process request from ${address}'); + _log('process request from $address'); if (message != null) _processRemoteRequest(message); return; // request message // case ID_PIECE: @@ -490,7 +488,7 @@ abstract class Peer return; } } - _log('Cannot process the message', 'Unknown message : ${message}'); + _log('Cannot process the message', 'Unknown message : $message'); } /// 从requestbuffer中将request删除 @@ -537,7 +535,7 @@ abstract class Peer var index = view.getUint32(0); var begin = view.getUint32(4); var length = view.getUint32(8); - var requestIndex; + int? requestIndex; for (var i = 0; i < _remoteRequestBuffer.length; i++) { var r = _remoteRequestBuffer[i]; if (r[0] == index && r[1] == begin) { @@ -637,9 +635,9 @@ abstract class Peer void _processRemoteRequest(Uint8List message) { if (_remoteRequestBuffer.length > reqq) { dev.log('Request Error:', - error: 'Too many requests from ${address}', + error: 'Too many requests from $address', name: runtimeType.toString()); - dispose(BadException('Too many requests from ${address}')); + dispose(BadException('Too many requests from $address')); return; } var view = ByteData.view(message.buffer); @@ -650,7 +648,7 @@ abstract class Peer dev.log('TOO LARGEt BLOCK', error: 'BLOCK $length', name: runtimeType.toString()); dispose(BadException( - '${address} : request block length larger than limit : $length > $MAX_REQUEST_LENGTH')); + '$address : request block length larger than limit : $length > $MAX_REQUEST_LENGTH')); return; } if (chokeRemote) { @@ -675,7 +673,7 @@ abstract class Peer /// 不同于其他消息处理,PIECE消息是进行批量处理的。 void _processReceivePieces(List messages) { var requests = >[]; - messages.forEach((message) { + for (var message in messages) { var dataHead = Uint8List(8); List.copyRange(dataHead, 0, message, 0, 8); var view = ByteData.view(dataHead.buffer); @@ -685,14 +683,14 @@ abstract class Peer var request = removeRequest(index, begin, blockLength); // 没有请求的就不处理 if (request == null) { - return; + continue; } var block = Uint8List(message.length - 8); List.copyRange(block, 0, message, 8); requests.add(request); _log('收到请求Piece ($index,$begin) 内容, 从当前Peer已下载 $downloaded bytes '); firePiece(index, begin, block); - }); + } messages.clear(); ackRequest(requests); updateDownload(requests); @@ -701,11 +699,11 @@ abstract class Peer void _processHave(List messages) { var indices = []; - messages.forEach((message) { + for (var message in messages) { var index = ByteData.view(message.buffer).getUint32(0); indices.add(index); updateRemoteBitfield(index, true); - }); + } fireHave(indices); } @@ -973,7 +971,7 @@ abstract class Peer /// index of a piece that has just been successfully downloaded and verified via the hash. void sendHave(int index) { var bytes = Uint8List(4); - _log('发送have信息给对方 : ${bytes},$index'); + _log('发送have信息给对方 : $bytes,$index'); ByteData.view(bytes.buffer).setUint32(0, index, Endian.big); sendMessage(ID_HAVE, bytes); } @@ -1118,7 +1116,7 @@ abstract class Peer void _startToCountdown() { _countdownTimer?.cancel(); _countdownTimer = Timer(Duration(seconds: countdownTime), () { - dispose('Over ${countdownTime} seconds no communication, close'); + dispose('Over $countdownTime seconds no communication, close'); }); } diff --git a/lib/src/peer/peers_manager.dart b/lib/src/peer/peers_manager.dart index 1842b68..e20c65f 100644 --- a/lib/src/peer/peers_manager.dart +++ b/lib/src/peer/peers_manager.dart @@ -97,19 +97,19 @@ class PeersManager with Holepunch, PEX { /// All peers number. Include the connecting peer. int get peersNumber { - if (_peersAddress == null || _peersAddress.isEmpty) return 0; + if (_peersAddress.isEmpty) return 0; return _peersAddress.length; } /// All connected peers number. Include seeder. int get connectedPeersNumber { - if (_activePeers == null || _activePeers.isEmpty) return 0; + if (_activePeers.isEmpty) return 0; return _activePeers.length; } /// All seeder number int get seederNumber { - if (_activePeers == null || _activePeers.isEmpty) return 0; + if (_activePeers.isEmpty) return 0; var c = 0; return _activePeers.fold(c, (previousValue, element) { if (element.isSeeder) { @@ -154,7 +154,7 @@ class PeersManager with Holepunch, PEX { /// /// This speed caculation: sum(`active peer download speed`) double get currentDownloadSpeed { - if (_activePeers == null || _activePeers.isEmpty) return 0.0; + if (_activePeers.isEmpty) return 0.0; return _activePeers.fold( 0.0, (p, element) => p + element.currentDownloadSpeed); } @@ -163,7 +163,7 @@ class PeersManager with Holepunch, PEX { /// /// This speed caculation: sum(`active peer upload speed`) double get uploadSpeed { - if (_activePeers == null || _activePeers.isEmpty) return 0.0; + if (_activePeers.isEmpty) return 0.0; return _activePeers.fold( 0.0, (p, element) => p + element.averageUploadSpeed); } @@ -198,7 +198,6 @@ class PeersManager with Holepunch, PEX { } void unHookPeer(Peer peer) { - if (peer == null) return; peer.offDispose(_processPeerDispose); peer.offBitfield(_processBitfieldUpdate); peer.offHaveAll(_processHaveAll); @@ -279,11 +278,11 @@ class PeersManager with Holepunch, PEX { void _processPieceWriteComplete(int index) async { if (_fileManager.localHave(index)) return; await _fileManager.updateBitfield(index); - _activePeers.forEach((peer) { + for (var peer in _activePeers) { // if (!peer.remoteHave(index)) { peer.sendHave(index); // } - }); + } _flushIndicesBuffer.add(index); if (_fileManager.isAllComplete) { await _flushFiles(_flushIndicesBuffer); @@ -296,8 +295,8 @@ class PeersManager with Holepunch, PEX { Future _flushFiles(final Set indices) async { if (indices.isEmpty) return; var piecesSize = _metaInfo.pieceLength; - var _buffer = indices.length * piecesSize; - if (_buffer >= maxWriteBufferSize || _fileManager.isAllComplete) { + var buffer = indices.length * piecesSize; + if (buffer >= maxWriteBufferSize || _fileManager.isAllComplete) { var temp = Set.from(indices); indices.clear(); await _fileManager.flushFiles(temp); @@ -306,9 +305,9 @@ class PeersManager with Holepunch, PEX { } void _fireAllComplete() { - _allcompletehandles.forEach((element) { + for (var element in _allcompletehandles) { Timer.run(() => element()); - }); + } } bool onAllComplete(void Function() h) { @@ -331,7 +330,7 @@ class PeersManager with Holepunch, PEX { if (request[0] == pieceIndex && request[1] == begin) { dindex.add(i); var peer = request[2] as Peer; - if (peer != null && !peer.isDisposed) { + if (!peer.isDisposed) { if (peer.sendPiece(pieceIndex, begin, block)) { _uploaded += block.length; _uploadedNotifySize += block.length; @@ -341,9 +340,9 @@ class PeersManager with Holepunch, PEX { } } if (dindex.isNotEmpty) { - dindex.forEach((i) { + for (var i in dindex) { _remoteRequest.removeAt(i); - }); + } if (_uploadedNotifySize >= MAX_UPLOADED_NOTIFY_SIZE) { _uploadedNotifySize = 0; _fileManager.updateUpload(_uploaded); @@ -370,15 +369,15 @@ class PeersManager with Holepunch, PEX { } void _pushSubpicesBack(List> requests) { - if (requests == null || requests.isEmpty) return; - requests.forEach((element) { + if (requests.isEmpty) return; + for (var element in requests) { var pindex = element[0]; var begin = element[1]; // TODO 这里很危险,目前都是已16kb来分解一个piece,如果不是呢? var piece = _pieceManager[pindex]; var subindex = begin ~/ DEFAULT_REQUEST_LENGTH; piece?.pushSubPiece(subindex); - }); + } } void _processPeerDispose(dynamic source, [dynamic reason]) { @@ -396,9 +395,9 @@ class PeersManager with Holepunch, PEX { _pushSubpicesBack(bufferRequests); var completedPieces = peer.remoteCompletePieces; - completedPieces.forEach((index) { + for (var index in completedPieces) { _pieceProvider[index]?.removeAvalidatePeer(peer.id); - }); + } _pausedRemoteRequest.remove(peer.id); var tempIndex = []; for (var i = 0; i < _pausedRequest.length; i++) { @@ -407,9 +406,9 @@ class PeersManager with Holepunch, PEX { tempIndex.add(i); } } - tempIndex.forEach((index) { + for (var index in tempIndex) { _pausedRequest.removeAt(index); - }); + } if (reason is TCPConnectException) { // print('TCPConnectException'); @@ -534,8 +533,8 @@ class PeersManager with Holepunch, PEX { void _processHaveUpdate(dynamic source, List indices) { var peer = source as Peer; var flag = false; - indices.forEach((index) { - if (_pieceProvider[index] == null) return; + for (var index in indices) { + if (_pieceProvider[index] == null) continue; if (!_fileManager.localHave(index)) { if (peer.chokeMe) { @@ -545,7 +544,7 @@ class PeersManager with Holepunch, PEX { _pieceProvider[index]?.addAvalidatePeer(peer.id); } } - }); + } if (flag && peer.isSleeping) Timer.run(() => _requestPieces(peer)); } @@ -554,16 +553,16 @@ class PeersManager with Holepunch, PEX { // 更新pieces的可用Peer if (!choke) { var completedPieces = peer.remoteCompletePieces; - completedPieces.forEach((index) { + for (var index in completedPieces) { _pieceProvider[index]?.addAvalidatePeer(peer.id); - }); + } // 这里开始通知request; Timer.run(() => _requestPieces(peer)); } else { var completedPieces = peer.remoteCompletePieces; - completedPieces.forEach((index) { + for (var index in completedPieces) { _pieceProvider[index]?.removeAvalidatePeer(peer.id); - }); + } } } @@ -579,7 +578,7 @@ class PeersManager with Holepunch, PEX { void _processRequestTimeout(dynamic source, List> requests) { var peer = source as Peer; var flag = false; - requests.forEach((element) { + for (var element in requests) { if (element[4] >= 3) { flag = true; Timer.run(() => peer.requestCancel(element[0], element[1], element[2])); @@ -589,21 +588,21 @@ class PeersManager with Holepunch, PEX { var piece = _pieceManager[index]; piece?.pushSubPiece(subindex); } - }); + } // 唤醒其他可能没有工作的peer if (flag) { - _activePeers.forEach((p) { + for (var p in _activePeers) { if (p != peer && p.isSleeping) { Timer.run(() => _requestPieces(p)); } - }); + } } } void _sendKeepAliveToAll() { - _activePeers.forEach((peer) { + for (var peer in _activePeers) { Timer.run(() => _keepAlive(peer)); - }); + } } void _keepAlive(Peer peer) { @@ -629,15 +628,15 @@ class PeersManager with Holepunch, PEX { _paused = false; _keepAliveTimer?.cancel(); _keepAliveTimer = null; - _pausedRequest.forEach((element) { + for (var element in _pausedRequest) { var peer = element[0] as Peer; var index = element[1]; if (!peer.isDisposed) Timer.run(() => _requestPieces(peer, index)); - }); + } _pausedRequest.clear(); _pausedRemoteRequest.forEach((key, value) { - value.forEach((element) { + for (var element in value) { var peer = element[0] as Peer; var index = element[1]; var begin = element[2]; @@ -645,17 +644,17 @@ class PeersManager with Holepunch, PEX { if (!peer.isDisposed) { Timer.run(() => _processRemoteRequest(peer, index, begin, length)); } - }); + } }); _pausedRemoteRequest.clear(); } Future disposeAllSeeder([dynamic reason]) async { - _activePeers.forEach((peer) async { + for (var peer in _activePeers) { if (peer.isSeeder) { await peer.dispose(reason); } - }); + } return; } @@ -671,14 +670,14 @@ class PeersManager with Holepunch, PEX { _pieceManager.offPieceComplete(_processPieceWriteComplete); await _flushFiles(_flushIndicesBuffer); - _flushIndicesBuffer?.clear(); - _allcompletehandles?.clear(); - _noActivePeerhandles?.clear(); - _remoteRequest?.clear(); - _pausedRequest?.clear(); - _pausedRemoteRequest?.clear(); - Function _disposePeers = (Set peers) async { - if (peers != null && peers.isNotEmpty) { + _flushIndicesBuffer.clear(); + _allcompletehandles.clear(); + _noActivePeerhandles.clear(); + _remoteRequest.clear(); + _pausedRequest.clear(); + _pausedRemoteRequest.clear(); + disposePeers(Set peers) async { + if (peers.isNotEmpty) { for (var i = 0; i < peers.length; i++) { var peer = peers.elementAt(i); unHookPeer(peer); @@ -686,8 +685,9 @@ class PeersManager with Holepunch, PEX { } } peers.clear(); - }; - await _disposePeers(_activePeers); + } + + await disposePeers(_activePeers); } //TODO test: diff --git a/lib/src/peer/pex.dart b/lib/src/peer/pex.dart index f04b28d..2da0b87 100644 --- a/lib/src/peer/pex.dart +++ b/lib/src/peer/pex.dart @@ -25,40 +25,40 @@ mixin PEX { void startPEX() { _timer?.cancel(); _timer = Timer.periodic(Duration(seconds: 60), (timer) { - _sendUt_pex_peers(); + sendUtPexPeers(); }); } Iterable get activePeers; - void _sendUt_pex_peers() { + void sendUtPexPeers() { var dropped = []; var added = []; - activePeers.forEach((p) { + for (var p in activePeers) { if (!_lastUTPEX.remove(p.address)) { added.add(p.address); } - }); - _lastUTPEX.forEach((element) { + } + for (var element in _lastUTPEX) { dropped.add(element); - }); + } _lastUTPEX.clear(); var data = {}; data['added'] = []; - added.forEach((element) { + for (var element in added) { _lastUTPEX.add(element); data['added'].addAll(element.toBytes()); - }); + } data['dropped'] = []; - dropped.forEach((element) { + for (var element in dropped) { data['dropped'].addAll(element.toBytes()); - }); + } if (data['added'].isEmpty && data['dropped'].isEmpty) return; var message = encode(data); - activePeers.forEach((peer) { + for (var peer in activePeers) { peer.sendExtendMessage('ut_pex', message); - }); + } } dynamic parsePEXDatas(dynamic source, List message) { diff --git a/lib/src/peer/speed_calculator.dart b/lib/src/peer/speed_calculator.dart index 6ae2bff..3557182 100644 --- a/lib/src/peer/speed_calculator.dart +++ b/lib/src/peer/speed_calculator.dart @@ -68,12 +68,12 @@ mixin SpeedCalculator { /// 更新下载 void updateDownload(List> requests) { - if (requests == null || requests.isEmpty) return; + if (requests.isEmpty) return; var downloaded = 0; - requests.forEach((request) { - if (request[4] != 0) return; // 重新计时的不算 + for (var request in requests) { + if (request[4] != 0) continue; // 重新计时的不算 downloaded += request[2]; - }); + } _downloadedHistory.add([downloaded, DateTime.now().microsecondsSinceEpoch]); _downloaded += downloaded; } diff --git a/lib/src/piece/base_piece_selector.dart b/lib/src/piece/base_piece_selector.dart index 63a769e..a68eed4 100644 --- a/lib/src/piece/base_piece_selector.dart +++ b/lib/src/piece/base_piece_selector.dart @@ -1,6 +1,5 @@ import 'package:dartorrent_common/dartorrent_common.dart'; -import '../utils.dart'; import 'piece.dart'; import 'piece_provider.dart'; import 'piece_selector.dart'; @@ -19,8 +18,8 @@ class BasePieceSelector implements PieceSelector { [bool random = false]) { // random = true; var maxList = []; - var a; - var startIndex; + Piece? a; + int? startIndex; for (var i = 0; i < piecesIndexList.length; i++) { var p = provider[piecesIndexList[i]]; if (p != null && @@ -32,7 +31,7 @@ class BasePieceSelector implements PieceSelector { } } if (startIndex == null) return null; - maxList.add(a); + maxList.add(a!); for (var i = startIndex; i < piecesIndexList.length; i++) { var p = provider[piecesIndexList[i]]; if (p == null || @@ -41,7 +40,7 @@ class BasePieceSelector implements PieceSelector { continue; } // 选择稀有piece - if (a.avalidatePeersCount > p.avalidatePeersCount) { + if (a!.avalidatePeersCount > p.avalidatePeersCount) { if (!random) return p; maxList.clear(); a = p; diff --git a/lib/src/piece/piece.dart b/lib/src/piece/piece.dart index 660e4b9..a1d4e64 100644 --- a/lib/src/piece/piece.dart +++ b/lib/src/piece/piece.dart @@ -167,9 +167,9 @@ class Piece { int get hashCode => hashString.hashCode; @override - bool operator ==(b) { - if (b is Piece) { - return b.hashString == hashString; + bool operator ==(other) { + if (other is Piece) { + return other.hashString == hashString; } return false; } diff --git a/lib/src/piece/piece_manager.dart b/lib/src/piece/piece_manager.dart index 598044e..fd9a937 100644 --- a/lib/src/piece/piece_manager.dart +++ b/lib/src/piece/piece_manager.dart @@ -111,9 +111,9 @@ class PieceManager implements PieceProvider { _donwloadingPieces.remove(index); if (piece != null) { piece.dispose(); - _pieceCompleteHandles.forEach((handle) { + for (var handle in _pieceCompleteHandles) { Timer.run(() => handle(index)); - }); + } } } diff --git a/lib/src/task.dart b/lib/src/task.dart index 5a1abc1..ae5b613 100644 --- a/lib/src/task.dart +++ b/lib/src/task.dart @@ -223,10 +223,10 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { void _processTrackerPeerEvent(Tracker source, PeerEvent? event) { if (event == null) return; var ps = event.peers; - if (ps != null && ps.isNotEmpty) { - ps.forEach((url) { + if (ps.isNotEmpty) { + for (var url in ps) { _processNewPeerFound(url); - }); + } } } @@ -326,9 +326,9 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { await _tracker?.stop(force); Set? tempHandler = Set.from(_stopHandlers); await dispose(); - tempHandler.forEach((element) { + for (var element in tempHandler) { Timer.run(() => element()); - }); + } tempHandler.clear(); tempHandler = null; } @@ -383,9 +383,9 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { } void _fireFileComplete(String filepath) { - _fileCompleteHandlers.forEach((handler) { + for (var handler in _fileCompleteHandlers) { Timer.run(() => handler(filepath)); - }); + } } @override @@ -434,9 +434,9 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { } void _fireTaskComplete() { - _taskCompleteHandlers.forEach((element) { + for (var element in _taskCompleteHandlers) { Timer.run(() => element()); - }); + } } @override @@ -446,21 +446,20 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { double get progress { var d = downloaded; if (d == null) return 0.0; - var l = _metaInfo?.length; - if (l == null) return 0.0; + var l = _metaInfo.length; return d / l; } void _fireTaskPaused() { - _pauseHandlers.forEach((element) { + for (var element in _pauseHandlers) { Timer.run(() => element()); - }); + } } void _fireTaskResume() { - _resumeHandlers.forEach((element) { + for (var element in _resumeHandlers) { Timer.run(() => element()); - }); + } } @override @@ -523,7 +522,6 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { @override void requestPeersFromDHT() { - if (_metaInfo == null) return; _dht?.requestPeers(String.fromCharCodes(_metaInfo.infoHashBuffer)); } } diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 90e7f5c..41e5f25 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'dart:math'; import 'package:dartorrent_common/dartorrent_common.dart'; import 'package:torrent_task/torrent_task.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index ffafbcc..5b3e0c8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,5 +17,5 @@ dependencies: utp: ^1.0.1 dev_dependencies: - pedantic: ^1.9.0 - test: ^1.14.4 + lints: ^2.0.1 + test: ^1.24.3 diff --git a/test/peer_communicate.dart b/test/peer_communicate.dart index 9ea1466..2e77866 100644 --- a/test/peer_communicate.dart +++ b/test/peer_communicate.dart @@ -49,7 +49,7 @@ void main() async { }); peer.onHandShake((peer, remotePeerId, data) { callMap['handshake1'] = true; - print('receive ${remotePeerId} handshake'); + print('receive $remotePeerId handshake'); peer.sendInterested(true); print('send interested to $remotePeerId'); }); @@ -152,7 +152,7 @@ void main() async { }); peer.onHandShake((peer, remotePeerId, data) { callMap['handshake2'] = true; - print('receive ${remotePeerId} handshake'); + print('receive $remotePeerId handshake'); peer.sendBitfield(bitfield); print('send bitfield to server'); peer.sendInterested(true); diff --git a/test/torrent_client_test.dart b/test/torrent_client_test.dart index 6f05351..3aa54ac 100644 --- a/test/torrent_client_test.dart +++ b/test/torrent_client_test.dart @@ -22,9 +22,9 @@ void main() { if (c * 8 != pieces) c++; print('check buffer length'); assert(bitfield!.buffer.length == c); - bitfield!.buffer.forEach((element) { + for (var element in bitfield!.buffer) { assert(element == 0); - }); + } }); test('random set/get test, and check the complete index ', () { @@ -55,18 +55,18 @@ void main() { assert(bitfield!.haveCompletePiece()); print('Check bitfield value...'); - list.forEach((index) { + for (var index in list) { assert(bitfield!.getBit(index)); - }); + } var tempList = []; tempList.addAll(list); - tempList.forEach((index) { + for (var index in tempList) { bitfield!.setBit(index, false); - }); + } print('Clean all bitfield...'); - bitfield!.buffer.forEach((element) { + for (var element in bitfield!.buffer) { assert(element == 0); - }); + } }); test('add/remote complete list', () { @@ -135,7 +135,7 @@ void main() { bitfieldList = List.generate(10, (index) => Bitfield.createEmptyBitfield(pieces)); - bitfieldList.forEach((bitfield) { + for (var bitfield in bitfieldList) { var t = Random(); var randomIndex = {}; for (var i = 0; i < pieces; i++) { @@ -151,11 +151,13 @@ void main() { assert(bitfield.getBit(index) == true); } } - }); + } }); test('ramdon pieces', () { - bitfieldList.forEach((element) => print(element.toString())); + for (var element in bitfieldList) { + print(element.toString()); + } }); }); From cbf057bed9e8c942132608578a3d9343cc90da5e Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Wed, 19 Jul 2023 10:22:56 +0300 Subject: [PATCH 04/18] translate chinese text to english --- example/torrent_task_comin_example.dart | 4 +- lib/src/file/download_file.dart | 10 +- lib/src/file/download_file_manager.dart | 21 +-- lib/src/file/state_file.dart | 3 +- lib/src/metadata/metadata_downloader.dart | 11 +- lib/src/peer/bitfield.dart | 21 +-- lib/src/peer/congestion_control.dart | 14 +- lib/src/peer/peer.dart | 165 ++++++++++++---------- lib/src/peer/peer_event_dispatcher.dart | 5 +- lib/src/peer/peers_manager.dart | 27 ++-- lib/src/peer/speed_calculator.dart | 23 +-- lib/src/piece/base_piece_selector.dart | 12 +- lib/src/piece/piece.dart | 15 +- lib/src/piece/piece_manager.dart | 24 ++-- lib/src/piece/piece_selector.dart | 12 +- lib/src/task.dart | 17 +-- lib/torrent_task.dart | 4 +- test/peer_communicate.dart | 6 +- test/torrent_client_test.dart | 26 ++-- 19 files changed, 223 insertions(+), 197 deletions(-) diff --git a/example/torrent_task_comin_example.dart b/example/torrent_task_comin_example.dart index 892f14d..f351b1c 100644 --- a/example/torrent_task_comin_example.dart +++ b/example/torrent_task_comin_example.dart @@ -9,7 +9,7 @@ import 'package:torrent_task/torrent_task.dart'; Future main() async { var model = await Torrent.parse('example${Platform.pathSeparator}test4.torrent'); - // 不获取peers + // No peers retrieval model.announces.clear(); var task = TorrentTask.newTask(model, 'tmp${Platform.pathSeparator}test'); Timer? timer; @@ -51,7 +51,7 @@ Future main() async { // await Future.delayed(Duration(seconds: 120)); // task.resume(); // }); - // 自己下载自己 + // download from yourself task.addPeer(CompactAddress(InternetAddress.tryParse('192.168.0.24')!, 57331), PeerType.UTP); } diff --git a/lib/src/file/download_file.dart b/lib/src/file/download_file.dart index 09ce668..94cfc89 100644 --- a/lib/src/file/download_file.dart +++ b/lib/src/file/download_file.dart @@ -68,9 +68,11 @@ class DownloadFile { return completer.future; } - /// 处理读写请求 + /// Process read and write requests. /// - /// 每次只处理一个请求。`Stream`在进入该方法后通过`StreamSubscription`暂停通道信息读取,直到处理完一条请求后才恢复 + /// Only one request is processed at a time. The Stream is paused through StreamSubscription + /// upon entering this method, and it resumes reading from the channel only after processing + /// the current request. void _processRequest(event) async { _ss?.pause(); if (event['type'] == WRITE) { @@ -103,7 +105,7 @@ class DownloadFile { } } - /// 请求将缓冲区写入磁盘 + /// Request to write the buffer to disk. Future requestFlush() async { _writeAcces ??= await getRandomAccessFile(WRITE); var completer = Completer(); @@ -140,7 +142,7 @@ class DownloadFile { } /// - /// 获取对应文件,如果文件不存在会创建一个新文件 + /// Get the corresponding file, and if the file does not exist, create a new file. Future _getOrCreateFile() async { _file ??= File(filePath); var exists = await _file?.exists(); diff --git a/lib/src/file/download_file_manager.dart b/lib/src/file/download_file_manager.dart index 24fd464..92da842 100644 --- a/lib/src/file/download_file_manager.dart +++ b/lib/src/file/download_file_manager.dart @@ -33,8 +33,7 @@ class DownloadFileManager { final StateFile _stateFile; - /// TODO - /// - 没有建立文件读取缓存 + /// TODO: File read caching DownloadFileManager(this.metainfo, this._stateFile) { _piece2fileMap = List.filled(_stateFile.bitfield.piecesNum, null); } @@ -100,9 +99,11 @@ class DownloadFileManager { int get downloaded => _stateFile.downloaded; - /// 该方法看似只将缓冲区内容写入磁盘,实际上 - /// 每当缓存写入后都会认为该[pieceIndex]对应`Piece`已经完成,则会去移除 - /// `_file2pieceMap`中文件对应的piece index,当全部移除完毕,会抛出File Complete事件 + /// This method appears to only write the buffer content to the disk, but in + /// reality,every time the cache is written, it is considered that the [Piece] + /// corresponding to [pieceIndex] has been completed. Therefore, it will + /// remove the file's corresponding piece index from the _file2pieceMap. When + /// all the pieces have been removed, a File Complete event will be triggered. Future flushFiles(Set pieceIndices) async { var d = _stateFile.downloaded; var flushed = {}; @@ -126,7 +127,7 @@ class DownloadFileManager { } var msg = - 'downloaded:${d / (1024 * 1024)} mb , 完成度 ${((d / metainfo.length) * 10000).toInt() / 100} %'; + 'downloaded:${d / (1024 * 1024)} mb , Progress ${((d / metainfo.length) * 10000).toInt() / 100} %'; log(msg, name: runtimeType.toString()); return true; } @@ -219,11 +220,11 @@ class DownloadFileManager { } /// - /// 将`Sub Piece`的内容写入文件中。完成后会发送 `sub piece complete`事件, - /// 如果失败,就会发送`sub piece failed`事件 + // Writes the content of a Sub Piece to the file. After completion, a sub piece complete event will be sent. + /// If it fails, a sub piece failed event will be sent. /// - /// 该`Sub Piece`是来自于[pieceIndex]对应的`Piece`,内容为[block],起始位置是[begin]。 - /// 该类不会去验证写入的Sub Piece是否重复,重复内容直接覆盖之前内容 + /// The Sub Piece is from the Piece corresponding to [pieceIndex], and the content is [block] starting from [begin]. + /// This class does not validate if the written Sub Piece is a duplicate; it simply overwrites the previous content. void writeFile(int pieceIndex, int begin, List block) { var tempFiles = _piece2fileMap?[pieceIndex]; var ps = pieceIndex * metainfo.pieceLength + begin; diff --git a/lib/src/file/state_file.dart b/lib/src/file/state_file.dart index a8d0fa7..3ad3aa3 100644 --- a/lib/src/file/state_file.dart +++ b/lib/src/file/state_file.dart @@ -189,7 +189,8 @@ class StateFile { await _access?.flush(); await _access?.close(); } catch (e) { - log('关闭状态文件出错:', error: e, name: runtimeType.toString()); + log('Error while closing the status file: ', + error: e, name: runtimeType.toString()); } finally { _access = null; _ss = null; diff --git a/lib/src/metadata/metadata_downloader.dart b/lib/src/metadata/metadata_downloader.dart index 0897ac8..34a3b67 100644 --- a/lib/src/metadata/metadata_downloader.dart +++ b/lib/src/metadata/metadata_downloader.dart @@ -123,7 +123,8 @@ class MetadataDownloader if (!_running) return; if (address.address == localExtenelIP) return; if (socket != null) { - // 说明是主动连接的peer,目前只允许一个ip连一次 + // Indicates that it is an actively connecting peer, and currently, only + // one connection per IP address is allowed. if (!_incomingAddress.add(address.address)) { return; } @@ -157,7 +158,7 @@ class MetadataDownloader return _activePeers.contains(id); } - /// 支持哪些扩展在这里添加 + /// Add supported extensions here void _registerExtended(Peer peer) { peer.registerExtened('ut_metadata'); peer.registerExtened('ut_pex'); @@ -260,7 +261,7 @@ class MetadataDownloader if (msg['msg_type'] == 2) { var piece = msg['piece']; if (piece != null && piece < _metaDataBlockNum) { - _metaDataPieces.add(piece); //退还拒绝的piece + _metaDataPieces.add(piece); //Return rejected piece var timer = _requestTimeout.remove(remotePeerId); timer?.cancel(); _requestMetaData(); @@ -273,7 +274,7 @@ class MetadataDownloader } void _pieceDownloadComplete(int piece, int start, List bytes) async { - // 防止多次调用 + // Prevent multiple invocations" if (_completedPieces.length >= _metaDataBlockNum! || _completedPieces.contains(piece)) { return; @@ -282,7 +283,7 @@ class MetadataDownloader List.copyRange(_infoDatas, started, bytes, start); _completedPieces.add(piece); if (_completedPieces.length >= _metaDataBlockNum!) { - // 此时就停止,然后抛出事件 + // At this point, stop and emit the event await stop(); for (var h in _handlers) { Timer.run(() { diff --git a/lib/src/peer/bitfield.dart b/lib/src/peer/bitfield.dart index 41caa2e..7fd8326 100644 --- a/lib/src/peer/bitfield.dart +++ b/lib/src/peer/bitfield.dart @@ -1,8 +1,8 @@ import 'dart:typed_data'; -/// 用来于的基础数字,靠右移动获取要`&`和`|`的数 +/// The base number used for bitwise AND and OR operations. /// -/// 即:10000000 +/// It is represented as: 10000000 (binary representation) const BASE_NUM = 128; class Bitfield { @@ -14,19 +14,22 @@ class Bitfield { bool getBit(int index) { if (index < 0 || index >= piecesNum) return false; - var i = index ~/ 8; // 表示第几个数字 - var b = index.remainder(8); // 这表示该数字的第几位bit + var i = index ~/ 8; // This represents the position of the byte. + var b = index + .remainder(8); // This represents the position of the bit in the byte var andNum = BASE_NUM >> b; - return ((andNum & buffer[i]) != 0); // 等于0说明该位上的数字为0,即false + return ((andNum & buffer[i]) != + 0); // If it is equal to 0, it means that the bit at that position is 0, which is equivalent to false. } /// - /// [index] 如果不在 [0 - piecesNum]范围内,不会报错,直接返回 + /// If [index] is not within the range [0 - piecesNum], no error will be thrown, and the method will return directly. void setBit(int index, bool bit) { if (index < 0 || index >= piecesNum) return; if (getBit(index) == bit) return; - var i = index ~/ 8; // 表示第几个数字 - var b = index.remainder(8); // 这表示该数字的第几位bit + var i = index ~/ 8; // This represents the position of the byte. + var b = index + .remainder(8); // This represents the position of the bit in the byte var orNum = BASE_NUM >> b; if (bit) { _completedIndex = completedPieces; @@ -38,7 +41,7 @@ class Bitfield { } } - /// 如果有完成的piece就返回ture,不一定会全部检索 + /// Returns `true` if there is a completed piece, without necessarily checking all of them. bool haveCompletePiece() { for (var i = 0; i < buffer.length; i++) { var a = buffer[i]; diff --git a/lib/src/peer/congestion_control.dart b/lib/src/peer/congestion_control.dart index 393404a..03eb2b5 100644 --- a/lib/src/peer/congestion_control.dart +++ b/lib/src/peer/congestion_control.dart @@ -10,14 +10,14 @@ const MAX_WINDOW = 1048576; const RECORD_TIME = 5000000; -/// 最大每次增加的request为3 +/// The maximum number of requests to be increased in each round is 3. const MAX_CWND_INCREASE_REQUESTS_PER_RTT = 3 * 16384; -/// LEDBAT拥塞控制 +/// LEDBAT Congestion Control /// -/// 注意,所有时间单位都是微秒 +/// Note: All time units are in microseconds mixin CongestionControl { - // 初始是10秒 + // The initial value is 10 seconds. double _rto = 10000000; double? _srtt; @@ -45,7 +45,7 @@ mixin CongestionControl { return _handles.remove(handle); } - /// 更新超时时间 + /// Update the timeout. void updateRTO(int rtt) { if (rtt == 0) return; if (_srtt == null) { @@ -56,7 +56,7 @@ mixin CongestionControl { _srtt = (1 - 0.125) * _srtt! + 0.125 * rtt; } _rto = _srtt! + max(100000, 4 * _rttvar!); - // 不到1秒,就设置为1秒 + // If less than 1 second, set it to 1 second. _rto = max(_rto, 1000000); } @@ -110,7 +110,7 @@ mixin CongestionControl { var downloaded = 0; int? minRtt; for (var request in requests) { - // 重发后收到的不管 + // Ignore the received packets after resending. if (request[4] != 0) continue; var now = DateTime.now().microsecondsSinceEpoch; var rtt = now - request[3]; diff --git a/lib/src/peer/peer.dart b/lib/src/peer/peer.dart index a72a302..c097178 100644 --- a/lib/src/peer/peer.dart +++ b/lib/src/peer/peer.dart @@ -62,7 +62,6 @@ enum PeerType { TCP, UTP } /// 30 Seconds const DEFAULT_CONNECT_TIMEOUT = 30; -/// 带有 [index],[begin],[length]参数的方法 typedef PieceConfigHandle = void Function( Peer peer, int index, int begin, int length); typedef NoneParamHandle = void Function(Peer peer); @@ -79,49 +78,50 @@ abstract class Peer SpeedCalculator { /// Countdown time , when peer don't receive or send any message from/to remote , /// this class will invoke close. - /// 单位:秒 + /// Unit: second int countdownTime = 150; String get id { return address.toContactEncodingString(); } - /// 下载项目的piece总数 + /// The total number of pieces of downloaded items final int _piecesNum; - /// 远程的Bitfield + /// Remote Bitfield Bitfield? _remoteBitfield; - /// 该peer是否已经disposed + /// Whether the peer has been disposed bool _disposed = false; - /// 倒计时关闭Timer + /// Countdown to close Timer. Timer? _countdownTimer; - /// 对方是否choke了我,初始默认true + /// Whether the other party choke me, the initial default is true bool _chokeMe = true; - /// 我是否choke了对方,默认true + /// Did I choke the other party, the default is true bool chokeRemote = true; - /// 对方是否对我的资源感兴趣,默认false + /// Whether the other party is interested in my resources, the default is false bool _interestedMe = false; - /// 我是否对对方的资源感兴趣,默认false + /// Am I interested in the resources of the other party, the default is false bool interestedRemote = false; - /// Debug 使用 + /// Debug use // ignore: unused_field dynamic _disposeReason; - /// 远程Peer的地址和端口 + /// The address and port of the remote peer final CompactAddress address; /// Torrent infohash buffer final List _infoHashBuffer; /// Local Peer Id - final String _localPeerId; // 本机的peer id。发送消息会用到 + final String + _localPeerId; // The local peer ID. It is used when sending messages. String? _remotePeerId; @@ -131,19 +131,19 @@ abstract class Peer /// has this peer send local bitfield to remote? bool _bitfieldSended = false; - /// 远程数据接受,监听subcription + /// Remote data reception, listening to subscription. StreamSubscription? _streamChunk; - /// 从通道中获取数据的buffer + /// Buffer to obtain data from the channel. List _cacheBuffer = []; - /// 本地发送请求buffer。格式位:[index,begin,length] + /// The local sends a request buffer. The format is: [index, begin, length]. final _requestBuffer = >[]; - /// 远程发送请求buffer。格式位:[index,begin,length] + /// The remote sends a request buffer. The format is: [index, begin, length]. final _remoteRequestBuffer = >[]; - // /// Max request count in one piple ,5 + /// Max request count in one piple ,5 static const MAX_REQUEST_COUNT = 5; bool remoteEnableFastPeer = false; @@ -154,13 +154,13 @@ abstract class Peer bool localEnableExtended = true; - /// 本地的Allow Fast pieces + /// Local Allow Fast pieces. final Set _allowFastPieces = {}; - /// 远程发送的Allow Fast pieces + /// Remote Allow Fast pieces. final Set _remoteAllowFastPieces = {}; - /// 远程发送的Suggest pieces + /// Remote Suggest pieces. final Set _remoteSuggestPieces = {}; final PeerType type; @@ -169,12 +169,18 @@ abstract class Peer int? remoteReqq; - /// - /// [_id] 是用于区分不同Peer的Id,和[_localPeerId]不同,[_localPeerId]是bt协议中的Peer_id。 - /// [address]是远程peer的地址和端口,子类在实现的时候可以利用该值进行远程连接。[_infoHashBuffer] - /// 是torrent文件中的infohash值,[_piecesNum]是下载项目的总piece数目,用于构建远程`Bitfield`数据 - /// 使用。可选项[localEnableFastPeer]默认位`true`,表示本地是否开启[Fast Extension(BEP 0006)](http://www.bittorrent.org/beps/bep_0006.html), - /// [localEnableExtended]表示本地是否可以使用[Extension Protocol](http://www.bittorrent.org/beps/bep_0010.html) + /// [_id] is used to differentiate between different peers. It is different from + /// [_localPeerId], which is the Peer_id in the BitTorrent protocol. + /// [address] is the remote peer's address and port, and subclasses can use this + /// value for remote connections. + /// [_infoHashBuffer] is the infohash value from the torrent file, + /// and [_piecesNum] is the total number of pieces in the download project, + /// which is used to construct the remote `Bitfield` data. + /// The optional parameter [localEnableFastPeer] is set to `true` by default, + /// indicating whether local peers can use the + /// [Fast Extension (BEP 0006)](http://www.bittorrent.org/beps/bep_0006.html). + /// [localEnableExtended] indicates whether local peers can use the + /// [Extension Protocol](http://www.bittorrent.org/beps/bep_0010.html). Peer(this._localPeerId, this.address, this._infoHashBuffer, this._piecesNum, {this.type = PeerType.TCP, this.localEnableFastPeer = true, @@ -197,15 +203,15 @@ abstract class Peer enableExtend: enableExtend, enableFast: enableFast); } - /// 远程的Bitfield + /// The remote peer's bitfield. Bitfield? get remoteBitfield => _remoteBitfield; - /// 是否已经发送local bitfield给对方 + /// Whether the local bitfield has been sent to the remote peer. bool get bitfieldSended => _bitfieldSended; bool get isLeecher => !isSeeder; - /// 如果具备完整的torrent文件,那它就是一个seeder + /// If it has the complete torrent file, then it is a seeder. bool get isSeeder { if (_remoteBitfield == null) return false; if (_remoteBitfield!.haveAll()) return true; @@ -216,10 +222,10 @@ abstract class Peer String get localPeerId => _localPeerId; - /// 远程发送的Request请求 + /// Requests received from the remote peer. List> get remoteRequestbuffer => _remoteRequestBuffer; - /// 本地发送给远程的Request请求 + /// Requests sent from the local peer to the remote peer. List> get requestBuffer => _requestBuffer; Set get remoteSuggestPieces => _remoteSuggestPieces; @@ -248,7 +254,7 @@ abstract class Peer } } - /// 远程所有的已完成Piece + /// All completed pieces of the remote peer. List get remoteCompletePieces { if (_remoteBitfield == null) return []; return _remoteBitfield!.completedPieces; @@ -262,7 +268,7 @@ abstract class Peer startSpeedCalculator(); _streamChunk = stream?.listen(_processReceiveData, onDone: () { _log('Connection is closed $address'); - dispose(BadException('远程关闭了连接')); + dispose(BadException('The remote peer closed the connection')); }, onError: (e) { _log('Error happen: $address', e); dispose(e); @@ -274,23 +280,28 @@ abstract class Peer } } - /// 初始化一些基本数据 + /// Initialize some basic data. void _init() { - // 初始化数据 + /// Initialize data. _disposeReason = null; _disposed = false; _handShaked = false; - // 清空通道数据缓存: + + /// Clear the channel data cache. _cacheBuffer.clear(); - // 清空请求缓存 + + /// Clear the request cache. _requestBuffer.clear(); _remoteRequestBuffer.clear(); - // 重置fast pieces + + /// Reset the fast pieces. _remoteAllowFastPieces.clear(); _allowFastPieces.clear(); - // 重置suggest pieces + + /// Reset the suggest pieces. _remoteSuggestPieces.clear(); - // 重置远程fast extension标识 + + /// Reset the remote fast extension flag. remoteEnableFastPeer = false; } @@ -299,14 +310,14 @@ abstract class Peer return request; } - /// 添加一个request到buffer中 - /// - /// 这个request是一个数组 : - /// - 0 : index - /// - 1 : begin - /// - 2 : length - /// - 3 : send time - /// - 4 : resend times + /// Add a request to the buffer. + + /// This request is an array: + /// - 0: index + /// - 1: begin + /// - 2: length + /// - 3: send time + /// - 4: resend times bool addRequest(int index, int begin, int length) { var maxCount = currentWindow; // maxCount = oldCount; @@ -326,12 +337,15 @@ abstract class Peer } void _processReceiveData(dynamic data) { - // 不管收到什么消息,只要不是空的,重置倒计时: + // Regardless of what message is received, as long as it is not empty, reset the countdown timer. if (data != null && data.isNotEmpty) _startToCountdown(); - // if (data.isNotEmpty) log('收到数据 $data'); - if (data != null) _cacheBuffer.addAll(data); // 接受remote发送数据。缓冲到一处 + // if (data.isNotEmpty) log('Received data: $data'); + if (data != null) { + _cacheBuffer.addAll( + data); // Accept data sent by the remote peer and buffer it in one place. + } if (_cacheBuffer.isEmpty) return; - // 查看是不是handshake头 + // Check if it's a handshake header. if (_cacheBuffer[0] == 19 && _cacheBuffer.length >= 68) { if (_isHandShakeHead(_cacheBuffer)) { if (_validateInfoHash(_cacheBuffer)) { @@ -491,9 +505,9 @@ abstract class Peer _log('Cannot process the message', 'Unknown message : $message'); } - /// 从requestbuffer中将request删除 + /// Remove a request from the request buffer. /// - /// 每当得到了piece回应或者request超时,都会调用此方法 + /// This method is called whenever a piece response is received or a request times out. List? _removeRequestFromBuffer(int index, int begin, int length) { var i = _findRequestIndexFromBuffer(index, begin, length); if (i != -1) { @@ -607,7 +621,7 @@ abstract class Peer startRequestDataTimeout(); fireRejectRequest(index, begin, length); } else { - // 有可能被删除了,但是reject来慢了而已 + // It's possible that the peer was deleted, but the reject message arrived too late. // dispose('Never send request ($index,$begin) but recieve a rejection'); return; } @@ -657,20 +671,20 @@ abstract class Peer fireRequest(index, begin, length); return; } else { - // choke对方我不需要应答 + // Choking the remote peer without sending an acknowledgment. // sendRejectRequest(index, begin, length); return; } } _remoteRequestBuffer.add([index, begin, length]); - // TODO 这里做速度限制! + // TODO Implement speed limit here! fireRequest(index, begin, length); } - /// 处理接收到的PIECE消息 + /// Handle the received PIECE messages. /// - /// 不同于其他消息处理,PIECE消息是进行批量处理的。 + /// Unlike other message types, PIECE messages are processed in batches. void _processReceivePieces(List messages) { var requests = >[]; for (var message in messages) { @@ -681,14 +695,16 @@ abstract class Peer var begin = view.getUint32(4); var blockLength = message.length - 8; var request = removeRequest(index, begin, blockLength); - // 没有请求的就不处理 + + /// Ignore if there are no requests to process. if (request == null) { continue; } var block = Uint8List(message.length - 8); List.copyRange(block, 0, message, 8); requests.add(request); - _log('收到请求Piece ($index,$begin) 内容, 从当前Peer已下载 $downloaded bytes '); + _log( + 'Received request for Piece ($index, $begin) content, downloaded $downloaded bytes from the current Peer.'); firePiece(index, begin, block); } messages.clear(); @@ -707,7 +723,7 @@ abstract class Peer fireHave(indices); } - /// 更新远程Bitfield + /// Update the remote peer's bitfield. void updateRemoteBitfield(int index, bool have) { _remoteBitfield?.setBit(index, have); } @@ -787,9 +803,9 @@ abstract class Peer /// See : [Peer protocol message](https://wiki.theory.org/BitTorrentSpecification#Messages) void sendByteMessage(List bytes); - /// 发送handshake消息。 + /// Send a handshake message. /// - /// 在发送handshake后,会主动发送bitfield和have消息给对方 + /// After sending the handshake message, this method will also proactively send the bitfield and have messages to the remote peer. void sendHandShake() { if (_handShaked) return; var message = []; @@ -915,9 +931,9 @@ abstract class Peer @override List> get currentRequestBuffer => _requestBuffer; - /// 请求取消某个request + /// Cancel a specific request by removing it from the request queue. /// - /// 如果在请求队列中就删除,否则返回 + /// If the request is present in the queue, it will be removed; otherwise, this operation will simply return. void requestCancel(int index, int begin, int length) { var request = removeRequest(index, begin, length); if (request != null) { @@ -949,7 +965,7 @@ abstract class Peer /// valid and available piece. Spare bits at the end are set to zero. /// void sendBitfield(Bitfield bitfield) { - _log('发送bitfile信息给对方 : ${bitfield.buffer}'); + _log('Sending bitfile information to the peer: ${bitfield.buffer}'); if (_bitfieldSended) return; _bitfieldSended = true; if (remoteEnableFastPeer && localEnableFastPeer) { @@ -971,7 +987,7 @@ abstract class Peer /// index of a piece that has just been successfully downloaded and verified via the hash. void sendHave(int index) { var bytes = Uint8List(4); - _log('发送have信息给对方 : $bytes,$index'); + _log('Sending have information to the peer: $bytes, $index'); ByteData.view(bytes.buffer).setUint32(0, index, Endian.big); sendMessage(ID_HAVE, bytes); } @@ -990,7 +1006,7 @@ abstract class Peer sendMessage(id); } - /// 发送`interested` 或 `not interested` 到 对方,表明自己是否对它拥有资源感兴趣 + ///Send interested or not interested to the other party to indicate whether you are interested in its resources or not. /// /// - `interested: ` /// - `not interested: ` @@ -1108,7 +1124,7 @@ abstract class Peer } } - /// 开始倒计时。 + /// Countdown started. /// /// Over `countdownTime` seconds , peer will close to disconnect the remote. /// but if peer send or receive any message from/to remote during countdown, @@ -1120,10 +1136,9 @@ abstract class Peer }); } - /// 该Peer被dispose。 + /// The Peer has been disposed. /// - /// 被dispose后的peer将无法再发送或监听数据,状态数据也恢复到初始状态,并且之前添加的事件监听 - /// 器都会被移除。 + /// After disposal, the Peer will no longer be able to send or receive data, its state data will be reset to its initial state, and all previously added event listeners will be removed. Future dispose([dynamic reason]) async { if (_disposed) return; _disposeReason = reason; @@ -1167,7 +1182,7 @@ class BadException implements Exception { BadException(this.e); @override String toString() { - return '不需重连错误 : $e'; + return 'No need to reconnect error: $e'; } } diff --git a/lib/src/peer/peer_event_dispatcher.dart b/lib/src/peer/peer_event_dispatcher.dart index 68ac302..9a05148 100644 --- a/lib/src/peer/peer_event_dispatcher.dart +++ b/lib/src/peer/peer_event_dispatcher.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'bitfield.dart'; -/// 带有 [index],[begin],[length]参数的方法 typedef PieceConfigHandle = void Function( dynamic source, int index, int begin, int length); typedef NoneParamHandle = void Function(dynamic source); @@ -29,9 +28,9 @@ const PEER_EVENT_SUGGEST_PIECE = 'suggest_piece'; const PEER_EVENT_ALLOW_FAST = 'allow_fast'; const PEER_EVENT_REJECT_REQUEST = 'reject_request'; -/// 专门负责添加、删除Peer事件回调方法的 `mixin` +// A mixin specifically responsible for adding and removing Peer event callback methods. mixin PeerEventDispatcher { - /// 所有事件回调方法Map + /// Map of all event callback methods. final _handleFunctions = >{}; Set _getFunctionSet(String key) { diff --git a/lib/src/peer/peers_manager.dart b/lib/src/peer/peers_manager.dart index e20c65f..147f44f 100644 --- a/lib/src/peer/peers_manager.dart +++ b/lib/src/peer/peers_manager.dart @@ -22,7 +22,7 @@ const MAX_UPLOADED_NOTIFY_SIZE = 1024 * 1024 * 10; // 10 mb /// /// TODO: -/// - 没有处理对外的Suggest Piece/Fast Allow +/// - The external Suggest Piece/Fast Allow requests are not handled. class PeersManager with Holepunch, PEX { final List IGNORE_IPS = [ InternetAddress.tryParse('0.0.0.0')!, @@ -41,7 +41,7 @@ class PeersManager with Holepunch, PEX { InternetAddress? localExtenelIP; - /// 写入磁盘的缓存最大值 + /// The maximum size of the disk write cache. int maxWriteBufferSize; final _flushIndicesBuffer = {}; @@ -191,7 +191,7 @@ class PeersManager with Holepunch, PEX { peer.connect(); } - /// 支持哪些扩展在这里添加 + /// Add supported extensions here void _registerExtended(Peer peer) { peer.registerExtened('ut_pex'); peer.registerExtened('ut_holepunch'); @@ -252,7 +252,7 @@ class PeersManager with Holepunch, PEX { if (address == null) return; if (address.address == localExtenelIP) return; if (socket != null) { - // 说明是主动连接的peer,目前只允许一个ip连一次 + // Indicates that it is an actively connected peer, and currently, only one IP address is allowed to connect at a time. if (!_incomingAddress.add(address.address)) { return; } @@ -350,7 +350,7 @@ class PeersManager with Holepunch, PEX { } } - /// 即使对方choke了我,也可以下载 + /// Even if the other peer has choked me, I can still download. void _processAllowFast(dynamic source, int index) { var peer = source as Peer; var piece = _pieceProvider[index]; @@ -373,7 +373,7 @@ class PeersManager with Holepunch, PEX { for (var element in requests) { var pindex = element[0]; var begin = element[1]; - // TODO 这里很危险,目前都是已16kb来分解一个piece,如果不是呢? + // TODO This is dangerous here. Currently, we are dividing a piece into 16 KB chunks. What if it's not the case? var piece = _pieceManager[pindex]; var subindex = begin ~/ DEFAULT_REQUEST_LENGTH; piece?.pushSubPiece(subindex); @@ -456,7 +456,7 @@ class PeersManager with Holepunch, PEX { if (piece == null) return; var subIndex = piece.popSubPiece()!; - var size = DEFAULT_REQUEST_LENGTH; // block大小现算 + var size = DEFAULT_REQUEST_LENGTH; // Block size is calculated dynamically. var begin = subIndex * size; if ((begin + size) > piece.byteLength) { size = piece.byteLength - begin; @@ -515,7 +515,8 @@ class PeersManager with Holepunch, PEX { if (bitfield != null) { if (peer.interestedRemote) return; if (_fileManager.isAllComplete && peer.isSeeder) { - peer.dispose(BadException('已经下载完成不再连接Seeder')); + peer.dispose(BadException( + "Do not connect to Seeder if the download is already completed")); return; } for (var i = 0; i < _fileManager.piecesNumber; i++) { @@ -550,13 +551,13 @@ class PeersManager with Holepunch, PEX { void _processChokeChange(dynamic source, bool choke) { var peer = source as Peer; - // 更新pieces的可用Peer + // Update available peers for pieces. if (!choke) { var completedPieces = peer.remoteCompletePieces; for (var index in completedPieces) { _pieceProvider[index]?.addAvalidatePeer(peer.id); } - // 这里开始通知request; + // Here, start notifying requests. Timer.run(() => _requestPieces(peer)); } else { var completedPieces = peer.remoteCompletePieces; @@ -571,7 +572,7 @@ class PeersManager with Holepunch, PEX { if (interested) { peer.sendChoke(false); } else { - peer.sendChoke(true); // 不感兴趣就choke它 + peer.sendChoke(true); // Choke it if not interested. } } @@ -589,7 +590,7 @@ class PeersManager with Holepunch, PEX { piece?.pushSubPiece(subindex); } } - // 唤醒其他可能没有工作的peer + // Wake up other possibly idle peers. if (flag) { for (var p in _activePeers) { if (p != peer && p.isSleeping) { @@ -756,6 +757,6 @@ class PeersManager with Holepunch, PEX { @override void holePunchRendezvous(CompactAddress ip) { // TODO: implement holePunchRendezvous - // print('收到 holePunch Rendezvous'); + print('Received holePunch Rendezvous.'); } } diff --git a/lib/src/peer/speed_calculator.dart b/lib/src/peer/speed_calculator.dart index 3557182..c997a96 100644 --- a/lib/src/peer/speed_calculator.dart +++ b/lib/src/peer/speed_calculator.dart @@ -3,11 +3,11 @@ import 'dart:math'; /// 5 seconds const RECORD_TIME = 5000000; -/// 上传下载速度计算器 +/// Upload and download speed calculator. mixin SpeedCalculator { final List> _downloadedHistory = >[]; - /// 当前5秒内的平均下载速度 + /// The average download speed within the last 5 seconds. double get currentDownloadSpeed { if (_downloadedHistory.isEmpty) return 0.0; var now = DateTime.now().microsecondsSinceEpoch; @@ -30,21 +30,21 @@ mixin SpeedCalculator { return (d / 1024) / (passed / 1000000); } - /// 从Peer连接开始到当前的平均下载速度 + /// The average download speed from the start of the Peer connection until the current moment. double get averageDownloadSpeed { var passed = livingTime; if (passed == null || passed == 0) return 0.0; return (_downloaded / 1024) / (passed / 1000000); } - /// 从Peer连接开始到当前的平均上传速度 + /// The average upload speed from the start of the Peer connection until the current moment. double get averageUploadSpeed { var passed = livingTime; if (passed == null || passed == 0) return 0.0; return (_uploaded / 1024) / (passed / 1000000); } - /// 从连接开始,直到peer销毁之前所持续时间 + /// The duration from the start of the connection until the peer is destroyed. int? get livingTime { if (_startTime == null) return null; var e = _endTime; @@ -58,20 +58,21 @@ mixin SpeedCalculator { int _downloaded = 0; - /// 从远程下载的总数据量,单位bytes + /// The total amount of data downloaded from the remote, in bytes. int get downloaded => _downloaded; int _uploaded = 0; - /// 上传到远程的总数据量,单位bytes + /// The total amount of data uploaded to the remote, in bytes. int get uploaded => _uploaded; - /// 更新下载 + /// Update the download. void updateDownload(List> requests) { if (requests.isEmpty) return; var downloaded = 0; for (var request in requests) { - if (request[4] != 0) continue; // 重新计时的不算 + if (request[4] != 0) + continue; // Do not count the time for re-calculation. downloaded += request[2]; } _downloadedHistory.add([downloaded, DateTime.now().microsecondsSinceEpoch]); @@ -82,12 +83,12 @@ mixin SpeedCalculator { _uploaded += uploaded; } - /// 速度计算开始计时 + /// Start the speed calculation timer. void startSpeedCalculator() { _startTime = DateTime.now().microsecondsSinceEpoch; } - /// 速度计算停止计时 + /// Stop the speed calculation timer. void stopSpeedCalculator() { _endTime = DateTime.now().microsecondsSinceEpoch; _downloadedHistory.clear(); diff --git a/lib/src/piece/base_piece_selector.dart b/lib/src/piece/base_piece_selector.dart index a68eed4..57e74d2 100644 --- a/lib/src/piece/base_piece_selector.dart +++ b/lib/src/piece/base_piece_selector.dart @@ -5,12 +5,12 @@ import 'piece_provider.dart'; import 'piece_selector.dart'; /// -/// `Piece`基础选择器。 +/// Basic piece selector. /// -/// 基本策略为: +/// The basic strategy is: /// -/// - `Piece`可用`Peer`数量最多 -/// - 在可用`Peer`数量都相同的情况下,选用`Sub Piece`数量最少的 +/// - Choose the Piece with the highest number of available Peers. +/// - If multiple Pieces have the same number of available Peers, choose the one with the fewest Sub Pieces remaining. class BasePieceSelector implements PieceSelector { @override Piece? selectPiece( @@ -39,7 +39,7 @@ class BasePieceSelector implements PieceSelector { !p.containsAvalidatePeer(remotePeerId)) { continue; } - // 选择稀有piece + // Select rare pieces if (a!.avalidatePeersCount > p.avalidatePeersCount) { if (!random) return p; maxList.clear(); @@ -47,7 +47,7 @@ class BasePieceSelector implements PieceSelector { maxList.add(a); } else { if (a.avalidatePeersCount == p.avalidatePeersCount) { - // 如果同样数量可用下载peer的piece所具有的sub piece少,优先处理 + // If multiple Pieces have the same number of available downloading Peers, prioritize the one with the fewest remaining Sub Pieces. if (p.avalidateSubPieceCount < a.avalidateSubPieceCount) { if (!random) return p; maxList.clear(); diff --git a/lib/src/piece/piece.dart b/lib/src/piece/piece.dart index a1d4e64..6b8f1de 100644 --- a/lib/src/piece/piece.dart +++ b/lib/src/piece/piece.dart @@ -75,11 +75,11 @@ class Piece { } /// - /// 子Piece下载完成。 + /// SubPiece download completed. /// - /// 将子piece放入 `_writtingSubPieces` 队列中 - /// 设置子Piece为完成状态。如果该子Piece已经设置过,返回`false`,没有设置 - /// 过说明设置成功,返回`true` + /// Put the subpiece into the _writtingSubPieces queue and mark it as completed. + /// If the subpiece has already been marked, return false; if it hasn't been marked + /// yet, mark it as completed and return true. bool subPieceDownloadComplete(int begin) { var subindex = begin ~/ DEFAULT_REQUEST_LENGTH; _subPiecesQueue.remove(subindex); @@ -88,7 +88,7 @@ class Piece { bool subPieceWriteComplete(int begin) { var subindex = begin ~/ DEFAULT_REQUEST_LENGTH; - // _subPiecesQueue.remove(subindex); // 有这可能? + // _subPiecesQueue.remove(subindex); // Is this possible? _writtingSubPieces.remove(subindex); var re = _downloadedSubPieces.add(subindex); if (isCompleted) { @@ -98,9 +98,10 @@ class Piece { } /// - ///子Piece [subIndex]是否还在。 + /// Whether the sub-piece [subIndex] is still available. /// - ///当子Piece被弹出栈用于下载,或者子Piece已经下载完成,那么就视为该Piece已经不再包含该子Piece + /// When a sub-piece is popped from the stack for download or if the sub-piece has already been downloaded, + /// the piece is considered to no longer contain that sub-piece. bool containsSubpiece(int subIndex) { return subPieceQueue.contains(subIndex); } diff --git a/lib/src/piece/piece_manager.dart b/lib/src/piece/piece_manager.dart index fd9a937..174369e 100644 --- a/lib/src/piece/piece_manager.dart +++ b/lib/src/piece/piece_manager.dart @@ -49,12 +49,12 @@ class PieceManager implements PieceProvider { _pieceCompleteHandles.remove(handle); } - /// 这个接口是用于FIleManager回调使用。 + /// This interface is used for FileManager callback. /// - /// 只有所有子Piece写入完成才认为该Piece算完成。 + /// Only when all sub-pieces have been written, the piece is considered complete. /// - /// 因为如果仅下载完成就修改bitfield,会造成发送have给对方后,对方请求的子piece还没在 - /// 文件系统中,会读取出错误的数据 + /// Because if we modify the bitfield only after downloading, it will cause the remote peer + /// to request sub-pieces that are not yet present in the file system, leading to errors in data reading. void processSubPieceWriteComplete(int pieceIndex, int begin, int length) { var piece = _pieces[pieceIndex]; if (piece != null) { @@ -65,9 +65,9 @@ class PieceManager implements PieceProvider { Piece? selectPiece(String remotePeerId, List remoteHavePieces, PieceProvider provider, final Set? suggestPieces) { - // 查看当前下载piece中是否可以使用该peer + // Check if the current downloading piece can be used by this peer. var avalidatePiece = []; - // 优先下载Suggest Pieces + // Prioritize downloading Suggest Pieces. if (suggestPieces != null && suggestPieces.isNotEmpty) { for (var i = 0; i < suggestPieces.length; i++) { var p = _pieces[suggestPieces.elementAt(i)]; @@ -86,7 +86,9 @@ class PieceManager implements PieceProvider { } } - // 如果可以下载正在下载中的piece,就下载该piece(多个Peer同时下载一个piece使其尽快完成的原则) + // If it is possible to download a piece that is currently being downloaded, + // prioritize downloading that piece (following the principle of multiple + // peers downloading the same piece to complete it as soon as possible). if (avalidatePiece.isNotEmpty) { candidatePieces = avalidatePiece; } @@ -102,10 +104,10 @@ class PieceManager implements PieceProvider { _donwloadingPieces.add(pieceIndex); } - /// 完成后的Piece需要一些处理 - /// - 从`_pieces`列表中删除 - /// - 从`_downloadingPieces`列表中删除 - /// - 通知监听器 + /// After completing a piece, some processing is required: + /// - Remove it from the _pieces list. + /// - Remove it from the _downloadingPieces list. + /// - Notify the listeners. void _processCompletePiece(int index) { var piece = _pieces.remove(index); _donwloadingPieces.remove(index); diff --git a/lib/src/piece/piece_selector.dart b/lib/src/piece/piece_selector.dart index 4e2eb5c..ff05454 100644 --- a/lib/src/piece/piece_selector.dart +++ b/lib/src/piece/piece_selector.dart @@ -1,16 +1,14 @@ import 'piece.dart'; import 'piece_provider.dart'; -/// Piece选择器。 +/// Piece selector. /// -/// 当客户端开始下载前,通过这个类选择出恰当的Piece来下载 +/// When the client starts downloading, this class selects appropriate Pieces to download. abstract class PieceSelector { - /// 选择恰当的Piece应该Peer下载. + /// Selects the appropriate Piece for the Peer to download. /// - /// [remotePeerId]是即将下载的`Peer`的标识,这个标识并**不一定**是协议中的`peer_id`, - /// 而是`Piece`类中区分`Peer`的标识。 - /// 该方法通过[provider]以及[piecesIndexList]获取对应的`Piece`对象,并在[piecesIndexList] - /// 集合中进行筛选。 + /// [remotePeerId] is the identifier of the Peer that is about to download. This identifier may not necessarily be the peer_id in the protocol, but rather a unique identifier used by the Piece class to distinguish Peers. + /// This method retrieves the corresponding Piece object using [provider] and [piecesIndexList], and filters it within the [piecesIndexList] collection. /// Piece? selectPiece( String remotePeerId, List piecesIndexList, PieceProvider provider, diff --git a/lib/src/task.dart b/lib/src/task.dart index ae5b613..280e013 100644 --- a/lib/src/task.dart +++ b/lib/src/task.dart @@ -93,12 +93,12 @@ abstract class TorrentTask { bool offResume(void Function() handler); - /// 增加DHT node,一般是将torrent文件中的nodes加入进去。 + /// Adding a DHT node usually involves adding the nodes from the torrent file into the DHT network. /// - /// 当然也可以直接添加已知的node地址 + /// Alternatively, you can directly add known node addresses. void addDHTNode(Uri uri); - /// 添加已知的Peer地址 + /// Add known Peer addresses. void addPeer(CompactAddress address, [PeerType type = PeerType.TCP, Socket socket]); } @@ -137,7 +137,8 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { final Set _peerIds = {}; - late String _peerId; // 这个是生成的本地peer的id,和Peer类的id是两回事 + late String + _peerId; // This is the generated local peer ID, which is different from the ID used in the Peer class. ServerSocket? _serverSocket; @@ -231,7 +232,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { } void _processLSDPeerEvent(CompactAddress address, String infoHash) { - print('居然有LSD!!'); + print('There is LSD! !'); } void _processNewPeerFound(CompactAddress url) { @@ -281,7 +282,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { @override Future start() async { - // 进入的peer: + // Incoming peer: _serverSocket ??= await ServerSocket.bind(InternetAddress.anyIPv4, 0); await _init(_metaInfo, _savePath); _serverSocket?.listen(_hookInPeer); @@ -297,7 +298,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { map['downloaded'] = _stateFile!.downloaded; map['uploaded'] = _stateFile!.uploaded; map['total_length'] = _metaInfo.length; - // 主动访问的peer: + // Outgoing peer: _tracker?.onPeerEvent(_processTrackerPeerEvent); _peersManager?.onAllComplete(_whenTaskDownloadComplete); _fileManager?.onFileComplete(_whenFileDownloadComplete); @@ -344,7 +345,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { _tracker?.offPeerEvent(_processTrackerPeerEvent); _peersManager?.offAllComplete(_whenTaskDownloadComplete); _fileManager?.offFileComplete(_whenFileDownloadComplete); - // 这是有顺序的,先停止tracker运行,然后停止监听serversocket以及所有的peer,最后关闭文件系统 + // This is in order, first stop the tracker, then stop listening on the server socket and all peers, finally close the file system. await _tracker?.dispose(); _tracker = null; await _peersManager?.dispose(); diff --git a/lib/torrent_task.dart b/lib/torrent_task.dart index 21d0191..5595391 100644 --- a/lib/torrent_task.dart +++ b/lib/torrent_task.dart @@ -7,10 +7,10 @@ export 'src/file/file_base.dart'; export 'src/piece/piece_base.dart'; export 'src/peer/peer_base.dart'; -/// Peer ID前缀 +/// Peer ID prefix const ID_PREFIX = '-DT0201-'; -/// 当前版本号 +/// Current version number Future getTorrenTaskVersion() async { var file = File('pubspec.yaml'); if (await file.exists()) { diff --git a/test/peer_communicate.dart b/test/peer_communicate.dart index 2e77866..3f366f7 100644 --- a/test/peer_communicate.dart +++ b/test/peer_communicate.dart @@ -125,8 +125,8 @@ void main() async { var id = String.fromCharCodes(block.getRange(2, 22)); assert(id == peer.remotePeerId); if (index == 4) { - print('测试完毕 $callMap'); - await peer.dispose(BadException('测试完成')); + print('Testing completed. $callMap'); + await peer.dispose(BadException('Testing completed')); } }); peer.onDispose((peer, [reason]) async { @@ -190,7 +190,7 @@ void main() async { view.setUint8(i + 2, idcontent[i]); } peer.sendPiece(index, begin, content); - peer.sendChoke(true); // 测试allow fast + peer.sendChoke(true); // Testing "allow fast". peer.sendAllowFast(4); }); peer.onDispose((peer, [reason]) async { diff --git a/test/torrent_client_test.dart b/test/torrent_client_test.dart index 3aa54ac..8930df4 100644 --- a/test/torrent_client_test.dart +++ b/test/torrent_client_test.dart @@ -12,7 +12,7 @@ import 'package:torrent_task/torrent_task.dart'; void main() { group('Bitfield test - ', () { Bitfield? bitfield; - var pieces = 123; // 不要给8的倍数 + var pieces = 123; // Do not provide a number that is multiple of 8 setUp(() { bitfield = Bitfield.createEmptyBitfield(pieces); }); @@ -28,7 +28,7 @@ void main() { }); test('random set/get test, and check the complete index ', () { - var t = Random(); //放大范围 + var t = Random(); // Increase the range. var randomIndex = {}; for (var i = 0; i < pieces; i++) { var index = t.nextInt(pieces * 2); @@ -70,7 +70,7 @@ void main() { }); test('add/remote complete list', () { - var t = Random(); //放大范围 + var t = Random(); //Increase the range. var randomIndex = {}; for (var i = 0; i < pieces; i++) { var index = t.nextInt(pieces * 2); @@ -101,7 +101,7 @@ void main() { group('Piece test - ', () { test('create sub-pieces', () async { - // 能整除的 + // Simulate bitfields that are divisible. var totalsize = 163840; var remain = Random().nextInt(100); totalsize = totalsize + remain; @@ -128,9 +128,9 @@ void main() { }); group('test same piece find - ', () { - var pieces = 123; // 不要给8的倍数 + var pieces = 123; // Do not provide a number that is multiple of 8 List bitfieldList = []; - // 模拟多个peer的bitfield: + // Simulate bitfields of multiple peers. setUp(() { bitfieldList = List.generate(10, (index) => Bitfield.createEmptyBitfield(pieces)); @@ -180,7 +180,7 @@ void main() { assert(!stateFile.bitfield.haveCompletePiece()); await stateFile.close(); - // 测试建立空文件后读取内容 + // To test reading the contents of an empty file after creating it. stateFile = await StateFile.getStateFile(directory, torrent!); b = torrent!.pieces.length ~/ 8; if (b * 8 != torrent!.pieces.length) b++; @@ -206,7 +206,7 @@ void main() { assert(stateFile.uploaded == 987654321); await stateFile.close(); - await stateFile.close(); //关闭两次会怎样? + await stateFile.close(); // What will happen if closed twice? var f = File( '$directory${Platform.pathSeparator}${torrent!.infoHash}.bt.state'); var locker = Completer(); @@ -248,7 +248,7 @@ void main() { '$directory${Platform.pathSeparator}${torrent!.infoHash}.bt.state'); assert(await t.exists()); await stateFile.delete(); - await stateFile.delete(); //删除两次 + await stateFile.delete(); //Deleting twice. assert(!await t.exists()); }); }); @@ -261,9 +261,9 @@ void main() { assert(await file.requestWrite(0, buffer, 0, buffer.length)); var bytes = await file.requestRead(0, buffer.length); var result = String.fromCharCodes(bytes); - assert(result == content, '验证读取内容错误'); + assert(result == content, 'Validating reading content error.'); await file.close(); - await file.close(); // 关闭两次 + await file.close(); // Closing twice. var file1 = File('test/test.txt'); assert(await file1.exists()); var b = []; @@ -272,13 +272,13 @@ void main() { b.addAll(data); }, onDone: () { var result = String.fromCharCodes(b); - assert(result == content, '验证文件内容错误'); + assert(result == content, 'File content verification error.'); lock.complete(); }); await lock.future; await file.delete(); file1 = File('test/test.txt'); - assert(!await file1.exists(), '文件删除错误'); + assert(!await file1.exists(), 'File deletion error.'); }); }); } From 4179ec6932d11c52f7c660137f824173a2ec2dcd Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Sun, 23 Jul 2023 14:41:26 +0300 Subject: [PATCH 05/18] Peer now has a source, debug logging --- example/metadata_example.dart | 3 +- example/torrent_task_comin_example.dart | 2 +- lib/src/metadata/metadata_downloader.dart | 16 +++--- lib/src/peer/extended_proccessor.dart | 1 + lib/src/peer/holepunch.dart | 2 + lib/src/peer/peer.dart | 64 +++++++++++++++++------ lib/src/peer/peers_manager.dart | 27 ++++++---- lib/src/task.dart | 23 +++++--- test/peer_communicate.dart | 6 ++- 9 files changed, 98 insertions(+), 46 deletions(-) diff --git a/example/metadata_example.dart b/example/metadata_example.dart index a13b62e..03dd2ab 100644 --- a/example/metadata_example.dart +++ b/example/metadata_example.dart @@ -3,6 +3,7 @@ import 'dart:typed_data'; import 'package:bencode_dart/bencode_dart.dart'; import 'package:dartorrent_common/dartorrent_common.dart'; import 'package:torrent_task/src/metadata/metadata_downloader.dart'; +import 'package:torrent_task/torrent_task.dart'; import 'package:torrent_tracker/torrent_tracker.dart'; void main(List args) async { @@ -27,7 +28,7 @@ void main(List args) async { if (event == null) return; var peers = event.peers; for (var element in peers) { - metadata.addNewPeerAddress(element); + metadata.addNewPeerAddress(element, PeerSource.tracker); } }); // ignore: unawaited_futures diff --git a/example/torrent_task_comin_example.dart b/example/torrent_task_comin_example.dart index f351b1c..26981f7 100644 --- a/example/torrent_task_comin_example.dart +++ b/example/torrent_task_comin_example.dart @@ -53,5 +53,5 @@ Future main() async { // }); // download from yourself task.addPeer(CompactAddress(InternetAddress.tryParse('192.168.0.24')!, 57331), - PeerType.UTP); + PeerSource.manual, PeerType.UTP); } diff --git a/lib/src/metadata/metadata_downloader.dart b/lib/src/metadata/metadata_downloader.dart index 34a3b67..1626dbe 100644 --- a/lib/src/metadata/metadata_downloader.dart +++ b/lib/src/metadata/metadata_downloader.dart @@ -109,7 +109,7 @@ class MetadataDownloader void _processDHTPeer(CompactAddress peer, String infoHash) { if (infoHash == _infoHashString) { - addNewPeerAddress(peer); + addNewPeerAddress(peer, PeerSource.dht); } } @@ -118,7 +118,7 @@ class MetadataDownloader /// /// Usually [socket] is null , unless this peer was incoming connection, but /// this type peer was managed by [TorrentTask] , user don't need to know that. - void addNewPeerAddress(CompactAddress address, + void addNewPeerAddress(CompactAddress address, PeerSource source, [PeerType type = PeerType.TCP, dynamic socket]) { if (!_running) return; if (address.address == localExtenelIP) return; @@ -132,12 +132,12 @@ class MetadataDownloader if (_peersAddress.add(address)) { Peer? peer; if (type == PeerType.TCP) { - peer = - Peer.newTCPPeer(_localPeerId, address, _infoHashBuffer, 0, socket); + peer = Peer.newTCPPeer( + _localPeerId, address, _infoHashBuffer, 0, socket, source); } if (type == PeerType.UTP) { - peer = - Peer.newUTPPeer(_localPeerId, address, _infoHashBuffer, 0, socket); + peer = Peer.newUTPPeer( + _localPeerId, address, _infoHashBuffer, 0, socket, source); } if (peer != null) _hookPeer(peer); } @@ -328,12 +328,12 @@ class MetadataDownloader peer.sendExtendMessage('ut_holepunch', message); return; } - addNewPeerAddress(address); + addNewPeerAddress(address, PeerSource.pex); } @override void holePunchConnect(CompactAddress ip) { - addNewPeerAddress(ip, PeerType.UTP); + addNewPeerAddress(ip, PeerSource.holepunch, PeerType.UTP); } @override diff --git a/lib/src/peer/extended_proccessor.dart b/lib/src/peer/extended_proccessor.dart index ad94c68..e532686 100644 --- a/lib/src/peer/extended_proccessor.dart +++ b/lib/src/peer/extended_proccessor.dart @@ -61,6 +61,7 @@ mixin ExtendedProcessor { } void processExtendHandshake(dynamic data) { + if (data == null || !(data as Map).containsKey('m')) return; var m = data['m'] as Map; _rawMap = m; m.forEach((key, value) { diff --git a/lib/src/peer/holepunch.dart b/lib/src/peer/holepunch.dart index 1d6184c..42500b4 100644 --- a/lib/src/peer/holepunch.dart +++ b/lib/src/peer/holepunch.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:developer'; import 'dart:io'; import 'dart:typed_data'; @@ -43,6 +44,7 @@ mixin Holepunch { /// /// err_code (4 bytes): void parseHolepuchMessage(List data) { + log('Parsing holepunch message', name: runtimeType.toString()); var type = data[0]; var iptype = data[1]; var offset = 0; diff --git a/lib/src/peer/peer.dart b/lib/src/peer/peer.dart index c097178..26cde2f 100644 --- a/lib/src/peer/peer.dart +++ b/lib/src/peer/peer.dart @@ -70,6 +70,8 @@ typedef BoolHandle = void Function(Peer peer, bool value); typedef SingleIntHandle = void Function(Peer peer, int value); +enum PeerSource { tracker, dht, pex, lsd, incoming, manual, holepunch } + abstract class Peer with PeerEventDispatcher, @@ -165,6 +167,8 @@ abstract class Peer final PeerType type; + final PeerSource source; + int reqq; int? remoteReqq; @@ -182,6 +186,7 @@ abstract class Peer /// [localEnableExtended] indicates whether local peers can use the /// [Extension Protocol](http://www.bittorrent.org/beps/bep_0010.html). Peer(this._localPeerId, this.address, this._infoHashBuffer, this._piecesNum, + this.source, {this.type = PeerType.TCP, this.localEnableFastPeer = true, this.localEnableExtended = true, @@ -189,17 +194,31 @@ abstract class Peer _remoteBitfield = Bitfield.createEmptyBitfield(_piecesNum); } - factory Peer.newTCPPeer(String localPeerId, CompactAddress address, - List infoHashBuffer, int piecesNum, Socket? socket, - {bool enableExtend = true, bool enableFast = true}) { - return _TCPPeer(localPeerId, address, infoHashBuffer, piecesNum, socket, + factory Peer.newTCPPeer( + String localPeerId, + CompactAddress address, + List infoHashBuffer, + int piecesNum, + Socket? socket, + PeerSource source, + {bool enableExtend = true, + bool enableFast = true}) { + return _TCPPeer( + localPeerId, address, infoHashBuffer, piecesNum, socket, source, enableExtend: enableExtend, enableFast: enableFast); } - factory Peer.newUTPPeer(String localPeerId, CompactAddress address, - List infoHashBuffer, int piecesNum, UTPSocket? socket, - {bool enableExtend = true, bool enableFast = true}) { - return _UTPPeer(localPeerId, address, infoHashBuffer, piecesNum, socket, + factory Peer.newUTPPeer( + String localPeerId, + CompactAddress address, + List infoHashBuffer, + int piecesNum, + UTPSocket? socket, + PeerSource source, + {bool enableExtend = true, + bool enableFast = true}) { + return _UTPPeer( + localPeerId, address, infoHashBuffer, piecesNum, socket, source, enableExtend: enableExtend, enableFast: enableFast); } @@ -704,7 +723,7 @@ abstract class Peer List.copyRange(block, 0, message, 8); requests.add(request); _log( - 'Received request for Piece ($index, $begin) content, downloaded $downloaded bytes from the current Peer.'); + 'Received request for Piece ($index, $begin) content, downloaded $downloaded bytes from the current Peer $type $address'); firePiece(index, begin, block); } messages.clear(); @@ -1159,12 +1178,17 @@ abstract class Peer void _log(String message, [dynamic error]) { if (error != null) { - // dev.log(message, error: error, name: runtimeType.toString()); + dev.log(message, error: error, name: runtimeType.toString()); } else { - // log(message, name: runtimeType.toString()); + dev.log(message, name: runtimeType.toString()); } } + @override + String toString() { + return '$type:$id $address $source'; + } + @override int get hashCode => address.address.address.hashCode; @@ -1194,9 +1218,9 @@ class TCPConnectException implements Exception { class _TCPPeer extends Peer { Socket? _socket; _TCPPeer(String localPeerId, CompactAddress address, List infoHashBuffer, - int piecesNum, this._socket, + int piecesNum, this._socket, PeerSource source, {bool enableExtend = true, bool enableFast = true}) - : super(localPeerId, address, infoHashBuffer, piecesNum, + : super(localPeerId, address, infoHashBuffer, piecesNum, source, type: PeerType.TCP, localEnableExtended: enableExtend, localEnableFastPeer: enableFast); @@ -1243,10 +1267,16 @@ class _TCPPeer extends Peer { class _UTPPeer extends Peer { UTPSocketClient? _client; UTPSocket? _socket; - _UTPPeer(String localPeerId, CompactAddress address, List infoHashBuffer, - int piecesNum, this._socket, - {bool enableExtend = true, bool enableFast = true}) - : super(localPeerId, address, infoHashBuffer, piecesNum, + _UTPPeer( + String localPeerId, + CompactAddress address, + List infoHashBuffer, + int piecesNum, + this._socket, + PeerSource source, { + bool enableExtend = true, + bool enableFast = true, + }) : super(localPeerId, address, infoHashBuffer, piecesNum, source, type: PeerType.UTP, localEnableExtended: enableExtend, localEnableFastPeer: enableFast); diff --git a/lib/src/peer/peers_manager.dart b/lib/src/peer/peers_manager.dart index 147f44f..ed6b36b 100644 --- a/lib/src/peer/peers_manager.dart +++ b/lib/src/peer/peers_manager.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:developer'; import 'dart:io'; import 'package:torrent_model/torrent_model.dart'; @@ -193,6 +194,8 @@ class PeersManager with Holepunch, PEX { /// Add supported extensions here void _registerExtended(Peer peer) { + log('registering extensions for peer ${peer.address}', + name: runtimeType.toString()); peer.registerExtened('ut_pex'); peer.registerExtened('ut_holepunch'); } @@ -220,6 +223,7 @@ class PeersManager with Holepunch, PEX { } void _processExtendedMessage(dynamic source, String name, dynamic data) { + log('Processing Extended Message $name', name: runtimeType.toString()); if (name == 'ut_holepunch') { parseHolepuchMessage(data); } @@ -247,7 +251,7 @@ class PeersManager with Holepunch, PEX { /// /// Usually [socket] is null , unless this peer was incoming connection, but /// this type peer was managed by [TorrentTask] , user don't need to know that. - void addNewPeerAddress(CompactAddress? address, + void addNewPeerAddress(CompactAddress? address, PeerSource source, [PeerType type = PeerType.TCP, dynamic socket]) { if (address == null) return; if (address.address == localExtenelIP) return; @@ -261,11 +265,11 @@ class PeersManager with Holepunch, PEX { Peer? peer; if (type == PeerType.TCP) { peer = Peer.newTCPPeer(_localPeerId, address, _metaInfo.infoHashBuffer, - _metaInfo.pieces.length, socket); + _metaInfo.pieces.length, socket, source); } if (type == PeerType.UTP) { peer = Peer.newUTPPeer(_localPeerId, address, _metaInfo.infoHashBuffer, - _metaInfo.pieces.length, socket); + _metaInfo.pieces.length, socket, source); } if (peer != null) _hookPeer(peer); } @@ -418,11 +422,15 @@ class PeersManager with Holepunch, PEX { if (reconnect) { if (_activePeers.length < MAX_ACTIVE_PEERS && !isDisposed) { - addNewPeerAddress(peer.address, peer.type); + addNewPeerAddress( + peer.address, + peer.source, + peer.type, + ); } } else { if (peer.isSeeder && !_fileManager.isAllComplete && !isDisposed) { - addNewPeerAddress(peer.address, peer.type); + addNewPeerAddress(peer.address, peer.source, peer.type); } } } @@ -711,7 +719,7 @@ class PeersManager with Holepunch, PEX { peer.sendExtendMessage('ut_holepunch', message); return; } - addNewPeerAddress(address); + addNewPeerAddress(address, PeerSource.pex); } @override @@ -719,7 +727,8 @@ class PeersManager with Holepunch, PEX { @override void holePunchConnect(CompactAddress ip) { - addNewPeerAddress(ip, PeerType.UTP); + log("holePunch connect $ip"); + addNewPeerAddress(ip, PeerSource.holepunch, PeerType.UTP); } int get utpPeerCount { @@ -751,12 +760,12 @@ class PeersManager with Holepunch, PEX { @override void holePunchError(String err, CompactAddress ip) { - // print('holepunch error - $err'); + log('holepunch error - $err'); } @override void holePunchRendezvous(CompactAddress ip) { // TODO: implement holePunchRendezvous - print('Received holePunch Rendezvous.'); + log('Received holePunch Rendezvous from $ip'); } } diff --git a/lib/src/task.dart b/lib/src/task.dart index 280e013..e17ab13 100644 --- a/lib/src/task.dart +++ b/lib/src/task.dart @@ -99,7 +99,7 @@ abstract class TorrentTask { void addDHTNode(Uri uri); /// Add known Peer addresses. - void addPeer(CompactAddress address, + void addPeer(CompactAddress address, PeerSource source, [PeerType type = PeerType.TCP, Socket socket]); } @@ -205,9 +205,9 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { } @override - void addPeer(CompactAddress address, + void addPeer(CompactAddress address, PeerSource source, [PeerType type = PeerType.TCP, Socket? socket]) { - _peersManager?.addNewPeerAddress(address, type, socket); + _peersManager?.addNewPeerAddress(address, source, type, socket); } void _whenTaskDownloadComplete() async { @@ -226,7 +226,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { var ps = event.peers; if (ps.isNotEmpty) { for (var url in ps) { - _processNewPeerFound(url); + _processNewPeerFound(url, PeerSource.tracker); } } } @@ -235,13 +235,17 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { print('There is LSD! !'); } - void _processNewPeerFound(CompactAddress url) { - _peersManager?.addNewPeerAddress(url); + void _processNewPeerFound(CompactAddress url, PeerSource source) { + log("Add new dht peer ${url.toString()} to peersManager", + name: runtimeType.toString()); + _peersManager?.addNewPeerAddress(url, source); } void _processDHTPeer(CompactAddress peer, String infoHash) { + log("Got new peer from $peer DHT for infohash: ${Uint8List.fromList(infoHash.codeUnits).toHexString()}", + name: runtimeType.toString()); if (infoHash == _infoHashString) { - _processNewPeerFound(peer); + _processNewPeerFound(peer, PeerSource.dht); } } @@ -257,7 +261,10 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { log('incoming connect: ${socket.remoteAddress.address}:${socket.remotePort}', name: runtimeType.toString()); _peersManager?.addNewPeerAddress( - CompactAddress(socket.address, socket.port), PeerType.TCP, socket); + CompactAddress(socket.address, socket.port), + PeerSource.incoming, + PeerType.TCP, + socket); } @override diff --git a/test/peer_communicate.dart b/test/peer_communicate.dart index 3f366f7..a3a0d59 100644 --- a/test/peer_communicate.dart +++ b/test/peer_communicate.dart @@ -42,7 +42,8 @@ void main() async { CompactAddress(socket.address, socket.port), infoBuffer, piecesNum, - socket); + socket, + PeerSource.incoming); peer.onConnect((peer) { callMap['connect1'] = true; peer.sendHandShake(); @@ -143,7 +144,8 @@ void main() async { CompactAddress(InternetAddress.tryParse('127.0.0.1')!, serverPort), infoBuffer, piecesNum, - null); + null, + PeerSource.manual); peer.onConnect((peer) { callMap['connect2'] = true; print('connect server success'); From 1d6afb7f1e47bc9ce56cfd947cbad5dca9370433 Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Mon, 24 Jul 2023 16:17:51 +0300 Subject: [PATCH 06/18] Correct debug message --- lib/src/task.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/task.dart b/lib/src/task.dart index e17ab13..a13fcbc 100644 --- a/lib/src/task.dart +++ b/lib/src/task.dart @@ -236,7 +236,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { } void _processNewPeerFound(CompactAddress url, PeerSource source) { - log("Add new dht peer ${url.toString()} to peersManager", + log("Add new peer ${url.toString()} from ${source.name} to peersManager", name: runtimeType.toString()); _peersManager?.addNewPeerAddress(url, source); } From 0718a2863cfb81bd6e000a1ae3ff55dc04f91c6b Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Tue, 25 Jul 2023 10:19:48 +0300 Subject: [PATCH 07/18] correct typing --- lib/src/lsd/lsd.dart | 2 +- lib/src/peer/extended_proccessor.dart | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/src/lsd/lsd.dart b/lib/src/lsd/lsd.dart index 8f17776..5053378 100644 --- a/lib/src/lsd/lsd.dart +++ b/lib/src/lsd/lsd.dart @@ -36,7 +36,7 @@ class LSD { Timer? _timer; - void start() async { + Future start() async { _socket ??= await RawDatagramSocket.bind(InternetAddress.anyIPv4, LSD_PORT); _socket?.listen((event) { if (event == RawSocketEvent.read) { diff --git a/lib/src/peer/extended_proccessor.dart b/lib/src/peer/extended_proccessor.dart index e532686..a6d086e 100644 --- a/lib/src/peer/extended_proccessor.dart +++ b/lib/src/peer/extended_proccessor.dart @@ -61,8 +61,10 @@ mixin ExtendedProcessor { } void processExtendHandshake(dynamic data) { - if (data == null || !(data as Map).containsKey('m')) return; - var m = data['m'] as Map; + if (data == null || !(data as Map).containsKey('m')) { + return; + } + var m = data['m'] as Map; _rawMap = m; m.forEach((key, value) { if (value == 0) return; From fecfef5daad1b36ae5ac7fa40397e1608cb603f5 Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Tue, 25 Jul 2023 10:23:01 +0300 Subject: [PATCH 08/18] Add the remote ip and port and not the local --- lib/src/task.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/task.dart b/lib/src/task.dart index a13fcbc..97a3520 100644 --- a/lib/src/task.dart +++ b/lib/src/task.dart @@ -261,7 +261,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { log('incoming connect: ${socket.remoteAddress.address}:${socket.remotePort}', name: runtimeType.toString()); _peersManager?.addNewPeerAddress( - CompactAddress(socket.address, socket.port), + CompactAddress(socket.remoteAddress, socket.remotePort), PeerSource.incoming, PeerType.TCP, socket); From be30e87eba52518b1537c331d20dea8adbfe4cfb Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Tue, 25 Jul 2023 10:36:16 +0300 Subject: [PATCH 09/18] Get the public ip when starting Don't wait until a handshake happens --- lib/src/metadata/metadata_downloader.dart | 15 ++++++++++----- lib/src/peer/peers_manager.dart | 17 +++++++++++------ pubspec.yaml | 1 + 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/lib/src/metadata/metadata_downloader.dart b/lib/src/metadata/metadata_downloader.dart index 1626dbe..86916eb 100644 --- a/lib/src/metadata/metadata_downloader.dart +++ b/lib/src/metadata/metadata_downloader.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:bencode_dart/bencode_dart.dart'; +import 'package:dart_ipify/dart_ipify.dart'; import 'package:dartorrent_common/dartorrent_common.dart'; import 'package:dht_dart/dht_dart.dart'; import 'package:torrent_tracker/torrent_tracker.dart'; @@ -24,7 +25,7 @@ class MetadataDownloader InternetAddress.tryParse('127.0.0.1')! ]; - InternetAddress? localExtenelIP; + InternetAddress? localExternalIP; int? _metaDataSize; @@ -67,6 +68,10 @@ class MetadataDownloader _infoHashBuffer = hexString2Buffer(_infoHashString)!; assert(_infoHashBuffer.isNotEmpty && _infoHashBuffer.length == 20, 'Info Hash String is incorrect'); + _init(); + } + Future _init() async { + localExternalIP = InternetAddress.tryParse(await Ipify.ipv4()); } void startDownload() { @@ -121,7 +126,7 @@ class MetadataDownloader void addNewPeerAddress(CompactAddress address, PeerSource source, [PeerType type = PeerType.TCP, dynamic socket]) { if (!_running) return; - if (address.address == localExtenelIP) return; + if (address.address == localExternalIP) return; if (socket != null) { // Indicates that it is an actively connecting peer, and currently, only // one connection per IP address is allowed. @@ -144,7 +149,7 @@ class MetadataDownloader } void _hookPeer(Peer peer) { - if (peer.address.address == localExtenelIP) return; + if (peer.address.address == localExternalIP) return; if (_peerExsist(peer)) return; peer.onDispose(_processPeerDispose); peer.onHandShake(_processPeerHandshake); @@ -216,7 +221,7 @@ class MetadataDownloader } } - if (localExtenelIP != null && + if (localExternalIP != null && data['yourip'] != null && (data['yourip'].length == 4 || data['yourip'].length == 16)) { InternetAddress myip; @@ -226,7 +231,7 @@ class MetadataDownloader return; } if (IGNORE_IPS.contains(myip)) return; - localExtenelIP = InternetAddress.fromRawAddress(data['yourip']); + localExternalIP = InternetAddress.fromRawAddress(data['yourip']); } var metaDataEventId = peer.getExtendedEventId('ut_metadata'); diff --git a/lib/src/peer/peers_manager.dart b/lib/src/peer/peers_manager.dart index ed6b36b..08a6c03 100644 --- a/lib/src/peer/peers_manager.dart +++ b/lib/src/peer/peers_manager.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:developer'; import 'dart:io'; +import 'package:dart_ipify/dart_ipify.dart'; import 'package:torrent_model/torrent_model.dart'; import 'package:dartorrent_common/dartorrent_common.dart'; @@ -40,7 +41,7 @@ class PeersManager with Holepunch, PEX { final Set _incomingAddress = {}; - InternetAddress? localExtenelIP; + InternetAddress? localExternalIP; /// The maximum size of the disk write cache. int maxWriteBufferSize; @@ -88,11 +89,15 @@ class PeersManager with Holepunch, PEX { _fileManager.onSubPieceWriteComplete(_processSubPieceWriteComplte); _fileManager.onSubPieceReadComplete(readSubPieceComplete); _pieceManager.onPieceComplete(_processPieceWriteComplete); - + _init(); // Start pex interval startPEX(); } + Future _init() async { + localExternalIP = InternetAddress.tryParse(await Ipify.ipv4()); + } + /// Task is paused bool get isPaused => _paused; @@ -170,7 +175,7 @@ class PeersManager with Holepunch, PEX { } void _hookPeer(Peer peer) { - if (peer.address.address == localExtenelIP) return; + if (peer.address.address == localExternalIP) return; if (_peerExsist(peer)) return; peer.onDispose(_processPeerDispose); peer.onBitfield(_processBitfieldUpdate); @@ -231,7 +236,7 @@ class PeersManager with Holepunch, PEX { parsePEXDatas(source, data); } if (name == 'handshake') { - if (localExtenelIP != null && + if (localExternalIP != null && data['yourip'] != null && (data['yourip'].length == 4 || data['yourip'].length == 16)) { InternetAddress myip; @@ -241,7 +246,7 @@ class PeersManager with Holepunch, PEX { return; } if (IGNORE_IPS.contains(myip)) return; - localExtenelIP = InternetAddress.fromRawAddress(data['yourip']); + localExternalIP = InternetAddress.fromRawAddress(data['yourip']); } } } @@ -254,7 +259,7 @@ class PeersManager with Holepunch, PEX { void addNewPeerAddress(CompactAddress? address, PeerSource source, [PeerType type = PeerType.TCP, dynamic socket]) { if (address == null) return; - if (address.address == localExtenelIP) return; + if (address.address == localExternalIP) return; if (socket != null) { // Indicates that it is an actively connected peer, and currently, only one IP address is allowed to connect at a time. if (!_incomingAddress.add(address.address)) { diff --git a/pubspec.yaml b/pubspec.yaml index 5b3e0c8..5b7c5fd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,6 +15,7 @@ dependencies: dartorrent_common: ^1.0.4 bencode_dart: ^1.0.3 utp: ^1.0.1 + dart_ipify: ^1.1.1 dev_dependencies: lints: ^2.0.1 From ba896634221486d3f0c07ab088d8b58d0a19ec90 Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Wed, 2 Aug 2023 23:44:28 +0300 Subject: [PATCH 10/18] better toString for peer --- lib/src/peer/peer.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/peer/peer.dart b/lib/src/peer/peer.dart index 26cde2f..7f117a4 100644 --- a/lib/src/peer/peer.dart +++ b/lib/src/peer/peer.dart @@ -1186,7 +1186,7 @@ abstract class Peer @override String toString() { - return '$type:$id $address $source'; + return '$type:${_remotePeerId?.codeUnits != null ? Uint8List.fromList(_remotePeerId!.codeUnits).toHexString() : ''} $address $source'; } @override From 90946eb4debb859ae6d9e03c3ae95e4ed94b2e03 Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Wed, 2 Aug 2023 23:45:09 +0300 Subject: [PATCH 11/18] Allow looser type --- lib/src/peer/extended_proccessor.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/peer/extended_proccessor.dart b/lib/src/peer/extended_proccessor.dart index a6d086e..4e20ab7 100644 --- a/lib/src/peer/extended_proccessor.dart +++ b/lib/src/peer/extended_proccessor.dart @@ -64,7 +64,7 @@ mixin ExtendedProcessor { if (data == null || !(data as Map).containsKey('m')) { return; } - var m = data['m'] as Map; + var m = data['m'] as Map; _rawMap = m; m.forEach((key, value) { if (value == 0) return; From 9bc3bb1ce36ae53a1aaa090c8eea1b7ef8e96f4f Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Wed, 2 Aug 2023 23:50:27 +0300 Subject: [PATCH 12/18] If no peer type passed,try to connect in both --- example/torrent_task_comin_example.dart | 2 +- lib/src/peer/peers_manager.dart | 13 +++++++------ lib/src/task.dart | 10 +++++----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/example/torrent_task_comin_example.dart b/example/torrent_task_comin_example.dart index 26981f7..dbffa4b 100644 --- a/example/torrent_task_comin_example.dart +++ b/example/torrent_task_comin_example.dart @@ -53,5 +53,5 @@ Future main() async { // }); // download from yourself task.addPeer(CompactAddress(InternetAddress.tryParse('192.168.0.24')!, 57331), - PeerSource.manual, PeerType.UTP); + PeerSource.manual); } diff --git a/lib/src/peer/peers_manager.dart b/lib/src/peer/peers_manager.dart index 08a6c03..fb83653 100644 --- a/lib/src/peer/peers_manager.dart +++ b/lib/src/peer/peers_manager.dart @@ -257,8 +257,9 @@ class PeersManager with Holepunch, PEX { /// Usually [socket] is null , unless this peer was incoming connection, but /// this type peer was managed by [TorrentTask] , user don't need to know that. void addNewPeerAddress(CompactAddress? address, PeerSource source, - [PeerType type = PeerType.TCP, dynamic socket]) { + {PeerType? type, dynamic socket}) { if (address == null) return; + if (IGNORE_IPS.contains(address.address)) return; if (address.address == localExternalIP) return; if (socket != null) { // Indicates that it is an actively connected peer, and currently, only one IP address is allowed to connect at a time. @@ -268,11 +269,11 @@ class PeersManager with Holepunch, PEX { } if (_peersAddress.add(address)) { Peer? peer; - if (type == PeerType.TCP) { + if (type == null || type == PeerType.TCP) { peer = Peer.newTCPPeer(_localPeerId, address, _metaInfo.infoHashBuffer, _metaInfo.pieces.length, socket, source); } - if (type == PeerType.UTP) { + if (type == null || type == PeerType.UTP) { peer = Peer.newUTPPeer(_localPeerId, address, _metaInfo.infoHashBuffer, _metaInfo.pieces.length, socket, source); } @@ -430,12 +431,12 @@ class PeersManager with Holepunch, PEX { addNewPeerAddress( peer.address, peer.source, - peer.type, + type: peer.type, ); } } else { if (peer.isSeeder && !_fileManager.isAllComplete && !isDisposed) { - addNewPeerAddress(peer.address, peer.source, peer.type); + addNewPeerAddress(peer.address, peer.source, type: peer.type); } } } @@ -733,7 +734,7 @@ class PeersManager with Holepunch, PEX { @override void holePunchConnect(CompactAddress ip) { log("holePunch connect $ip"); - addNewPeerAddress(ip, PeerSource.holepunch, PeerType.UTP); + addNewPeerAddress(ip, PeerSource.holepunch, type: PeerType.UTP); } int get utpPeerCount { diff --git a/lib/src/task.dart b/lib/src/task.dart index 97a3520..bd21de7 100644 --- a/lib/src/task.dart +++ b/lib/src/task.dart @@ -100,7 +100,7 @@ abstract class TorrentTask { /// Add known Peer addresses. void addPeer(CompactAddress address, PeerSource source, - [PeerType type = PeerType.TCP, Socket socket]); + {PeerType? type, Socket socket}); } class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { @@ -206,8 +206,9 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { @override void addPeer(CompactAddress address, PeerSource source, - [PeerType type = PeerType.TCP, Socket? socket]) { - _peersManager?.addNewPeerAddress(address, source, type, socket); + {PeerType? type, Socket? socket}) { + _peersManager?.addNewPeerAddress(address, source, + type: type, socket: socket); } void _whenTaskDownloadComplete() async { @@ -263,8 +264,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { _peersManager?.addNewPeerAddress( CompactAddress(socket.remoteAddress, socket.remotePort), PeerSource.incoming, - PeerType.TCP, - socket); + socket: socket); } @override From ff222fb33b48d2b715c0b4318fe814648596ab06 Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Wed, 9 Aug 2023 19:57:15 +0300 Subject: [PATCH 13/18] lsd reuse port --- lib/src/lsd/lsd.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/lsd/lsd.dart b/lib/src/lsd/lsd.dart index 5053378..cbba8d4 100644 --- a/lib/src/lsd/lsd.dart +++ b/lib/src/lsd/lsd.dart @@ -37,7 +37,8 @@ class LSD { Timer? _timer; Future start() async { - _socket ??= await RawDatagramSocket.bind(InternetAddress.anyIPv4, LSD_PORT); + _socket ??= await RawDatagramSocket.bind(InternetAddress.anyIPv4, LSD_PORT, + reusePort: true); _socket?.listen((event) { if (event == RawSocketEvent.read) { var datagram = _socket?.receive(); From e9858f2bfae2932efac738d6833c21dc1e70facb Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Wed, 9 Aug 2023 19:59:33 +0300 Subject: [PATCH 14/18] add progress to metadata downloader --- lib/src/metadata/metadata_downloader.dart | 4 ++ lib/src/metadata/metadata_messager.dart | 46 +++++++++++------------ 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/lib/src/metadata/metadata_downloader.dart b/lib/src/metadata/metadata_downloader.dart index 86916eb..63f5683 100644 --- a/lib/src/metadata/metadata_downloader.dart +++ b/lib/src/metadata/metadata_downloader.dart @@ -33,6 +33,10 @@ class MetadataDownloader int? get metaDataSize => _metaDataSize; + num get progress => _metaDataBlockNum != null + ? _completedPieces.length / _metaDataBlockNum! * 100 + : 0; + late String _localPeerId; late List _infoHashBuffer; diff --git a/lib/src/metadata/metadata_messager.dart b/lib/src/metadata/metadata_messager.dart index b4563b5..e38e20f 100644 --- a/lib/src/metadata/metadata_messager.dart +++ b/lib/src/metadata/metadata_messager.dart @@ -1,28 +1,28 @@ import 'package:bencode_dart/bencode_dart.dart'; -mixin MetaDataMessager {} +mixin MetaDataMessager { + List createRequestMessage(int piece) { + // {'msg_type': 0, 'piece': 0} + var message = {}; + message['msg_type'] = 0; + message['piece'] = piece; + return encode(message); + } -List createRequestMessage(int piece) { - // {'msg_type': 0, 'piece': 0} - var message = {}; - message['msg_type'] = 0; - message['piece'] = piece; - return encode(message); -} - -List createRejectMessage(int piece) { - // {'msg_type': 2, 'piece': 0} - var message = {}; - message['msg_type'] = 2; - message['piece'] = piece; - return encode(message); -} + List createRejectMessage(int piece) { + // {'msg_type': 2, 'piece': 0} + var message = {}; + message['msg_type'] = 2; + message['piece'] = piece; + return encode(message); + } -List createDataMessage(int piece, List bytes) { - // {'msg_type': 1, 'piece': 0 , 'total_size' : xxxx}xxxx - var message = {}; - message['msg_type'] = 1; - message['piece'] = piece; - message['total_size'] = bytes.length; - return encode(message); + List createDataMessage(int piece, List bytes) { + // {'msg_type': 1, 'piece': 0 , 'total_size' : xxxx}xxxx + var message = {}; + message['msg_type'] = 1; + message['piece'] = piece; + message['total_size'] = bytes.length; + return encode(message); + } } From 0980ccf6548fffa62855d2a94d1d45c2de617623 Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Thu, 10 Aug 2023 21:09:03 +0300 Subject: [PATCH 15/18] Enable utp peers --- lib/src/task.dart | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/src/task.dart b/lib/src/task.dart index bd21de7..474c020 100644 --- a/lib/src/task.dart +++ b/lib/src/task.dart @@ -7,6 +7,7 @@ import 'package:torrent_model/torrent_model.dart'; import 'package:torrent_tracker/torrent_tracker.dart'; import 'package:dartorrent_common/dartorrent_common.dart'; import 'package:dht_dart/dht_dart.dart'; +import 'package:utp/utp.dart'; import 'file/download_file_manager.dart'; import 'file/state_file.dart'; @@ -141,6 +142,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { _peerId; // This is the generated local peer ID, which is different from the ID used in the Peer class. ServerSocket? _serverSocket; + ServerUTPSocket? _utpServer; final Set _cominIp = {}; @@ -250,6 +252,24 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { } } + void _hookUTP(UTPSocket socket) { + if (socket.remoteAddress == LOCAL_ADDRESS) { + socket.close(); + return; + } + if (_cominIp.length >= MAX_IN_PEERS || !_cominIp.add(socket.address)) { + socket.close(); + return; + } + log('incoming connect: ${socket.remoteAddress.address}:${socket.remotePort}', + name: runtimeType.toString()); + _peersManager?.addNewPeerAddress( + CompactAddress(socket.remoteAddress, socket.remotePort), + PeerSource.incoming, + type: PeerType.UTP, + socket: socket); + } + void _hookInPeer(Socket socket) { if (socket.remoteAddress == LOCAL_ADDRESS) { socket.close(); @@ -264,6 +284,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { _peersManager?.addNewPeerAddress( CompactAddress(socket.remoteAddress, socket.remotePort), PeerSource.incoming, + type: PeerType.TCP, socket: socket); } @@ -293,9 +314,10 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { _serverSocket ??= await ServerSocket.bind(InternetAddress.anyIPv4, 0); await _init(_metaInfo, _savePath); _serverSocket?.listen(_hookInPeer); - // _utpServer ??= await ServerUTPSocket.bind(InternetAddress.anyIPv4, 0); - // _utpServer.listen(_hookUTP); - // print(_utpServer.port); + _utpServer ??= await ServerUTPSocket.bind( + InternetAddress.anyIPv4, _serverSocket?.port ?? 0); + _utpServer?.listen(_hookUTP); + print(_utpServer?.port); var map = {}; map['name'] = _metaInfo.name; @@ -311,7 +333,7 @@ class _TorrentTask implements TorrentTask, AnnounceOptionsProvider { _fileManager?.onFileComplete(_whenFileDownloadComplete); _lsd?.onLSDPeer(_processLSDPeerEvent); - _lsd?.port = _serverSocket?.port; + _lsd?.port = _utpServer?.port; _lsd?.start(); _dht?.announce( From a3e6f49c0451e541b8a378d9d84bed8bd755e6b9 Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Thu, 10 Aug 2023 21:09:43 +0300 Subject: [PATCH 16/18] start task when metadata is fetched --- example/metadata_example.dart | 44 ++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/example/metadata_example.dart b/example/metadata_example.dart index 03dd2ab..c502556 100644 --- a/example/metadata_example.dart +++ b/example/metadata_example.dart @@ -1,7 +1,9 @@ +import 'dart:async'; import 'dart:typed_data'; import 'package:bencode_dart/bencode_dart.dart'; import 'package:dartorrent_common/dartorrent_common.dart'; +import 'package:torrent_model/torrent_model.dart'; import 'package:torrent_task/src/metadata/metadata_downloader.dart'; import 'package:torrent_task/torrent_task.dart'; import 'package:torrent_tracker/torrent_tracker.dart'; @@ -16,10 +18,46 @@ void main(List args) async { var tracker = TorrentAnnounceTracker(metadata); // When metadata contents download complete , it will send this event and stop itself: - metadata.onDownloadComplete((data) { - var msg = decode(Uint8List.fromList(data)); - print('complete , info : $msg'); + metadata.onDownloadComplete((data) async { tracker.stop(true); + var msg = decode(Uint8List.fromList(data)); + Map torrent = {}; + torrent['info'] = msg; + var torrentModel = parseTorrentFileContent(torrent); + if (torrentModel != null) { + print('complete , info : ${torrentModel.name}'); + var startTime = DateTime.now().millisecondsSinceEpoch; + var task = TorrentTask.newTask(torrentModel, 'tmp'); + Timer? timer; + task.onTaskComplete(() { + print( + 'Complete! spend time : ${((DateTime.now().millisecondsSinceEpoch - startTime) / 60000).toStringAsFixed(2)} minutes'); + timer?.cancel(); + task.stop(); + }); + task.onStop(() async { + print('Task Stopped'); + }); + timer = Timer.periodic(Duration(seconds: 2), (timer) async { + var progress = '${(task.progress * 100).toStringAsFixed(2)}%'; + var ads = + ((task.averageDownloadSpeed) * 1000 / 1024).toStringAsFixed(2); + var aps = ((task.averageUploadSpeed) * 1000 / 1024).toStringAsFixed(2); + var ds = ((task.currentDownloadSpeed) * 1000 / 1024).toStringAsFixed(2); + var ps = ((task.uploadSpeed) * 1000 / 1024).toStringAsFixed(2); + + var utpd = ((task.utpDownloadSpeed) * 1000 / 1024).toStringAsFixed(2); + var utpu = ((task.utpUploadSpeed) * 1000 / 1024).toStringAsFixed(2); + var utpc = task.utpPeerCount; + + var active = task.connectedPeersNumber; + var seeders = task.seederNumber; + var all = task.allPeersNumber; + print( + 'Progress : $progress , Peers:($active/$seeders/$all)($utpc) . Download speed : ($utpd)($ads/$ds)kb/s , upload speed : ($utpu)($aps/$ps)kb/s'); + }); + await task.start(); + } }); var u8List = Uint8List.fromList(metadata.infoHashBuffer); From 29eed823b92f3171877bfc3db02ed75472bdb5d1 Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Mon, 14 Aug 2023 18:26:28 +0300 Subject: [PATCH 17/18] remove pubspec.lock --- pubspec.lock | 404 --------------------------------------------------- 1 file changed, 404 deletions(-) delete mode 100644 pubspec.lock diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index d23ecaa..0000000 --- a/pubspec.lock +++ /dev/null @@ -1,404 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - url: "https://pub.flutter-io.cn" - source: hosted - version: "14.0.0" - analyzer: - dependency: transitive - description: - name: analyzer - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.41.1" - args: - dependency: transitive - description: - name: args - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.6.0" - async: - dependency: transitive - description: - name: async - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.4.2" - bencode_dart: - dependency: "direct main" - description: - name: bencode_dart - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.2" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.0.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.1.3" - cli_util: - dependency: transitive - description: - name: cli_util - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.2.0" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.15.0" - convert: - dependency: transitive - description: - name: convert - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.1.1" - coverage: - dependency: transitive - description: - name: coverage - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.14.2" - crypto: - dependency: transitive - description: - name: crypto - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.1.5" - dartorrent_common: - dependency: "direct main" - description: - name: dartorrent_common - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.3" - dht_dart: - dependency: "direct main" - description: - name: dht_dart - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.0.7" - file: - dependency: transitive - description: - name: file - url: "https://pub.flutter-io.cn" - source: hosted - version: "5.2.1" - glob: - dependency: transitive - description: - name: glob - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.0" - http: - dependency: transitive - description: - name: http - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.12.2" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.2.0" - http_parser: - dependency: transitive - description: - name: http_parser - url: "https://pub.flutter-io.cn" - source: hosted - version: "3.1.4" - intl: - dependency: transitive - description: - name: intl - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.16.1" - io: - dependency: transitive - description: - name: io - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.3.4" - js: - dependency: transitive - description: - name: js - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.6.2" - logging: - dependency: transitive - description: - name: logging - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.11.4" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.12.9" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.3.0" - mime: - dependency: transitive - description: - name: mime - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.9.7" - node_interop: - dependency: transitive - description: - name: node_interop - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.1" - node_io: - dependency: transitive - description: - name: node_io - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.0" - node_preamble: - dependency: transitive - description: - name: node_preamble - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.4.12" - package_config: - dependency: transitive - description: - name: package_config - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.9.3" - path: - dependency: transitive - description: - name: path - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.7.0" - pedantic: - dependency: "direct dev" - description: - name: pedantic - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.9.2" - pool: - dependency: transitive - description: - name: pool - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.4.0" - pub_semver: - dependency: transitive - description: - name: pub_semver - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.4.4" - shelf: - dependency: transitive - description: - name: shelf - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.7.9" - shelf_packages_handler: - dependency: transitive - description: - name: shelf_packages_handler - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.0.0" - shelf_static: - dependency: transitive - description: - name: shelf_static - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.2.8" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.2.3" - source_map_stack_trace: - dependency: transitive - description: - name: source_map_stack_trace - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.0.0" - source_maps: - dependency: transitive - description: - name: source_maps - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.10.9" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.7.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.9.6" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.0.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.5" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.1.0" - test: - dependency: "direct dev" - description: - name: test - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.15.7" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.2.18+1" - test_core: - dependency: transitive - description: - name: test_core - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.3.11+4" - torrent_model: - dependency: "direct main" - description: - name: torrent_model - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.3" - torrent_tracker: - dependency: "direct main" - description: - name: torrent_tracker - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.3.12" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.3.0" - utp: - dependency: "direct main" - description: - name: utp - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.1" - vm_service: - dependency: transitive - description: - name: vm_service - url: "https://pub.flutter-io.cn" - source: hosted - version: "4.2.0" - watcher: - dependency: transitive - description: - name: watcher - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.9.7+15" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.1.0" - webkit_inspection_protocol: - dependency: transitive - description: - name: webkit_inspection_protocol - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.7.4" - yaml: - dependency: transitive - description: - name: yaml - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.2.1" -sdks: - dart: ">=2.12.0-0 <3.0.0" From a146c3025b36150d5dc068c60df270f406066ff1 Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Mon, 14 Aug 2023 18:27:51 +0300 Subject: [PATCH 18/18] rename package, prepare to publish --- CHANGELOG.md | 5 +- README.md | 10 +- example/lsd_example.dart | 6 +- example/metadata_example.dart | 12 +- example/torrent_task_comin_example.dart | 6 +- example/torrent_task_example.dart | 6 +- lib/{torrent_task.dart => dtorrent_task.dart} | 2 +- lib/src/file/download_file_manager.dart | 2 +- lib/src/file/state_file.dart | 2 +- lib/src/lsd/lsd.dart | 2 +- lib/src/metadata/metadata_downloader.dart | 8 +- lib/src/metadata/metadata_messager.dart | 2 +- lib/src/peer/extended_proccessor.dart | 2 +- lib/src/peer/holepunch.dart | 2 +- lib/src/peer/peer.dart | 8 +- lib/src/peer/peers_manager.dart | 4 +- lib/src/peer/pex.dart | 4 +- lib/src/piece/base_piece_selector.dart | 2 +- lib/src/piece/piece_manager.dart | 2 +- lib/src/stream/stream.dart | 0 lib/src/stream/torrent_stream.dart | 190 ++++++++++++++++++ lib/src/task.dart | 10 +- lib/src/utils.dart | 4 +- pubspec.yaml | 21 +- test/peer_communicate.dart | 4 +- test/torrent_client_test.dart | 6 +- 26 files changed, 257 insertions(+), 65 deletions(-) rename lib/{torrent_task.dart => dtorrent_task.dart} (96%) create mode 100644 lib/src/stream/stream.dart create mode 100644 lib/src/stream/torrent_stream.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index fc5948d..25440fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,4 +31,7 @@ - Change congestion control ## 0.3.0 -- Add Send Metadata extension (BEP0009) \ No newline at end of file +- Add Send Metadata extension (BEP0009) + +## 0.3.1 +- nullsafety \ No newline at end of file diff --git a/README.md b/README.md index a18b36e..4595c3e 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ Dart library for implementing BitTorrent client. Whole Dart Torrent client contains serival parts : -- [Bencode](https://pub.dev/packages/bencode_dart) +- [Bencode](https://pub.dev/packages/b_encode_decode) - [Tracker](https://pub.dev/packages/torrent_tracker) - [DHT](https://pub.dev/packages/dht_dart) -- [Torrent model](https://pub.dev/packages/torrent_model) -- [Common library](https://pub.dev/packages/dartorrent_common) +- [Torrent model](https://pub.dev/packages/dtorrent_parser) +- [Common library](https://pub.dev/packages/dtorrent_common) - [UTP](https://pub.dev/packages/utp) This package implements regular BitTorrent Protocol and manage above packages to work together for downloading. @@ -29,10 +29,10 @@ Other support will come soon. ## How to use -This package need to dependency [`torrent_model`](https://pub.dev/packages/torrent_model): +This package need to dependency [`dtorrent_parser`](https://pub.dev/packages/dtorrent_parser): ``` dependencies: - torrent_model : ^1.0.3 + dtorrent_parser : ^1.0.4 torrent_task : '>= 0.2.1 < 2.0.0' ``` diff --git a/example/lsd_example.dart b/example/lsd_example.dart index 02bcc4f..fc8cf4e 100644 --- a/example/lsd_example.dart +++ b/example/lsd_example.dart @@ -1,8 +1,8 @@ import 'dart:io'; -import 'package:torrent_model/torrent_model.dart'; -import 'package:torrent_task/src/lsd/lsd.dart'; -import 'package:torrent_task/torrent_task.dart'; +import 'package:dtorrent_parser/dtorrent_parser.dart'; +import 'package:dtorrent_task/src/lsd/lsd.dart'; +import 'package:dtorrent_task/dtorrent_task.dart'; void main(List args) async { print(await getTorrenTaskVersion()); diff --git a/example/metadata_example.dart b/example/metadata_example.dart index c502556..9909eed 100644 --- a/example/metadata_example.dart +++ b/example/metadata_example.dart @@ -1,12 +1,12 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:bencode_dart/bencode_dart.dart'; -import 'package:dartorrent_common/dartorrent_common.dart'; -import 'package:torrent_model/torrent_model.dart'; -import 'package:torrent_task/src/metadata/metadata_downloader.dart'; -import 'package:torrent_task/torrent_task.dart'; -import 'package:torrent_tracker/torrent_tracker.dart'; +import 'package:b_encode_decode/b_encode_decode.dart'; +import 'package:dtorrent_common/dtorrent_common.dart'; +import 'package:dtorrent_parser/dtorrent_parser.dart'; +import 'package:dtorrent_task/src/metadata/metadata_downloader.dart'; +import 'package:dtorrent_task/dtorrent_task.dart'; +import 'package:dtorrent_tracker/dtorrent_tracker.dart'; void main(List args) async { var infohashString = '217bddb5816f2abc56ce1d9fe430711542b109cc'; diff --git a/example/torrent_task_comin_example.dart b/example/torrent_task_comin_example.dart index dbffa4b..b399c74 100644 --- a/example/torrent_task_comin_example.dart +++ b/example/torrent_task_comin_example.dart @@ -1,9 +1,9 @@ import 'dart:async'; import 'dart:io'; -import 'package:dartorrent_common/dartorrent_common.dart'; -import 'package:torrent_model/torrent_model.dart'; -import 'package:torrent_task/torrent_task.dart'; +import 'package:dtorrent_common/dtorrent_common.dart'; +import 'package:dtorrent_parser/dtorrent_parser.dart'; +import 'package:dtorrent_task/dtorrent_task.dart'; /// This example is for connect local Future main() async { diff --git a/example/torrent_task_example.dart b/example/torrent_task_example.dart index 919db0d..375f344 100644 --- a/example/torrent_task_example.dart +++ b/example/torrent_task_example.dart @@ -2,9 +2,9 @@ import 'dart:async'; import 'dart:developer'; import 'dart:io'; -import 'package:dartorrent_common/dartorrent_common.dart'; -import 'package:torrent_model/torrent_model.dart'; -import 'package:torrent_task/torrent_task.dart'; +import 'package:dtorrent_common/dtorrent_common.dart'; +import 'package:dtorrent_parser/dtorrent_parser.dart'; +import 'package:dtorrent_task/dtorrent_task.dart'; void main() async { try { diff --git a/lib/torrent_task.dart b/lib/dtorrent_task.dart similarity index 96% rename from lib/torrent_task.dart rename to lib/dtorrent_task.dart index 5595391..6e3c6f7 100644 --- a/lib/torrent_task.dart +++ b/lib/dtorrent_task.dart @@ -1,4 +1,4 @@ -library torrent_task; +library dtorrent_task; import 'dart:io'; diff --git a/lib/src/file/download_file_manager.dart b/lib/src/file/download_file_manager.dart index 92da842..e3ad13b 100644 --- a/lib/src/file/download_file_manager.dart +++ b/lib/src/file/download_file_manager.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'dart:developer'; import 'dart:io'; -import 'package:torrent_model/torrent_model.dart'; +import 'package:dtorrent_parser/dtorrent_parser.dart'; import '../peer/peer_base.dart'; import 'download_file.dart'; diff --git a/lib/src/file/state_file.dart b/lib/src/file/state_file.dart index 3ad3aa3..094ca18 100644 --- a/lib/src/file/state_file.dart +++ b/lib/src/file/state_file.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'dart:developer'; import 'dart:io'; import 'dart:typed_data'; -import 'package:torrent_model/torrent_model.dart'; +import 'package:dtorrent_parser/dtorrent_parser.dart'; import '../peer/bitfield.dart'; const BITFIELD_TYPE = 'bitfield'; diff --git a/lib/src/lsd/lsd.dart b/lib/src/lsd/lsd.dart index cbba8d4..b1254e6 100644 --- a/lib/src/lsd/lsd.dart +++ b/lib/src/lsd/lsd.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'dart:typed_data'; -import 'package:dartorrent_common/dartorrent_common.dart'; +import 'package:dtorrent_common/dtorrent_common.dart'; // const LSD_HOST = '239.192.152.143'; // const LSD_PORT = 6771; diff --git a/lib/src/metadata/metadata_downloader.dart b/lib/src/metadata/metadata_downloader.dart index 63f5683..aa6907e 100644 --- a/lib/src/metadata/metadata_downloader.dart +++ b/lib/src/metadata/metadata_downloader.dart @@ -3,11 +3,11 @@ import 'dart:collection'; import 'dart:io'; import 'dart:typed_data'; -import 'package:bencode_dart/bencode_dart.dart'; +import 'package:b_encode_decode/b_encode_decode.dart'; import 'package:dart_ipify/dart_ipify.dart'; -import 'package:dartorrent_common/dartorrent_common.dart'; -import 'package:dht_dart/dht_dart.dart'; -import 'package:torrent_tracker/torrent_tracker.dart'; +import 'package:dtorrent_common/dtorrent_common.dart'; +import 'package:bittorrent_dht/bittorrent_dht.dart'; +import 'package:dtorrent_tracker/dtorrent_tracker.dart'; import '../peer/peer.dart'; import '../peer/holepunch.dart'; diff --git a/lib/src/metadata/metadata_messager.dart b/lib/src/metadata/metadata_messager.dart index e38e20f..2678d6a 100644 --- a/lib/src/metadata/metadata_messager.dart +++ b/lib/src/metadata/metadata_messager.dart @@ -1,4 +1,4 @@ -import 'package:bencode_dart/bencode_dart.dart'; +import 'package:b_encode_decode/b_encode_decode.dart'; mixin MetaDataMessager { List createRequestMessage(int piece) { diff --git a/lib/src/peer/extended_proccessor.dart b/lib/src/peer/extended_proccessor.dart index 4e20ab7..8b23427 100644 --- a/lib/src/peer/extended_proccessor.dart +++ b/lib/src/peer/extended_proccessor.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:bencode_dart/bencode_dart.dart'; +import 'package:b_encode_decode/b_encode_decode.dart'; mixin ExtendedProcessor { final Map _extendedEventMap = {}; diff --git a/lib/src/peer/holepunch.dart b/lib/src/peer/holepunch.dart index 42500b4..bd7d9c2 100644 --- a/lib/src/peer/holepunch.dart +++ b/lib/src/peer/holepunch.dart @@ -3,7 +3,7 @@ import 'dart:developer'; import 'dart:io'; import 'dart:typed_data'; -import 'package:dartorrent_common/dartorrent_common.dart'; +import 'package:dtorrent_common/dtorrent_common.dart'; enum HolepunchType { rendezvous, connect, error } diff --git a/lib/src/peer/peer.dart b/lib/src/peer/peer.dart index 7f117a4..9ed7b7f 100644 --- a/lib/src/peer/peer.dart +++ b/lib/src/peer/peer.dart @@ -4,10 +4,10 @@ import 'dart:developer' as dev; import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; -import 'package:bencode_dart/bencode_dart.dart'; -import 'package:dartorrent_common/dartorrent_common.dart'; -import 'package:torrent_task/torrent_task.dart'; -import 'package:utp/utp.dart'; +import 'package:b_encode_decode/b_encode_decode.dart'; +import 'package:dtorrent_common/dtorrent_common.dart'; +import 'package:dtorrent_task/dtorrent_task.dart'; +import 'package:utp_protocol/utp_protocol.dart'; import 'peer_event_dispatcher.dart'; import 'congestion_control.dart'; diff --git a/lib/src/peer/peers_manager.dart b/lib/src/peer/peers_manager.dart index fb83653..7ee7adf 100644 --- a/lib/src/peer/peers_manager.dart +++ b/lib/src/peer/peers_manager.dart @@ -3,8 +3,8 @@ import 'dart:developer'; import 'dart:io'; import 'package:dart_ipify/dart_ipify.dart'; -import 'package:torrent_model/torrent_model.dart'; -import 'package:dartorrent_common/dartorrent_common.dart'; +import 'package:dtorrent_parser/dtorrent_parser.dart'; +import 'package:dtorrent_common/dtorrent_common.dart'; import 'bitfield.dart'; import 'peer.dart'; diff --git a/lib/src/peer/pex.dart b/lib/src/peer/pex.dart index 2da0b87..9abba08 100644 --- a/lib/src/peer/pex.dart +++ b/lib/src/peer/pex.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'dart:io'; import 'dart:typed_data'; -import 'package:bencode_dart/bencode_dart.dart'; -import 'package:dartorrent_common/dartorrent_common.dart'; +import 'package:b_encode_decode/b_encode_decode.dart'; +import 'package:dtorrent_common/dtorrent_common.dart'; import '../peer/peer.dart'; diff --git a/lib/src/piece/base_piece_selector.dart b/lib/src/piece/base_piece_selector.dart index 57e74d2..994e7ca 100644 --- a/lib/src/piece/base_piece_selector.dart +++ b/lib/src/piece/base_piece_selector.dart @@ -1,4 +1,4 @@ -import 'package:dartorrent_common/dartorrent_common.dart'; +import 'package:dtorrent_common/dtorrent_common.dart'; import 'piece.dart'; import 'piece_provider.dart'; diff --git a/lib/src/piece/piece_manager.dart b/lib/src/piece/piece_manager.dart index 174369e..80f6490 100644 --- a/lib/src/piece/piece_manager.dart +++ b/lib/src/piece/piece_manager.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:torrent_model/torrent_model.dart'; +import 'package:dtorrent_parser/dtorrent_parser.dart'; import '../peer/bitfield.dart'; import 'piece.dart'; import 'piece_provider.dart'; diff --git a/lib/src/stream/stream.dart b/lib/src/stream/stream.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/src/stream/torrent_stream.dart b/lib/src/stream/torrent_stream.dart new file mode 100644 index 0000000..a5263a0 --- /dev/null +++ b/lib/src/stream/torrent_stream.dart @@ -0,0 +1,190 @@ +import 'dart:async'; +import 'dart:developer'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:dtorrent_common/dtorrent_common.dart'; +import 'package:dtorrent_parser/dtorrent_parser.dart'; +import 'package:dtorrent_task/src/piece/base_piece_selector.dart'; +import 'package:dtorrent_task/dtorrent_task.dart'; +import 'package:dtorrent_tracker/dtorrent_tracker.dart'; + +class TorrentStream implements AnnounceOptionsProvider { + static InternetAddress LOCAL_ADDRESS = + InternetAddress.fromRawAddress(Uint8List.fromList([127, 0, 0, 1])); + + TorrentAnnounceTracker? _tracker; + StateFile? _stateFile; + + PieceManager? _pieceManager; + + DownloadFileManager? _fileManager; + + PeersManager? _peersManager; + + final Torrent _metaInfo; + + final String _savePath; + final Set _peerIds = {}; + + late String + _peerId; // This is the generated local peer ID, which is different from the ID used in the Peer class. + + ServerSocket? _serverSocket; + + final Set _cominIp = {}; + + bool _paused = false; + late String _infoHashString; + StreamController _fileStream = StreamController(); + Stream get fileStream => _fileStream.stream; + + void seek(int position) { + if (position < 1 || position > _metaInfo.length) return; + var pieceIndex = position ~/ _metaInfo.pieceLength; + // _pieceManager + } + + TorrentStream(this._metaInfo, this._savePath) { + _peerId = generatePeerId(); + } + final Set _taskCompleteHandlers = {}; + + void _fireTaskComplete() { + for (var element in _taskCompleteHandlers) { + Timer.run(() => element()); + } + } + + void addPeer(CompactAddress address, PeerSource source, + {PeerType? type, Socket? socket}) { + _peersManager?.addNewPeerAddress(address, source, + type: type, socket: socket); + } + + void _whenTaskDownloadComplete() async { + await _peersManager + ?.disposeAllSeeder('Download complete,disconnect seeder'); + await _tracker?.complete(); + _fireTaskComplete(); + } + + final Set _fileCompleteHandlers = {}; + + void _fireFileComplete(String filepath) { + for (var handler in _fileCompleteHandlers) { + Timer.run(() => handler(filepath)); + } + } + + void _whenFileDownloadComplete(String filePath) { + _fireFileComplete(filePath); + } + + void _processTrackerPeerEvent(Tracker source, PeerEvent? event) { + if (event == null) return; + var ps = event.peers; + if (ps.isNotEmpty) { + for (var url in ps) { + _processNewPeerFound(url, PeerSource.tracker); + } + } + } + + void _processLSDPeerEvent(CompactAddress address, String infoHash) { + print('There is LSD! !'); + } + + void _processNewPeerFound(CompactAddress url, PeerSource source) { + log("Add new peer ${url.toString()} from ${source.name} to peersManager", + name: runtimeType.toString()); + _peersManager?.addNewPeerAddress(url, source); + } + + void _processDHTPeer(CompactAddress peer, String infoHash) { + log("Got new peer from $peer DHT for infohash: ${Uint8List.fromList(infoHash.codeUnits).toHexString()}", + name: runtimeType.toString()); + if (infoHash == _infoHashString) { + _processNewPeerFound(peer, PeerSource.dht); + } + } + + void _hookInPeer(Socket socket) { + if (socket.remoteAddress == LOCAL_ADDRESS) { + socket.close(); + return; + } + if (_cominIp.length >= MAX_IN_PEERS || !_cominIp.add(socket.address)) { + socket.close(); + return; + } + log('incoming connect: ${socket.remoteAddress.address}:${socket.remotePort}', + name: runtimeType.toString()); + _peersManager?.addNewPeerAddress( + CompactAddress(socket.remoteAddress, socket.remotePort), + PeerSource.incoming, + type: PeerType.TCP, + socket: socket); + } + + Future _init(Torrent model, String savePath) async { + _infoHashString = String.fromCharCodes(model.infoHashBuffer); + _tracker ??= TorrentAnnounceTracker(this); + _stateFile ??= await StateFile.getStateFile(savePath, model); + _pieceManager ??= PieceManager.createPieceManager( + BasePieceSelector(), model, _stateFile!.bitfield); + _fileManager ??= await DownloadFileManager.createFileManager( + model, savePath, _stateFile!); + _peersManager ??= PeersManager( + _peerId, _pieceManager!, _pieceManager!, _fileManager!, model); + return _peersManager!; + } + + Future start() async { + // Incoming peer: + _serverSocket ??= await ServerSocket.bind(InternetAddress.anyIPv4, 0); + await _init(_metaInfo, _savePath); + _serverSocket?.listen(_hookInPeer); + + var map = {}; + map['name'] = _metaInfo.name; + map['tcp_socket'] = _serverSocket?.port; + map['comoplete_pieces'] = List.from(_stateFile!.bitfield.completedPieces); + map['total_pieces_num'] = _stateFile!.bitfield.piecesNum; + map['downloaded'] = _stateFile!.downloaded; + map['uploaded'] = _stateFile!.uploaded; + map['total_length'] = _metaInfo.length; + // Outgoing peer: + _tracker?.onPeerEvent(_processTrackerPeerEvent); + _peersManager?.onAllComplete(_whenTaskDownloadComplete); + _fileManager?.onFileComplete(_whenFileDownloadComplete); + + // _dht?.announce( + // String.fromCharCodes(_metaInfo.infoHashBuffer), _serverSocket!.port); + // _dht?.onNewPeer(_processDHTPeer); + // ignore: unawaited_futures + // _dht?.bootstrap(); + if (_fileManager != null && _fileManager!.isAllComplete) { + // ignore: unawaited_futures + _tracker?.complete(); + } else { + _tracker?.runTrackers(_metaInfo.announces, _metaInfo.infoHashBuffer, + event: EVENT_STARTED); + } + return map; + } + + @override + Future> getOptions(Uri uri, String infoHash) { + var map = { + 'downloaded': _stateFile?.downloaded, + 'uploaded': _stateFile?.uploaded, + 'left': _metaInfo.length - _stateFile!.downloaded, + 'numwant': 50, + 'compact': 1, + 'peerId': _peerId, + 'port': _serverSocket?.port + }; + return Future.value(map); + } +} diff --git a/lib/src/task.dart b/lib/src/task.dart index 474c020..8c80c27 100644 --- a/lib/src/task.dart +++ b/lib/src/task.dart @@ -3,11 +3,11 @@ import 'dart:developer'; import 'dart:io'; import 'dart:typed_data'; -import 'package:torrent_model/torrent_model.dart'; -import 'package:torrent_tracker/torrent_tracker.dart'; -import 'package:dartorrent_common/dartorrent_common.dart'; -import 'package:dht_dart/dht_dart.dart'; -import 'package:utp/utp.dart'; +import 'package:dtorrent_parser/dtorrent_parser.dart'; +import 'package:dtorrent_tracker/dtorrent_tracker.dart'; +import 'package:dtorrent_common/dtorrent_common.dart'; +import 'package:bittorrent_dht/bittorrent_dht.dart'; +import 'package:utp_protocol/utp_protocol.dart'; import 'file/download_file_manager.dart'; import 'file/state_file.dart'; diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 41e5f25..2fbacba 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -1,7 +1,7 @@ import 'dart:convert'; -import 'package:dartorrent_common/dartorrent_common.dart'; -import 'package:torrent_task/torrent_task.dart'; +import 'package:dtorrent_common/dtorrent_common.dart'; +import 'package:dtorrent_task/dtorrent_task.dart'; String generatePeerId([String prefix = ID_PREFIX]) { var r = randomBytes(9); diff --git a/pubspec.yaml b/pubspec.yaml index 5b7c5fd..b4715cd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,20 +1,19 @@ -name: torrent_task +name: dtorrent_task description: BitTorrent download client package written by pure Dart language. -version: 0.3.0 -repository: https://github.com/eclipseglory/torrent_task -issue_tracker: https://github.com/eclipseglory/torrent_task/issues -publish_to: https://pub.dev/ +version: 0.3.1 +repository: https://github.com/moham96/dtorrent_task +issue_tracker: https://github.com/moham96/dtorrent_task/issues environment: sdk: ">=2.12.0 <3.0.0" dependencies: - torrent_model: ^1.0.4 - torrent_tracker: ^1.3.13 - dht_dart: ^0.0.8 - dartorrent_common: ^1.0.4 - bencode_dart: ^1.0.3 - utp: ^1.0.1 + dtorrent_parser: ^1.0.4 + dtorrent_tracker: ^1.3.14 + bittorrent_dht: ^0.0.8 + dtorrent_common: ^1.0.4 + b_encode_decode: ^1.0.3 + utp_protocol: ^1.0.2 dart_ipify: ^1.1.1 dev_dependencies: diff --git a/test/peer_communicate.dart b/test/peer_communicate.dart index a3a0d59..3823a9a 100644 --- a/test/peer_communicate.dart +++ b/test/peer_communicate.dart @@ -3,8 +3,8 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:dartorrent_common/dartorrent_common.dart'; -import 'package:torrent_task/torrent_task.dart'; +import 'package:dtorrent_common/dtorrent_common.dart'; +import 'package:dtorrent_task/dtorrent_task.dart'; void main() async { ServerSocket? serverSocket; diff --git a/test/torrent_client_test.dart b/test/torrent_client_test.dart index 8930df4..0171a1c 100644 --- a/test/torrent_client_test.dart +++ b/test/torrent_client_test.dart @@ -4,10 +4,10 @@ import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; -import 'package:dartorrent_common/dartorrent_common.dart'; +import 'package:dtorrent_common/dtorrent_common.dart'; import 'package:test/test.dart'; -import 'package:torrent_model/torrent_model.dart'; -import 'package:torrent_task/torrent_task.dart'; +import 'package:dtorrent_parser/dtorrent_parser.dart'; +import 'package:dtorrent_task/dtorrent_task.dart'; void main() { group('Bitfield test - ', () {