Flutter torrent追踪插件dtorrent_tracker的使用

发布于 1周前 作者 itying888 来自 Flutter

Flutter torrent追踪插件dtorrent_tracker的使用

关于

Dart实现的BitTorrent Http/Https和UDP追踪器/刮擦客户端。

支持

如何使用

追踪器

要创建一个TorrentAnnounceTracker实例,需要提供AnnounceOptionProvider参数。但是目前还没有实现,用户需要手动实现:

class SimpleProvider implements AnnounceOptionsProvider {
  SimpleProvider(this.torrent, this.peerId, this.port, this.infoHash);
  String peerId;
  int port;
  String infoHash;
  Torrent torrent;
  int compact = 1;
  int numwant = 50;

  [@override](/user/override)
  Future<Map<String, dynamic>> getOptions(Uri uri, String infoHash) {
    return Future.value({
      'downloaded': 0,
      'uploaded': 0,
      'left': torrent.length,
      'compact': compact, // 应该为1
      'numwant': numwant, // 最大为50
      'peerId': peerId,
      'port': port
    });
  }
}

当我们有了AnnounceOptionsProvider实例后,可以这样创建一个TorrentAnnounceTracker

var torrentTracker = TorrentAnnounceTracker(SimpleProvider(...));

TorrentAnnounceTracker有一些方法来运行startedstoppedcompleted等公告事件:

torrentTracker.runTracker(url, infohash, event: 'started');

我们可以在torrentTracker上添加一些监听器以获取公告结果:

torrentTracker.onAnnounceError((source, error) {
  log('announce error:', error: error);
});
torrentTracker.onPeerEvent((source, event) {
  print('${source.announceUrl} peer event: $event');
});

torrentTracker.onAnnounceOver((source, time) {
  print('${source.announceUrl} announce over!: $time');
  source.dispose();
});

刮擦

创建一个TorrentScrapeTracker实例:

var scrapeTracker = TorrentScrapeTracker();

然后添加刮擦URL(与公告跟踪器URL相同,TorrentScrapeTracker会进行转换)和infohash缓冲区以创建一个Scrape

scrapeTracker.addScrapes(torrent.announces, torrent.infoHashBuffer);

注意Scrape可以添加多个infoHash缓冲区,因为它可以在一次操作中“刮擦”多个种子信息,因此如果用户调用addScrapesaddScrape时提供了相同的URL但不同的infohash缓冲区,它将返回相同的Scrape实例。

要获取刮擦结果:

scrapeTracker.scrape(torrent.infoHashBuffer).listen((event) {
  print(event);
});

scrape方法需要一个infoHash缓冲区作为参数,并返回一个Stream,用户可以通过监听Stream事件来获取结果。

示例代码

以下是一个完整的示例代码,展示了如何使用dtorrent_tracker插件进行追踪和刮擦操作。

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:dtorrent_common/dtorrent_common.dart';
import 'package:dtorrent_parser/dtorrent_parser.dart';
import 'package:dtorrent_tracker/dtorrent_tracker.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;

var scriptDir = path.dirname(Platform.script.path);
var torrentsPath = path.canonicalize(path.join(scriptDir, '..', '..', '..', 'torrents'));

void main() async {
  var _log = Logger.root;
  Logger.root.level = Level.ALL;
  Logger.root.onRecord.listen((record) {
    print('[${record.loggerName}] ${record.level.name}: ${record.time}: ${record.message}');
  });

  var torrent = await Torrent.parse(path.join(torrentsPath, 'big-buck-bunny.torrent'));

  var id = generatePeerId();
  var port = 55551;
  var provider = SimpleProvider(torrent, id, port, torrent.infoHash);
  var peerAddress = <CompactAddress>{};
  print(provider.torrent);

  /// 追踪器
  try {
    var torrentTracker = TorrentAnnounceTracker(provider, maxRetryTime: 0);
    var trackerListener = torrentTracker.createListener();
    trackerListener
      ..on<AnnounceTrackerDisposedEvent>((event) {
        _log.info('Tracker disposed, remain ${torrentTracker.trackersNum}:', event.reason);
      })
      ..on<AnnounceErrorEvent>(
        (event) {
          // log('announce error:', error: error);
          // source.dispose(error);
        },
      )
      ..on<AnnouncePeerEventEvent>(
        (event) {
          if (event.event == null) return;
          peerAddress.addAll(event.event!.peers);
          event.source.dispose('Complete Announce');
          print('got ${peerAddress.length} peers');
        },
      )
      ..on<AnnounceOverEvent>(
        (event) {
          print('${event.source.announceUrl} announce over: ${event.time}');
          // source.dispose();
        },
      );

    findPublicTrackers().listen((urls) {
      torrentTracker.runTrackers(urls, torrent.infoHashBuffer);
    });

    // Timer(Duration(seconds: 120), () async {
    //   await torrentTracker.stop(true);
    //   print('completed $peerAddress');
    // });
  } catch (e) {
    print(e);
  }

  /// 刮擦
  // var scrapeTracker = TorrentScrapeTracker();
  // scrapeTracker.addScrapes(torrent.announces, torrent.infoHashBuffer);
  // scrapeTracker.scrape(torrent.infoHashBuffer).listen((event) {
  //   print(event);
  // }, onError: (e) => log('error:', error: e, name: 'MAIN'));
}

