private static async Task StartEngine() { Logger.Factory = (string className) => new TextLogger(Console.Out, className); int port; Torrent torrent = null; // Ask the user what port they want to use for incoming connections Console.Write($"{Environment.NewLine}Choose a listen port: "); while (!Int32.TryParse(Console.ReadLine(), out port)) { } // Create the settings which the engine will use // downloadsPath - this is the path where we will save all the files to // port - this is the port we listen for connections on EngineSettings engineSettings = new EngineSettings { SavePath = downloadsPath, ListenPort = port }; //engineSettings.GlobalMaxUploadSpeed = 30 * 1024; //engineSettings.GlobalMaxDownloadSpeed = 100 * 1024; //engineSettings.MaxReadRate = 1 * 1024 * 1024; // Create the default settings which a torrent will have. TorrentSettings torrentDefaults = new TorrentSettings(); // Create an instance of the engine. engine = new ClientEngine(engineSettings); byte[] nodes = Array.Empty <byte> (); try { if (File.Exists(dhtNodeFile)) { nodes = File.ReadAllBytes(dhtNodeFile); } } catch { Console.WriteLine("No existing dht nodes could be loaded"); } DhtEngine dht = new DhtEngine(new IPEndPoint(IPAddress.Any, port)); await engine.RegisterDhtAsync(dht); // This starts the Dht engine but does not wait for the full initialization to // complete. This is because it can take up to 2 minutes to bootstrap, depending // on how many nodes time out when they are contacted. await engine.DhtEngine.StartAsync(nodes); // If the SavePath does not exist, we want to create it. if (!Directory.Exists(engine.Settings.SavePath)) { Directory.CreateDirectory(engine.Settings.SavePath); } // If the torrentsPath does not exist, we want to create it if (!Directory.Exists(torrentsPath)) { Directory.CreateDirectory(torrentsPath); } BEncodedDictionary fastResume = new BEncodedDictionary(); try { if (File.Exists(fastResumeFile)) { fastResume = BEncodedValue.Decode <BEncodedDictionary> (File.ReadAllBytes(fastResumeFile)); } } catch { } // For each file in the torrents path that is a .torrent file, load it into the engine. foreach (string file in Directory.GetFiles(torrentsPath)) { if (file.EndsWith(".torrent", StringComparison.OrdinalIgnoreCase)) { try { // Load the .torrent from the file into a Torrent instance // You can use this to do preprocessing should you need to torrent = await Torrent.LoadAsync(file); Console.WriteLine(torrent.InfoHash.ToString()); } catch (Exception e) { Console.Write("Couldn't decode {0}: ", file); Console.WriteLine(e.Message); continue; } // When any preprocessing has been completed, you create a TorrentManager // which you then register with the engine. TorrentManager manager = new TorrentManager(torrent, downloadsPath, torrentDefaults); if (fastResume.ContainsKey(torrent.InfoHash.ToHex())) { manager.LoadFastResume(new FastResume((BEncodedDictionary)fastResume[torrent.InfoHash.ToHex()])); } await engine.Register(manager); // Store the torrent manager in our list so we can access it later torrents.Add(manager); manager.PeersFound += Manager_PeersFound; } } // If we loaded no torrents, just exist. The user can put files in the torrents directory and start // the client again if (torrents.Count == 0) { Console.WriteLine("No torrents found in the Torrents directory"); Console.WriteLine("Exiting..."); engine.Dispose(); return; } // For each torrent manager we loaded and stored in our list, hook into the events // in the torrent manager and start the engine. foreach (TorrentManager manager in torrents) { manager.PeerConnected += (o, e) => { lock (listener) listener.WriteLine($"Connection succeeded: {e.Peer.Uri}"); }; manager.ConnectionAttemptFailed += (o, e) => { lock (listener) listener.WriteLine( $"Connection failed: {e.Peer.ConnectionUri} - {e.Reason} - {e.Peer.AllowedEncryption}"); }; // Every time a piece is hashed, this is fired. manager.PieceHashed += delegate(object o, PieceHashedEventArgs e) { lock (listener) listener.WriteLine($"Piece Hashed: {e.PieceIndex} - {(e.HashPassed ? "Pass" : "Fail")}"); }; // Every time the state changes (Stopped -> Seeding -> Downloading -> Hashing) this is fired manager.TorrentStateChanged += delegate(object o, TorrentStateChangedEventArgs e) { lock (listener) listener.WriteLine($"OldState: {e.OldState} NewState: {e.NewState}"); }; // Every time the tracker's state changes, this is fired manager.TrackerManager.AnnounceComplete += (sender, e) => { listener.WriteLine($"{e.Successful}: {e.Tracker}"); }; // Start the torrentmanager. The file will then hash (if required) and begin downloading/seeding await manager.StartAsync(); } // Enable automatic port forwarding. The engine will use Mono.Nat to search for // uPnP or NAT-PMP compatible devices and then issue port forwarding requests to it. await engine.EnablePortForwardingAsync(CancellationToken.None); // This is how to access the list of port mappings, and to see if they were // successful, pending or failed. If they failed it could be because the public port // is already in use by another computer on your network. foreach (var successfulMapping in engine.PortMappings.Created) { } foreach (var failedMapping in engine.PortMappings.Failed) { } foreach (var failedMapping in engine.PortMappings.Pending) { } // While the torrents are still running, print out some stats to the screen. // Details for all the loaded torrent managers are shown. int i = 0; bool running = true; StringBuilder sb = new StringBuilder(1024); while (running) { if ((i++) % 10 == 0) { sb.Remove(0, sb.Length); running = torrents.Exists(m => m.State != TorrentState.Stopped); AppendFormat(sb, "Total Download Rate: {0:0.00}kB/sec", engine.TotalDownloadSpeed / 1024.0); AppendFormat(sb, "Total Upload Rate: {0:0.00}kB/sec", engine.TotalUploadSpeed / 1024.0); AppendFormat(sb, "Disk Read Rate: {0:0.00} kB/s", engine.DiskManager.ReadRate / 1024.0); AppendFormat(sb, "Disk Write Rate: {0:0.00} kB/s", engine.DiskManager.WriteRate / 1024.0); AppendFormat(sb, "Total Read: {0:0.00} kB", engine.DiskManager.TotalRead / 1024.0); AppendFormat(sb, "Total Written: {0:0.00} kB", engine.DiskManager.TotalWritten / 1024.0); AppendFormat(sb, "Open Connections: {0}", engine.ConnectionManager.OpenConnections); foreach (TorrentManager manager in torrents) { AppendSeparator(sb); AppendFormat(sb, "State: {0}", manager.State); AppendFormat(sb, "Name: {0}", manager.Torrent == null ? "MetaDataMode" : manager.Torrent.Name); AppendFormat(sb, "Progress: {0:0.00}", manager.Progress); AppendFormat(sb, "Download Speed: {0:0.00} kB/s", manager.Monitor.DownloadSpeed / 1024.0); AppendFormat(sb, "Upload Speed: {0:0.00} kB/s", manager.Monitor.UploadSpeed / 1024.0); AppendFormat(sb, "Total Downloaded: {0:0.00} MB", manager.Monitor.DataBytesDownloaded / (1024.0 * 1024.0)); AppendFormat(sb, "Total Uploaded: {0:0.00} MB", manager.Monitor.DataBytesUploaded / (1024.0 * 1024.0)); AppendFormat(sb, "Tracker Status"); foreach (var tier in manager.TrackerManager.Tiers) { AppendFormat(sb, $"\t{tier.ActiveTracker} : Announce Succeeded: {tier.LastAnnounceSucceeded}. Scrape Succeeded: {tier.LastScrapeSucceeded}."); } if (manager.PieceManager != null) { AppendFormat(sb, "Current Requests: {0}", await manager.PieceManager.CurrentRequestCountAsync()); } foreach (PeerId p in await manager.GetPeersAsync()) { AppendFormat(sb, "\t{2} - {1:0.00}/{3:0.00}kB/sec - {0}", p.Uri, p.Monitor.DownloadSpeed / 1024.0, p.AmRequestingPiecesCount, p.Monitor.UploadSpeed / 1024.0); } AppendFormat(sb, "", null); if (manager.Torrent != null) { foreach (var file in manager.Files) { AppendFormat(sb, "{1:0.00}% - {0}", file.Path, file.BitField.PercentComplete); } } } Console.Clear(); Console.WriteLine(sb.ToString()); listener.ExportTo(Console.Out); } Thread.Sleep(500); } // Stop searching for uPnP or NAT-PMP compatible devices and delete // all mapppings which had been created. await engine.DisablePortForwardingAsync(CancellationToken.None); }
private static async Task StartEngine() { int port; Torrent torrent = null; // Ask the user what port they want to use for incoming connections Console.Write(Environment.NewLine + "Choose a listen port: "); while (!Int32.TryParse(Console.ReadLine(), out port)) { } // Create the settings which the engine will use // downloadsPath - this is the path where we will save all the files to // port - this is the port we listen for connections on EngineSettings engineSettings = new EngineSettings(downloadsPath, port); engineSettings.PreferEncryption = false; engineSettings.AllowedEncryption = EncryptionTypes.All; //engineSettings.GlobalMaxUploadSpeed = 30 * 1024; //engineSettings.GlobalMaxDownloadSpeed = 100 * 1024; //engineSettings.MaxReadRate = 1 * 1024 * 1024; // Create the default settings which a torrent will have. // 4 Upload slots - a good ratio is one slot per 5kB of upload speed // 50 open connections - should never really need to be changed // Unlimited download speed - valid range from 0 -> int.Max // Unlimited upload speed - valid range from 0 -> int.Max TorrentSettings torrentDefaults = new TorrentSettings(4, 150, 0, 0); // Create an instance of the engine. engine = new ClientEngine(engineSettings); byte[] nodes = Array.Empty <byte> (); try { nodes = File.ReadAllBytes(dhtNodeFile); } catch { Console.WriteLine("No existing dht nodes could be loaded"); } DhtEngine dht = new DhtEngine(new IPEndPoint(IPAddress.Any, port)); await engine.RegisterDhtAsync(dht); // This starts the Dht engine but does not wait for the full initialization to // complete. This is because it can take up to 2 minutes to bootstrap, depending // on how many nodes time out when they are contacted. await engine.DhtEngine.StartAsync(nodes); // If the SavePath does not exist, we want to create it. if (!Directory.Exists(engine.Settings.SavePath)) { Directory.CreateDirectory(engine.Settings.SavePath); } // If the torrentsPath does not exist, we want to create it if (!Directory.Exists(torrentsPath)) { Directory.CreateDirectory(torrentsPath); } BEncodedDictionary fastResume; try { fastResume = BEncodedValue.Decode <BEncodedDictionary>(File.ReadAllBytes(fastResumeFile)); } catch { fastResume = new BEncodedDictionary(); } // For each file in the torrents path that is a .torrent file, load it into the engine. foreach (string file in Directory.GetFiles(torrentsPath)) { if (file.EndsWith(".torrent", StringComparison.OrdinalIgnoreCase)) { try { // Load the .torrent from the file into a Torrent instance // You can use this to do preprocessing should you need to torrent = Torrent.Load(file); Console.WriteLine(torrent.InfoHash.ToString()); } catch (Exception e) { Console.Write("Couldn't decode {0}: ", file); Console.WriteLine(e.Message); continue; } // When any preprocessing has been completed, you create a TorrentManager // which you then register with the engine. TorrentManager manager = new TorrentManager(torrent, downloadsPath, torrentDefaults); if (fastResume.ContainsKey(torrent.InfoHash.ToHex())) { manager.LoadFastResume(new FastResume((BEncodedDictionary)fastResume[torrent.InfoHash.ToHex()])); } await engine.Register(manager); // Store the torrent manager in our list so we can access it later torrents.Add(manager); manager.PeersFound += new EventHandler <PeersAddedEventArgs>(manager_PeersFound); } } // If we loaded no torrents, just exist. The user can put files in the torrents directory and start // the client again if (torrents.Count == 0) { Console.WriteLine("No torrents found in the Torrents directory"); Console.WriteLine("Exiting..."); engine.Dispose(); return; } // For each torrent manager we loaded and stored in our list, hook into the events // in the torrent manager and start the engine. foreach (TorrentManager manager in torrents) { // Every time a piece is hashed, this is fired. manager.PieceHashed += delegate(object o, PieceHashedEventArgs e) { lock (listener) listener.WriteLine(string.Format("Piece Hashed: {0} - {1}", e.PieceIndex, e.HashPassed ? "Pass" : "Fail")); }; // Every time the state changes (Stopped -> Seeding -> Downloading -> Hashing) this is fired manager.TorrentStateChanged += delegate(object o, TorrentStateChangedEventArgs e) { lock (listener) listener.WriteLine("OldState: " + e.OldState.ToString() + " NewState: " + e.NewState.ToString()); }; // Every time the tracker's state changes, this is fired manager.TrackerManager.AnnounceComplete += (sender, e) => { listener.WriteLine(string.Format("{0}: {1}", e.Successful, e.Tracker)); }; // Start the torrentmanager. The file will then hash (if required) and begin downloading/seeding await manager.StartAsync(); } // While the torrents are still running, print out some stats to the screen. // Details for all the loaded torrent managers are shown. int i = 0; bool running = true; StringBuilder sb = new StringBuilder(1024); while (running) { if ((i++) % 10 == 0) { sb.Remove(0, sb.Length); running = torrents.Exists(delegate(TorrentManager m) { return(m.State != TorrentState.Stopped); }); AppendFormat(sb, "Total Download Rate: {0:0.00}kB/sec", engine.TotalDownloadSpeed / 1024.0); AppendFormat(sb, "Total Upload Rate: {0:0.00}kB/sec", engine.TotalUploadSpeed / 1024.0); AppendFormat(sb, "Disk Read Rate: {0:0.00} kB/s", engine.DiskManager.ReadRate / 1024.0); AppendFormat(sb, "Disk Write Rate: {0:0.00} kB/s", engine.DiskManager.WriteRate / 1024.0); AppendFormat(sb, "Total Read: {0:0.00} kB", engine.DiskManager.TotalRead / 1024.0); AppendFormat(sb, "Total Written: {0:0.00} kB", engine.DiskManager.TotalWritten / 1024.0); AppendFormat(sb, "Open Connections: {0}", engine.ConnectionManager.OpenConnections); foreach (TorrentManager manager in torrents) { AppendSeperator(sb); AppendFormat(sb, "State: {0}", manager.State); AppendFormat(sb, "Name: {0}", manager.Torrent == null ? "MetaDataMode" : manager.Torrent.Name); AppendFormat(sb, "Progress: {0:0.00}", manager.Progress); AppendFormat(sb, "Download Speed: {0:0.00} kB/s", manager.Monitor.DownloadSpeed / 1024.0); AppendFormat(sb, "Upload Speed: {0:0.00} kB/s", manager.Monitor.UploadSpeed / 1024.0); AppendFormat(sb, "Total Downloaded: {0:0.00} MB", manager.Monitor.DataBytesDownloaded / (1024.0 * 1024.0)); AppendFormat(sb, "Total Uploaded: {0:0.00} MB", manager.Monitor.DataBytesUploaded / (1024.0 * 1024.0)); MonoTorrent.Client.Tracker.ITracker tracker = manager.TrackerManager.CurrentTracker; //AppendFormat(sb, "Tracker Status: {0}", tracker == null ? "<no tracker>" : tracker.State.ToString()); AppendFormat(sb, "Warning Message: {0}", tracker == null ? "<no tracker>" : tracker.WarningMessage); AppendFormat(sb, "Failure Message: {0}", tracker == null ? "<no tracker>" : tracker.FailureMessage); if (manager.PieceManager != null) { AppendFormat(sb, "Current Requests: {0}", await manager.PieceManager.CurrentRequestCountAsync()); } foreach (PeerId p in await manager.GetPeersAsync()) { AppendFormat(sb, "\t{2} - {1:0.00}/{3:0.00}kB/sec - {0}", p.Uri, p.Monitor.DownloadSpeed / 1024.0, p.AmRequestingPiecesCount, p.Monitor.UploadSpeed / 1024.0); } AppendFormat(sb, "", null); if (manager.Torrent != null) { foreach (TorrentFile file in manager.Torrent.Files) { AppendFormat(sb, "{1:0.00}% - {0}", file.Path, file.BitField.PercentComplete); } } } Console.Clear(); Console.WriteLine(sb.ToString()); listener.ExportTo(Console.Out); } System.Threading.Thread.Sleep(500); } }
private static async Task StartEngine() { #if DEBUG Logger.Factory = (string className) => new TextLogger(Console.Out, className); #endif Torrent torrent = null; // Create an instance of the engine. engine = new ClientEngine(); // If the torrentsPath does not exist, we want to create it if (!Directory.Exists(torrentsPath)) { Directory.CreateDirectory(torrentsPath); } // For each file in the torrents path that is a .torrent file, load it into the engine. foreach (string file in Directory.GetFiles(torrentsPath)) { if (file.EndsWith(".torrent", StringComparison.OrdinalIgnoreCase)) { try { // Load the .torrent from the file into a Torrent instance // You can use this to do preprocessing should you need to torrent = await Torrent.LoadAsync(file); Console.WriteLine(torrent.InfoHash.ToString()); } catch (Exception e) { Console.Write("Couldn't decode {0}: ", file); Console.WriteLine(e.Message); continue; } // EngineSettings.AutoSaveLoadFastResume is enabled, so any cached fast resume // data will be implicitly loaded. If fast resume data is found, the 'hash check' // phase of starting a torrent can be skipped. // // TorrentSettingsBuilder can be used to modify the settings for this // torrent. var manager = await engine.AddAsync(torrent, downloadsPath); // Store the torrent manager in our list so we can access it later torrents.Add(manager); manager.PeersFound += Manager_PeersFound; } } // If we loaded no torrents, just exist. The user can put files in the torrents directory and start // the client again if (torrents.Count == 0) { Console.WriteLine("No torrents found in the Torrents directory"); Console.WriteLine("Exiting..."); engine.Dispose(); return; } // For each torrent manager we loaded and stored in our list, hook into the events // in the torrent manager and start the engine. foreach (TorrentManager manager in torrents) { manager.PeerConnected += (o, e) => { lock (listener) listener.WriteLine($"Connection succeeded: {e.Peer.Uri}"); }; manager.ConnectionAttemptFailed += (o, e) => { lock (listener) listener.WriteLine( $"Connection failed: {e.Peer.ConnectionUri} - {e.Reason}"); }; // Every time a piece is hashed, this is fired. manager.PieceHashed += delegate(object o, PieceHashedEventArgs e) { lock (listener) listener.WriteLine($"Piece Hashed: {e.PieceIndex} - {(e.HashPassed ? "Pass" : "Fail")}"); }; // Every time the state changes (Stopped -> Seeding -> Downloading -> Hashing) this is fired manager.TorrentStateChanged += delegate(object o, TorrentStateChangedEventArgs e) { lock (listener) listener.WriteLine($"OldState: {e.OldState} NewState: {e.NewState}"); }; // Every time the tracker's state changes, this is fired manager.TrackerManager.AnnounceComplete += (sender, e) => { listener.WriteLine($"{e.Successful}: {e.Tracker}"); }; // Start the torrentmanager. The file will then hash (if required) and begin downloading/seeding. // As EngineSettings.AutoSaveLoadDhtCache is enabled, any cached data will be loaded into the // Dht engine when the first torrent is started, enabling it to bootstrap more rapidly. await manager.StartAsync(); } // While the torrents are still running, print out some stats to the screen. // Details for all the loaded torrent managers are shown. int i = 0; bool running = true; StringBuilder sb = new StringBuilder(1024); while (running) { if ((i++) % 10 == 0) { sb.Remove(0, sb.Length); running = torrents.Exists(m => m.State != TorrentState.Stopped); AppendFormat(sb, $"Transfer Rate: {engine.TotalDownloadSpeed / 1024.0:0.00}kB/sec down / {engine.TotalUploadSpeed / 1024.0:0.00}kB/sec up"); AppendFormat(sb, $"Memory Cache: {engine.DiskManager.CacheBytesUsed / 1024.0:0.00}/{engine.Settings.DiskCacheBytes / 1024.0:0.00} kB"); AppendFormat(sb, $"Disk IO Rate: {engine.DiskManager.ReadRate / 1024.0:0.00} kB/s read / {engine.DiskManager.WriteRate / 1024.0:0.00} kB/s write"); AppendFormat(sb, $"Disk IO Total: {engine.DiskManager.TotalBytesRead / 1024.0:0.00} kB read / {engine.DiskManager.TotalBytesWritten / 1024.0:0.00} kB written"); AppendFormat(sb, $"Open Connections: {engine.ConnectionManager.OpenConnections}"); // Print out the port mappings foreach (var mapping in engine.PortMappings.Created) { AppendFormat(sb, $"Successful Mapping {mapping.PublicPort}:{mapping.PrivatePort} ({mapping.Protocol})"); } foreach (var mapping in engine.PortMappings.Failed) { AppendFormat(sb, $"Failed mapping: {mapping.PublicPort}:{mapping.PrivatePort} ({mapping.Protocol})"); } foreach (var mapping in engine.PortMappings.Pending) { AppendFormat(sb, $"Pending mapping: {mapping.PublicPort}:{mapping.PrivatePort} ({mapping.Protocol})"); } foreach (TorrentManager manager in torrents) { AppendSeparator(sb); AppendFormat(sb, $"State: {manager.State}"); AppendFormat(sb, $"Name: {(manager.Torrent == null ? "MetaDataMode" : manager.Torrent.Name)}"); AppendFormat(sb, $"Progress: {manager.Progress:0.00}"); AppendFormat(sb, $"Transfer Rate: {manager.Monitor.DownloadSpeed / 1024.0:0.00}kB/s down / {manager.Monitor.UploadSpeed / 1024.0:0.00} kB/s up"); AppendFormat(sb, $"Total transferred: {manager.Monitor.DataBytesDownloaded / (1024.0 * 1024.0):0.00} MB down / {manager.Monitor.DataBytesUploaded / (1024.0 * 1024.0):0.00} MB up"); AppendFormat(sb, $"Tracker Status"); foreach (var tier in manager.TrackerManager.Tiers) { AppendFormat(sb, $"\t{tier.ActiveTracker} : Announce Succeeded: {tier.LastAnnounceSucceeded}. Scrape Succeeded: {tier.LastScrapeSucceeded}."); } if (manager.PieceManager != null) { AppendFormat(sb, "Current Requests: {0}", await manager.PieceManager.CurrentRequestCountAsync()); } var peers = await manager.GetPeersAsync(); AppendFormat(sb, "Outgoing:"); foreach (PeerId p in peers.Where(t => t.ConnectionDirection == Direction.Outgoing)) { AppendFormat(sb, "\t{2} - {1:0.00}/{3:0.00}kB/sec - {0} - {4} ({5})", p.Uri, p.Monitor.DownloadSpeed / 1024.0, p.AmRequestingPiecesCount, p.Monitor.UploadSpeed / 1024.0, p.EncryptionType, string.Join("|", p.SupportedEncryptionTypes.Select(t => t.ToString()).ToArray())); } AppendFormat(sb, ""); AppendFormat(sb, "Incoming:"); foreach (PeerId p in peers.Where(t => t.ConnectionDirection == Direction.Incoming)) { AppendFormat(sb, "\t{2} - {1:0.00}/{3:0.00}kB/sec - {0} - {4} ({5})", p.Uri, p.Monitor.DownloadSpeed / 1024.0, p.AmRequestingPiecesCount, p.Monitor.UploadSpeed / 1024.0, p.EncryptionType, string.Join("|", p.SupportedEncryptionTypes.Select(t => t.ToString()).ToArray())); } AppendFormat(sb, "", null); if (manager.Torrent != null) { foreach (var file in manager.Files) { AppendFormat(sb, "{1:0.00}% - {0}", file.Path, file.BitField.PercentComplete); } } } Console.Clear(); Console.WriteLine(sb.ToString()); listener.ExportTo(Console.Out); } Thread.Sleep(500); } }
private static async Task StartEngine() { int port; Torrent torrent = null; // Ask the user what port they want to use for incoming connections // 询问用户要将使用哪个端口用于连接 Console.Write($"{Environment.NewLine} 选择监听的端口: "); while (!Int32.TryParse(Console.ReadLine(), out port)) { } // 创建一个引擎的默认配置 // downloadsPath - 文件下载的目录 // port - 引擎监听的端口 EngineSettings engineSettings = new EngineSettings { SavePath = downloadsPath, ListenPort = port }; //engineSettings.GlobalMaxUploadSpeed = 30 * 1024; //engineSettings.GlobalMaxDownloadSpeed = 100 * 1024; //engineSettings.MaxReadRate = 1 * 1024 * 1024; // 创建一个 Torrent 默认的配置信息. TorrentSettings torrentDefaults = new TorrentSettings(); // 创建一个客户端引擎. engine = new ClientEngine(engineSettings); byte[] nodes = Array.Empty <byte> (); try { if (File.Exists(dhtNodeFile)) { nodes = File.ReadAllBytes(dhtNodeFile); } } catch { Console.WriteLine("No existing dht nodes could be loaded"); } DhtEngine dht = new DhtEngine(new IPEndPoint(IPAddress.Any, port)); await engine.RegisterDhtAsync(dht); // 这将启动Dht引擎,但不会等待完全初始化完成. // 这是因为根据连接节点时超时的数量,启动最多需要2分钟. await engine.DhtEngine.StartAsync(nodes); // 如果下载路径不存在,则创建之. if (!Directory.Exists(engine.Settings.SavePath)) { Directory.CreateDirectory(engine.Settings.SavePath); } // 如果Torrent存储目录不存在,则创建之. if (!Directory.Exists(torrentsPath)) { Directory.CreateDirectory(torrentsPath); } BEncodedDictionary fastResume = new BEncodedDictionary(); try { if (File.Exists(fastResumeFile)) { fastResume = BEncodedValue.Decode <BEncodedDictionary> (File.ReadAllBytes(fastResumeFile)); } } catch { } // 将Torrents目录中的每个 torrent 文件将其加载到引擎中. foreach (string file in Directory.GetFiles(torrentsPath)) { if (file.EndsWith(".torrent", StringComparison.OrdinalIgnoreCase)) { try { // 加载torrent文件到Torrent实例中,如果需要的话,可以使用它进行预处理 torrent = await Torrent.LoadAsync(file); Console.WriteLine(torrent.InfoHash.ToString()); } catch (Exception e) { Console.Write("Couldn't decode {0}: ", file); Console.WriteLine(e.Message); continue; } // 当任何预处理完成后,您将创建一个TorrentManager,然后在引擎上创建它. TorrentManager manager = new TorrentManager(torrent, downloadsPath, torrentDefaults); if (fastResume.ContainsKey(torrent.InfoHash.ToHex())) { manager.LoadFastResume(new FastResume((BEncodedDictionary)fastResume[torrent.InfoHash.ToHex()])); } await engine.Register(manager); // 将 TorrentManager 存储在列表中,方便以后访问它. torrents.Add(manager); manager.PeersFound += Manager_PeersFound; } } // If we loaded no torrents, just exist. The user can put files in the torrents directory and start the client again if (torrents.Count == 0) { Console.WriteLine("没有在目录中找到 torrent 文件"); Console.WriteLine("退出..."); engine.Dispose(); return; } // 遍历存储在列表中的每个TorrentManager,在TorrentManager中连接到事件并启动引擎。 foreach (TorrentManager manager in torrents) { manager.PeerConnected += (o, e) => { lock (listener) listener.WriteLine($"连接成功: {e.Peer.Uri}"); }; manager.ConnectionAttemptFailed += (o, e) => { lock (listener) listener.WriteLine( $"连接失败: {e.Peer.ConnectionUri} - {e.Reason} - {e.Peer.AllowedEncryption}"); }; // 每次散列一个片段,就会触发这个. manager.PieceHashed += delegate(object o, PieceHashedEventArgs e) { lock (listener) listener.WriteLine($"散列的片段: {e.PieceIndex} - {(e.HashPassed ? "Pass" : "Fail")}"); }; // 每当状态改变时触发 (Stopped -> Seeding -> Downloading -> Hashing) manager.TorrentStateChanged += delegate(object o, TorrentStateChangedEventArgs e) { lock (listener) listener.WriteLine($"旧状态: {e.OldState} 新状态: {e.NewState}"); }; // 每当跟踪器的状态改变时,就会触发. manager.TrackerManager.AnnounceComplete += (sender, e) => { listener.WriteLine($"{e.Successful}: {e.Tracker}"); }; // 开始运行TorrentManager. // 然后文件将散列(如果需要)并开始下载/发送. await manager.StartAsync(); } // Enable automatic port forwarding. The engine will use Mono.Nat to search for // uPnP or NAT-PMP compatible devices and then issue port forwarding requests to it. await engine.EnablePortForwardingAsync(CancellationToken.None); // This is how to access the list of port mappings, and to see if they were // successful, pending or failed. If they failed it could be because the public port // is already in use by another computer on your network. foreach (var successfulMapping in engine.PortMappings.Created) { } foreach (var failedMapping in engine.PortMappings.Failed) { } foreach (var failedMapping in engine.PortMappings.Pending) { } // While the torrents are still running, print out some stats to the screen. // Details for all the loaded torrent managers are shown. int i = 0; bool running = true; StringBuilder sb = new StringBuilder(1024); while (running) { if ((i++) % 10 == 0) { sb.Remove(0, sb.Length); running = torrents.Exists(m => m.State != TorrentState.Stopped); AppendFormat(sb, "Total Download Rate: {0:0.00}kB/sec", engine.TotalDownloadSpeed / 1024.0); AppendFormat(sb, "Total Upload Rate: {0:0.00}kB/sec", engine.TotalUploadSpeed / 1024.0); AppendFormat(sb, "Disk Read Rate: {0:0.00} kB/s", engine.DiskManager.ReadRate / 1024.0); AppendFormat(sb, "Disk Write Rate: {0:0.00} kB/s", engine.DiskManager.WriteRate / 1024.0); AppendFormat(sb, "Total Read: {0:0.00} kB", engine.DiskManager.TotalRead / 1024.0); AppendFormat(sb, "Total Written: {0:0.00} kB", engine.DiskManager.TotalWritten / 1024.0); AppendFormat(sb, "Open Connections: {0}", engine.ConnectionManager.OpenConnections); foreach (TorrentManager manager in torrents) { AppendSeparator(sb); AppendFormat(sb, "State: {0}", manager.State); AppendFormat(sb, "Name: {0}", manager.Torrent == null ? "MetaDataMode" : manager.Torrent.Name); AppendFormat(sb, "Progress: {0:0.00}", manager.Progress); AppendFormat(sb, "Download Speed: {0:0.00} kB/s", manager.Monitor.DownloadSpeed / 1024.0); AppendFormat(sb, "Upload Speed: {0:0.00} kB/s", manager.Monitor.UploadSpeed / 1024.0); AppendFormat(sb, "Total Downloaded: {0:0.00} MB", manager.Monitor.DataBytesDownloaded / (1024.0 * 1024.0)); AppendFormat(sb, "Total Uploaded: {0:0.00} MB", manager.Monitor.DataBytesUploaded / (1024.0 * 1024.0)); AppendFormat(sb, "Tracker Status"); foreach (var tier in manager.TrackerManager.Tiers) { AppendFormat(sb, $"\t{tier.ActiveTracker} : Announce Succeeded: {tier.LastAnnounceSucceeded}. Scrape Succeeded: {tier.LastScrapSucceeded}."); } if (manager.PieceManager != null) { AppendFormat(sb, "Current Requests: {0}", await manager.PieceManager.CurrentRequestCountAsync()); } foreach (PeerId p in await manager.GetPeersAsync()) { AppendFormat(sb, "\t{2} - {1:0.00}/{3:0.00}kB/sec - {0}", p.Uri, p.Monitor.DownloadSpeed / 1024.0, p.AmRequestingPiecesCount, p.Monitor.UploadSpeed / 1024.0); } AppendFormat(sb, "", null); if (manager.Torrent != null) { foreach (TorrentFile file in manager.Torrent.Files) { AppendFormat(sb, "{1:0.00}% - {0}", file.Path, file.BitField.PercentComplete); } } } Console.Clear(); Console.WriteLine(sb.ToString()); listener.ExportTo(Console.Out); } Thread.Sleep(500); } // 停止搜索与uPnP或NAT-PMP兼容的设备,并删除所有已创建的映射. await engine.DisablePortForwardingAsync(CancellationToken.None); }