From f2c01d2a38d6ce888e35b62cfbab11dd8efc5a96 Mon Sep 17 00:00:00 2001 From: Stone_Red <56473591+Stone-Red-Code@users.noreply.github.com> Date: Thu, 27 Jan 2022 17:18:05 +0100 Subject: [PATCH] - Add ability to add host after setup - Add more commands - Improve file downloading speed --- .../FileProcessing/FilesManager.cs | 4 +- .../Networking/BroadcastClient.cs | 9 +- .../Networking/NetworkClient.cs | 6 +- HyperbolicDownloader/Program.cs | 9 +- .../UserInterface/InputHandler.cs | 290 +++++++++++++----- HyperbolicDownloader/UserInterface/Setup.cs | 2 + 6 files changed, 232 insertions(+), 88 deletions(-) diff --git a/HyperbolicDownloader/FileProcessing/FilesManager.cs b/HyperbolicDownloader/FileProcessing/FilesManager.cs index a287855..5bdc819 100644 --- a/HyperbolicDownloader/FileProcessing/FilesManager.cs +++ b/HyperbolicDownloader/FileProcessing/FilesManager.cs @@ -65,11 +65,11 @@ internal class FilesManager public bool TryRemove(string hash) { - files.RemoveAll(f => f.Hash == hash); + int count = files.RemoveAll(f => f.Hash == hash); SaveFiles(); - return true; + return count > 0; } public bool Contains(string? hash) diff --git a/HyperbolicDownloader/Networking/BroadcastClient.cs b/HyperbolicDownloader/Networking/BroadcastClient.cs index 103fff5..4bdd19a 100644 --- a/HyperbolicDownloader/Networking/BroadcastClient.cs +++ b/HyperbolicDownloader/Networking/BroadcastClient.cs @@ -15,9 +15,12 @@ internal class BroadcastClient private UdpClient? udpListener; - public void Send(int port, string message) + public static void Send(int port, string message) { - Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp) + { + EnableBroadcast = true + }; IPAddress? ip4Address = NetworkUtilities.GetIP4Adress(); @@ -40,7 +43,7 @@ internal class BroadcastClient byte[] sendbuf = Encoding.ASCII.GetBytes(message); IPEndPoint ep = new IPEndPoint(broadcast, port); - s.SendTo(sendbuf, ep); + socket.SendTo(sendbuf, ep); } public void StartListening(int port) diff --git a/HyperbolicDownloader/Networking/NetworkClient.cs b/HyperbolicDownloader/Networking/NetworkClient.cs index 4dc9574..fd11e2b 100644 --- a/HyperbolicDownloader/Networking/NetworkClient.cs +++ b/HyperbolicDownloader/Networking/NetworkClient.cs @@ -158,7 +158,7 @@ namespace HyperbolicDownloader.Networking byte[] bytesToSend; hash = hash.Trim(); NetworkStream nwStream = client.GetStream(); - + client.SendBufferSize = 64000; if (filesManager.TryGet(hash, out HyperFileInfo? hyperFileInfo) && File.Exists(hyperFileInfo?.FilePath)) { if (hash != await FileValidator.CalculateHashAsync(hyperFileInfo.FilePath)) @@ -174,10 +174,10 @@ namespace HyperbolicDownloader.Networking FileInfo compressedFileInfo = new FileInfo(hyperFileInfo.FilePath); - bytesToSend = Encoding.ASCII.GetBytes(compressedFileInfo.Length.ToString()); + bytesToSend = Encoding.ASCII.GetBytes($"{compressedFileInfo.Length}/{Path.GetFileName(hyperFileInfo.FilePath)}"); await nwStream.WriteAsync(bytesToSend); - foreach (byte[]? chunk in FileCompressor.ReadChunks(hyperFileInfo.FilePath, 1000)) + foreach (byte[]? chunk in FileCompressor.ReadChunks(hyperFileInfo.FilePath, 64000)) { if (chunk is not null) { diff --git a/HyperbolicDownloader/Program.cs b/HyperbolicDownloader/Program.cs index c433f42..d41e4dc 100644 --- a/HyperbolicDownloader/Program.cs +++ b/HyperbolicDownloader/Program.cs @@ -25,13 +25,14 @@ internal class Program private static NatDevice? device; private static Mapping? portMapping; private static readonly HostsManager hostsManager = new(); - private static readonly FilesManager filesManager = new FilesManager(); + private static readonly FilesManager filesManager = new(); private static readonly NetworkClient networkClient = new(filesManager); - private static readonly Random random = new Random(); + private static readonly Random random = new(); private static async Task Main() { Console.CancelKeyPress += Console_CancelKeyPress; + Console.CursorVisible = false; if (File.Exists(HostsFilePath)) { @@ -70,7 +71,7 @@ internal class Program BroadcastClient broadcastClient = new BroadcastClient(); Console.WriteLine("Running local discovery routine..."); - broadcastClient.Send(BroadcastPort, privatePort.ToString()); + BroadcastClient.Send(BroadcastPort, privatePort.ToString()); await Task.Delay(5000); if (hostsManager.Count > 0) @@ -81,7 +82,7 @@ internal class Program if (hostsManager.Count == 0) { - hostsManager.AddRange(await UserInterface.Setup.ConfigureHost()); + hostsManager.AddRange(await Setup.ConfigureHost()); Console.WriteLine("Checking if hosts are active..."); hostsManager.RemoveInactiveHosts(); } diff --git a/HyperbolicDownloader/UserInterface/InputHandler.cs b/HyperbolicDownloader/UserInterface/InputHandler.cs index aae30d9..929a36e 100644 --- a/HyperbolicDownloader/UserInterface/InputHandler.cs +++ b/HyperbolicDownloader/UserInterface/InputHandler.cs @@ -4,7 +4,9 @@ using HyperbolicDownloader.FileProcessing; using HyperbolicDownloader.Networking; using Stone_Red_Utilities.ConsoleExtentions; +using Stone_Red_Utilities.StringExtentions; +using System.Diagnostics; using System.Net; using System.Net.Sockets; using System.Text; @@ -24,8 +26,12 @@ internal class InputHandler commander.Register((_) => Console.Clear(), "clear"); commander.Register(Exit, "exit"); commander.Register(GetFile, "get"); - commander.Register(AddFile, "add"); + Command addCommand = commander.Register(AddFile, "add"); + addCommand.Register(AddHost, "host"); + addCommand.Register(AddFile, "file"); + commander.Register(RemoveFile, "remove"); commander.Register(ListFiles, "list"); + this.filesManager = filesManager; } @@ -34,8 +40,11 @@ internal class InputHandler while (!exit) { Console.Write("> "); - string input = Console.ReadLine() ?? string.Empty; + Console.CursorVisible = true; + string input = Console.ReadLine()?.Trim() ?? string.Empty; + + Console.CursorVisible = false; if (!commander.Execute(input)) { ConsoleExt.WriteLine("Unknown command!", ConsoleColor.Red); @@ -43,94 +52,51 @@ internal class InputHandler } } - private void GetFile(string hash) + private void AddHost(string args) { - if (string.IsNullOrEmpty(hash)) + string[] parts = args.Split(":"); + + if (parts.Length != 2) { - Console.WriteLine("No hash value specified!"); + ConsoleExt.WriteLine("Invalid format! Use this format: (xxx.xxx.xxx.xxx:yyyy)", ConsoleColor.Red); + return; } - foreach (NetworkSocket host in hostsManager.ToList()) + string ipAddressInput = parts[0]; + string portInput = parts[1]; + + _ = int.TryParse(portInput, out int port); + if (port < 1000 || port >= 6000) { - bool validIpAdress = IPAddress.TryParse(host.IPAddress, out IPAddress? ipAddress); - - if (validIpAdress) + ConsoleExt.WriteLine("Invalid port number!", ConsoleColor.Red); + } + else if (IPAddress.TryParse(ipAddressInput, out IPAddress? ipAddress)) + { + try { - ConsoleExt.Write($"{host.IPAddress}:{host.Port} > ???", ConsoleColor.DarkYellow); - try + Console.WriteLine("Waiting for response..."); + NetworkSocket? localSocket = Program.GetLocalSocket() ?? new NetworkSocket("0.0.0.0", 0); + List? recivedHosts = NetworkClient.Send>(ipAddress, port, "GetHostsList", localSocket); + + if (recivedHosts is not null) { - Console.CursorLeft = 0; - - Task sendTask = NetworkClient.SendAsync(ipAddress!, host.Port, "HasFile", hash); - - _ = sendTask.Wait(1000); - - if (sendTask.Result) - { - ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Has the requested file", ConsoleColor.Green); - Console.WriteLine("Requesting file..."); - - TcpClient tcpClient = new TcpClient(); - tcpClient.Connect(ipAddress!, host.Port); - - NetworkStream nwStream = tcpClient.GetStream(); - byte[] buffer = new byte[tcpClient.ReceiveBufferSize]; - byte[] reciveBuffer = new byte[1000]; - - byte[] bytesToSend = Encoding.ASCII.GetBytes($"Download {hash}"); - nwStream.Write(bytesToSend); - int bytesRead = nwStream.Read(buffer, 0, tcpClient.ReceiveBufferSize); - - string dataReceived = Encoding.ASCII.GetString(buffer, 0, bytesRead); - - if (int.TryParse(dataReceived, out int fileSize)) //If received data is not a size an error occurred - { - Console.WriteLine($"Starting download..."); - - int totalBytesRead = 0; - - using FileStream? fileStream = new FileStream($"{hash}.txt", FileMode.Create); - - while (totalBytesRead < fileSize) - { - bytesRead = nwStream.Read(reciveBuffer, 0, reciveBuffer.Length); - - bytesRead = Math.Min(reciveBuffer.Length, fileSize - totalBytesRead); - - fileStream.Write(reciveBuffer, 0, bytesRead); - totalBytesRead += bytesRead; - - Console.CursorLeft = 0; - Console.Out.WriteAsync($"Downloading: {Math.Ceiling(100d / fileSize * totalBytesRead)}% {totalBytesRead}/{fileSize}"); - } - - fileStream.Close(); - //FileCompressor.DecompressFile($"{hash}.gz", $"result.txt"); - Console.WriteLine(); - ConsoleExt.WriteLine("Done", ConsoleColor.Green); - return; - } - else - { - ConsoleExt.WriteLine(dataReceived, ConsoleColor.Red); - } - } - else - { - ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Does not have the requested file", ConsoleColor.Red); - } + ConsoleExt.WriteLine($"Success! Added {recivedHosts.Count} new host(s).", ConsoleColor.Green); + hostsManager.AddRange(recivedHosts); } - catch (SocketException) + else { - Console.CursorLeft = 0; - ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Inactive", ConsoleColor.Red); + ConsoleExt.WriteLine($"Invalid response!", ConsoleColor.Red); } } - else + catch (SocketException ex) { - hostsManager.Remove(host); + ConsoleExt.WriteLine($"Invalid host! Error message: {ex.Message}", ConsoleColor.Red); } } + else + { + ConsoleExt.WriteLine("Invalid IP address!", ConsoleColor.Red); + } } private void AddFile(string args) @@ -146,6 +112,18 @@ internal class InputHandler } } + private void RemoveFile(string args) + { + if (filesManager.TryRemove(args)) + { + ConsoleExt.WriteLine($"Removed file successfully!", ConsoleColor.Green); + } + else + { + ConsoleExt.WriteLine("The file is not being tracked!", ConsoleColor.Red); + } + } + private void ListFiles(string _) { int index = 0; @@ -162,4 +140,164 @@ internal class InputHandler { exit = true; } + + private void GetFile(string hash) + { + if (string.IsNullOrEmpty(hash)) + { + Console.WriteLine("No hash value specified!"); + } + + hash = hash.Trim(); + + foreach (NetworkSocket host in hostsManager.ToList()) + { + bool validIpAdress = IPAddress.TryParse(host.IPAddress, out IPAddress? ipAddress); + + if (!validIpAdress) + { + hostsManager.Remove(host); + continue; + } + + ConsoleExt.Write($"{host.IPAddress}:{host.Port} > ???", ConsoleColor.DarkYellow); + + Console.CursorLeft = 0; + + Task sendTask = NetworkClient.SendAsync(ipAddress!, host.Port, "HasFile", hash); + + _ = sendTask.Wait(1000); + + if (!sendTask.IsCompletedSuccessfully) + { + Console.CursorLeft = 0; + ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Inactive", ConsoleColor.Red); + hostsManager.Remove(host); + continue; + } + else if (!sendTask.Result) + { + ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Does not have the requested file", ConsoleColor.Red); + continue; + } + + ConsoleExt.WriteLine($"{host.IPAddress}:{host.Port} > Has the requested file", ConsoleColor.Green); + Console.WriteLine("Requesting file..."); + + TcpClient tcpClient = new TcpClient(); + tcpClient.Connect(ipAddress!, host.Port); + tcpClient.ReceiveBufferSize = 64000; + + NetworkStream nwStream = tcpClient.GetStream(); + byte[] buffer = new byte[tcpClient.ReceiveBufferSize]; + byte[] reciveBuffer = new byte[64000]; + + byte[] bytesToSend = Encoding.ASCII.GetBytes($"Download {hash}"); + nwStream.Write(bytesToSend); + nwStream.ReadTimeout = 5000; + int bytesRead = nwStream.Read(buffer, 0, tcpClient.ReceiveBufferSize); + + string dataReceived = Encoding.ASCII.GetString(buffer, 0, bytesRead); + + string[] parts = dataReceived.Split('/'); + + if (parts.Length == 2) //If received data does not contain 2 parts -> error + { + bool validFileSize = int.TryParse(parts[0], out int fileSize); + + if (!validFileSize || fileSize <= 0) + { + ConsoleExt.WriteLine("Invalid file size!", ConsoleColor.Red); + continue; + } + + string fileName = parts[1].ToFileName(); + + Console.WriteLine($"File name: {fileName}"); + Console.WriteLine($"Starting download..."); + + int totalBytesRead = 0; + + if (!Directory.Exists("./Downloads")) + { + Directory.CreateDirectory("./Downloads"); + } + + using FileStream? fileStream = new FileStream($"./Downloads/{fileName}", FileMode.Create); + + int bytesInOneSecond = 0; + int unitsPerSecond = 0; + string unit = "Kb"; + + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + while (totalBytesRead < fileSize) + { + try + { + bytesRead = nwStream.Read(reciveBuffer, 0, reciveBuffer.Length); + } + catch (IOException) + { + Console.WriteLine(); + ConsoleExt.WriteLine("Lost connection to other host!", ConsoleColor.Red); + break; + } + + bytesRead = Math.Min(bytesRead, fileSize - totalBytesRead); + + fileStream.Write(reciveBuffer, 0, bytesRead); + totalBytesRead += bytesRead; + + bytesInOneSecond += bytesRead; + + if (stopWatch.ElapsedMilliseconds >= 1000) + { + unitsPerSecond = (unitsPerSecond + bytesInOneSecond) / 2; + if (unitsPerSecond > 125000) + { + unitsPerSecond = unitsPerSecond / 125000; + unit = "Mb"; + } + else + { + unitsPerSecond = unitsPerSecond / 125; + unit = "Kb"; + } + bytesInOneSecond = 0; + stopWatch.Restart(); + } + + Console.CursorLeft = 0; + + Console.Out.WriteAsync($"Downloading: {Math.Clamp(Math.Ceiling(100d / fileSize * totalBytesRead), 0, 100)}% {totalBytesRead / 1000}/{fileSize / 1000}KB [{unitsPerSecond}{unit}/s] "); + } + + fileStream.Close(); + + if (totalBytesRead < fileSize) + { + continue; + } + + Console.WriteLine(); + + Console.WriteLine("Validating file..."); + if (!FileValidator.ValidateHash($"./Downloads/{fileName}", hash)) + { + ConsoleExt.WriteLine("Warning: File hash does not match! File might me corrupted or manipulated!", ConsoleColor.DarkYellow); + } + + Console.WriteLine($"File saved at: {Path.GetFullPath($"./Downloads/{fileName}")}"); + ConsoleExt.WriteLine("Done", ConsoleColor.Green); + stopWatch.Stop(); + return; + } + else + { + ConsoleExt.WriteLine(dataReceived, ConsoleColor.Red); + } + } + ConsoleExt.WriteLine("None of the available hosts have the requested file!", ConsoleColor.Red); + } } \ No newline at end of file diff --git a/HyperbolicDownloader/UserInterface/Setup.cs b/HyperbolicDownloader/UserInterface/Setup.cs index 3a21f20..92bc346 100644 --- a/HyperbolicDownloader/UserInterface/Setup.cs +++ b/HyperbolicDownloader/UserInterface/Setup.cs @@ -15,11 +15,13 @@ internal static class Setup do { Console.WriteLine(); + Console.CursorVisible = true; Console.Write("Please enter an IP address manually: "); string? ipAddressInput = Console.ReadLine(); Console.Write("Please enter an port number manually: "); string? portInput = Console.ReadLine(); + Console.CursorVisible = false; _ = int.TryParse(portInput, out int port); if (port < 1000 || port >= 6000)