class SimpleProvider implements AnnounceOptionsProvider {
  SimpleProvider(this.torrent, this.peerId, this.port, this.infoHash);
  String peerId;
  int port;
  String infoHash;
  Torrent torrent;
  int compact = 1;
  int numwant = 50;

  [@override](/user/override)
  Future<Map<String, dynamic>> getOptions(Uri uri, String infoHash) {
    return Future.value({
      'downloaded': 0,
      'uploaded': 0,
      'left': torrent.length,
      'compact': compact,
      'numwant': numwant,
      'peerId': peerId,
      'port': port
    });
  }
}

String generatePeerId() {
  var r = randomBytes(9);
  var base64Str = base64Encode(r);
  var id = '-MURLIN-$base64Str';
  return id;
}

更多关于Flutter torrent追踪插件dtorrent_tracker的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter torrent追踪插件dtorrent_tracker的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用dtorrent_tracker插件的一个简单示例。dtorrent_tracker是一个Flutter插件,用于实现BitTorrent tracker客户端的功能,可以用来与BitTorrent tracker服务器进行通信,以宣告peer信息并获取其他peer的信息。

首先,确保你的Flutter项目已经设置完毕,并且在pubspec.yaml文件中添加了dtorrent_tracker依赖:

dependencies:
  flutter:
    sdk: flutter
  dtorrent_tracker: ^latest_version  # 请替换为实际的最新版本号

然后运行flutter pub get来安装依赖。

接下来,是一个简单的示例代码,展示了如何使用dtorrent_tracker插件来宣告一个torrent并获取peer列表。

import 'package:flutter/material.dart';
import 'package:dtorrent_tracker/dtorrent_tracker.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<Peer> peers = [];

  @override
  void initState() {
    super.initState();
    _announceTorrent();
  }

  Future<void> _announceTorrent() async {
    try {
      // 创建TorrentAnnounce对象
      TorrentAnnounce announce = TorrentAnnounce(
        infoHash: Uint8List.fromList(List.generate(20, (index) => index % 256)), // 示例infoHash,请使用实际的infoHash
        peerId: 'fl-dtorrent1234',
        port: 6881,
        trackerUrl: 'udp://tracker.openbittorrent.com:80/announce', // 示例tracker URL,请使用实际的tracker URL
      );

      // 宣告torrent
      TorrentResponse response = await announce.announce(
        event: AnnounceEvent.started,
        uploaded: 0,
        downloaded: 0,
        left: 1000000, // 示例left值,表示未下载的数据量
      );

      // 更新peer列表
      setState(() {
        peers = response.peers;
      });

      print('宣布成功,获得的peer数量: ${peers.length}');
      peers.forEach((peer) {
        print('Peer IP: ${peer.ip}, Port: ${peer.port}');
      });
    } catch (e) {
      print('宣布失败: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('dtorrent_tracker 示例'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text('Peer 数量: ${peers.length}'),
              SizedBox(height: 20),
              Expanded(
                child: ListView.builder(
                  itemCount: peers.length,
                  itemBuilder: (context, index) {
                    Peer peer = peers[index];
                    return ListTile(
                      title: Text('IP: ${peer.ip}, Port: ${peer.port}'),
                    );
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

注意

  1. infoHash:需要替换为实际的torrent文件的infoHash。
  2. trackerUrl:需要替换为实际的tracker服务器URL。
  3. peerId:通常是客户端的唯一标识符,这里只是示例。
  4. left:表示torrent文件中尚未下载的数据量,这里只是示例值。

这个示例展示了如何使用dtorrent_tracker插件来宣告一个torrent并从tracker服务器获取peer列表。你可以根据实际需求调整代码,例如定期宣告以更新peer列表,处理不同的announce事件(如stopped, completed等)。

回到顶部