diff --git a/WorkWithFTP/.idea/.idea.WorkWithFTP/.idea/.gitignore b/WorkWithFTP/.idea/.idea.WorkWithFTP/.idea/.gitignore new file mode 100644 index 0000000..495e278 --- /dev/null +++ b/WorkWithFTP/.idea/.idea.WorkWithFTP/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/.idea.WorkWithFTP.iml +/modules.xml +/contentModel.xml +/projectSettingsUpdater.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/WorkWithFTP/.idea/.idea.WorkWithFTP/.idea/indexLayout.xml b/WorkWithFTP/.idea/.idea.WorkWithFTP/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/WorkWithFTP/.idea/.idea.WorkWithFTP/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/WorkWithFTP/.idea/.idea.WorkWithFTP/.idea/vcs.xml b/WorkWithFTP/.idea/.idea.WorkWithFTP/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/WorkWithFTP/.idea/.idea.WorkWithFTP/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/WorkWithFTP/TestsForFTP/Tests.cs b/WorkWithFTP/TestsForFTP/Tests.cs new file mode 100644 index 0000000..44079ac --- /dev/null +++ b/WorkWithFTP/TestsForFTP/Tests.cs @@ -0,0 +1,79 @@ +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using WorkWithFTPClient; +using WorkWithFTP; +using NUnit.Framework; + +namespace TestsForFTP +{ + public class Tests + { + private string ip = "127.0.0.1"; + private int port = 1488; + private Client client; + private Server server; + private string pathForData = "../../../data/"; + private CancellationTokenSource tokenSource; + + [SetUp] + public void SetUp() + { + server = new Server(ip, port); + client = new Client(ip, port); + tokenSource = new CancellationTokenSource(); + } + + [Test] + public async Task TestShouldThrowExceptionIfListWithIncorrectPath() + { + using var handle = server.StartServer(); + Assert.Throws(() => client.List("pluuuuug", tokenSource.Token).Wait()); + } + + [Test] + public async Task TestShouldThrowExceptionIfGetWithIncorrectPath() + { + using var handle = server.StartServer(); + Assert.Throws(() => client.Get("pluuuuug", null, tokenSource.Token).Wait()); + } + + [Test] + public async Task TestForList() + { + using var handle = server.StartServer(); + var result = new[] + { + (pathForData + "plug.txt", false), + (pathForData + "WorkWithVK.dll", false), + }; + var response = await client.List(pathForData, tokenSource.Token); + Assert.AreEqual(result.Length, response.Length); + for (int i = 0; i < result.Length; i++) + { + Assert.IsTrue(response.Any(x => x == result[i])); + } + } + + [Test] + public async Task TestForGet() + { + using var handle = server.StartServer(); + var destination = pathForData + "WorkWithVK2.dll"; + var pathForFile = pathForData + "WorkWithVK.dll"; + var result = File.ReadAllBytes(pathForFile); + using (var fstream = new FileStream(destination, FileMode.OpenOrCreate)) + { + var response = await client.Get(pathForFile, fstream, tokenSource.Token); + Assert.AreEqual(result.Length, response); + } + + var result2 = File.ReadAllBytes(destination); + Assert.AreEqual(result, result2); + File.Delete(destination); + } + } +} \ No newline at end of file diff --git a/WorkWithFTP/TestsForFTP/TestsForFTP.csproj b/WorkWithFTP/TestsForFTP/TestsForFTP.csproj new file mode 100644 index 0000000..4bf7466 --- /dev/null +++ b/WorkWithFTP/TestsForFTP/TestsForFTP.csproj @@ -0,0 +1,21 @@ + + + + net5.0 + + false + + + + + + + + + + + + + + + diff --git a/WorkWithFTP/TestsForFTP/data/WorkWithVK.dll b/WorkWithFTP/TestsForFTP/data/WorkWithVK.dll new file mode 100644 index 0000000..6562b1a Binary files /dev/null and b/WorkWithFTP/TestsForFTP/data/WorkWithVK.dll differ diff --git a/WorkWithFTP/TestsForFTP/data/plug.txt b/WorkWithFTP/TestsForFTP/data/plug.txt new file mode 100644 index 0000000..e69de29 diff --git a/WorkWithFTP/WorkWithFTP.sln b/WorkWithFTP/WorkWithFTP.sln new file mode 100644 index 0000000..3e564d0 --- /dev/null +++ b/WorkWithFTP/WorkWithFTP.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkWithFTPServer", "WorkWithFTPServer\WorkWithFTPServer.csproj", "{02771972-984B-42EB-BFB3-3BC98025E7D6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkWithFTPClient", "WorkWithFTPClient\WorkWithFTPClient.csproj", "{23186ECF-69E7-457E-A474-5568A6549E80}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestsForFTP", "TestsForFTP\TestsForFTP.csproj", "{3DF979A8-9F50-45B9-8F41-A84270CDCA17}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {02771972-984B-42EB-BFB3-3BC98025E7D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {02771972-984B-42EB-BFB3-3BC98025E7D6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {02771972-984B-42EB-BFB3-3BC98025E7D6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {02771972-984B-42EB-BFB3-3BC98025E7D6}.Release|Any CPU.Build.0 = Release|Any CPU + {23186ECF-69E7-457E-A474-5568A6549E80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {23186ECF-69E7-457E-A474-5568A6549E80}.Debug|Any CPU.Build.0 = Debug|Any CPU + {23186ECF-69E7-457E-A474-5568A6549E80}.Release|Any CPU.ActiveCfg = Release|Any CPU + {23186ECF-69E7-457E-A474-5568A6549E80}.Release|Any CPU.Build.0 = Release|Any CPU + {3DF979A8-9F50-45B9-8F41-A84270CDCA17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3DF979A8-9F50-45B9-8F41-A84270CDCA17}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3DF979A8-9F50-45B9-8F41-A84270CDCA17}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3DF979A8-9F50-45B9-8F41-A84270CDCA17}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/WorkWithFTP/WorkWithFTPClient/Client.cs b/WorkWithFTP/WorkWithFTPClient/Client.cs new file mode 100644 index 0000000..352e4fc --- /dev/null +++ b/WorkWithFTP/WorkWithFTPClient/Client.cs @@ -0,0 +1,87 @@ +using System; +using System.Dynamic; +using System.IO; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace WorkWithFTPClient +{ + /// + /// Клиент сервера + /// + public class Client + { + private readonly int port; + private readonly string hostName; + + public Client(string hostName, int port) + { + this.hostName = hostName; + this.port = port; + } + + /// + /// Скачивает файл с сервера + /// + public async Task Get(string path, FileStream destination, CancellationToken token) + { + using var client = new TcpClient(); + await client.ConnectAsync(hostName, port, token); + using var stream = client.GetStream(); + using var writer = new StreamWriter(stream){AutoFlush = true}; + using var reader = new StreamReader(stream); + await writer.WriteLineAsync($"2 {path}"); + var size = new char[long.MaxValue.ToString().Length + 1]; + var index = 0; + await reader.ReadAsync(size, index, 1); + if (size[0] == '-') + { + throw new ArgumentException(); + } + while (size[index] != ' ') + { + index++; + await reader.ReadAsync(size, index, 1); + } + + var countInString = ""; + for (int i = 0; i < index; i++) + { + countInString += size[i].ToString(); + } + var count = long.Parse(countInString); + await stream.CopyToAsync(destination, token); + return count; + } + + /// + /// Показывает все папки и файлы, которые лежат на сервере по заданному пути + /// + public async Task<(string, bool)[]> List(string path, CancellationToken token) + { + using var client = new TcpClient(); + await client.ConnectAsync(hostName, port, token); + using var stream = client.GetStream(); + using var writer = new StreamWriter(stream){AutoFlush = true}; + using var reader = new StreamReader(stream); + await writer.WriteLineAsync($"1 {path}"); + var response = await reader.ReadLineAsync(); + var splittedResponse = response.Split(' '); + if (splittedResponse[0] == "-1") + { + throw new ArgumentException(); + } + var countOfFiles = int.Parse(splittedResponse[0]); + var files = new (string, bool)[countOfFiles]; + for (int i = 1; i < splittedResponse.Length; i += 2) + { + var isDir = splittedResponse[i + 1] == "True"; + files[(i - 1) / 2] = (splittedResponse[i], isDir); + } + + return files; + } + } +} \ No newline at end of file diff --git a/WorkWithFTP/WorkWithFTPClient/Program.cs b/WorkWithFTP/WorkWithFTPClient/Program.cs new file mode 100644 index 0000000..f4a7030 --- /dev/null +++ b/WorkWithFTP/WorkWithFTPClient/Program.cs @@ -0,0 +1,56 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace WorkWithFTPClient +{ + class Program + { + static async Task Main(string[] args) + { + var client = new Client(args[0], int.Parse(args[1])); + Console.WriteLine("List:\nФормат запроса:1 \npath - путь к директории относительно того места, где запущен сервер"); + Console.WriteLine("Get:\nФормат запроса:2 \npath1 - путь к файлу относительно того места, где запущен сервер\n" + + "npath2 - путь к файлу на локальной машине, где запущен клиент"); + Console.WriteLine("Введите exit, если хотите закрыть соединение с сервером"); + var request = Console.ReadLine().Split(' '); + while (request[0] != "exit") + { + if (request[0] == "1") + { + try + { + var sourceToken = new CancellationTokenSource(); + var response = await client.List(request[1], sourceToken.Token); + foreach (var file in response) + { + Console.WriteLine($"{file.Item1} {file.Item2}"); + } + } + catch (Exception e) + { + Console.WriteLine("Упс... Что-то пошло не так"); + } + } + + if (request[0] == "2") + { + using (var fstream = new FileStream(request[2], FileMode.OpenOrCreate)) + { + try + { + var sourceToken = new CancellationTokenSource(); + var response = client.Get(request[1], fstream, sourceToken.Token); + } + catch (Exception e) + { + Console.WriteLine("Упс... Что-то пошло не так"); + } + } + } + request = Console.ReadLine().Split(' '); + } + } + } +} \ No newline at end of file diff --git a/WorkWithFTP/WorkWithFTPClient/WorkWithFTPClient.csproj b/WorkWithFTP/WorkWithFTPClient/WorkWithFTPClient.csproj new file mode 100644 index 0000000..9590466 --- /dev/null +++ b/WorkWithFTP/WorkWithFTPClient/WorkWithFTPClient.csproj @@ -0,0 +1,8 @@ + + + + Exe + net5.0 + + + diff --git a/WorkWithFTP/WorkWithFTPServer/Program.cs b/WorkWithFTP/WorkWithFTPServer/Program.cs new file mode 100644 index 0000000..0bdeaa0 --- /dev/null +++ b/WorkWithFTP/WorkWithFTPServer/Program.cs @@ -0,0 +1,29 @@ + +using System; + +namespace WorkWithFTP +{ + class Program + { + static void Main(string[] args) + { + string ip = args[0]; + var port = int.Parse(args[1]); + try + { + var server = new Server(ip, port); + using var handler = server.StartServer(); + Console.WriteLine("Введите stop, чтобы остановить сервер"); + var command = ""; + while (command != "stop") + { + command = Console.ReadLine(); + } + } + catch (Exception e) + { + Console.WriteLine("Упс... что-то пошло не так"); + } + } + } +} \ No newline at end of file diff --git a/WorkWithFTP/WorkWithFTPServer/Server.cs b/WorkWithFTP/WorkWithFTPServer/Server.cs new file mode 100644 index 0000000..f08cbc3 --- /dev/null +++ b/WorkWithFTP/WorkWithFTPServer/Server.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace WorkWithFTP +{ + /// + /// Класс сервера, реализующего протокол FTP + /// + public class Server + { + private readonly int port; + private readonly string IPAdress; + private CancellationTokenSource tokenSource; + private List tasks; + private TcpListener listener; + + private class ServerHandler : IDisposable + { + private Action stopServer; + + public ServerHandler(Action stopServer) + { + this.stopServer = stopServer; + } + + public void Dispose() + { + stopServer(); + } + } + + public Server(string ipAdress, int port) + { + IPAdress = ipAdress; + this.port = port; + tasks = new(); + } + + enum Request + { + List = 1, + Get, + Nonsense + } + + /// + /// Запускает сервер + /// + public IDisposable StartServer() + { + listener = new TcpListener(IPAddress.Parse(IPAdress), port); + listener.Start(); + tokenSource = new CancellationTokenSource(); + var mainTask = Task.Run(async () => + { + while (!tokenSource.Token.IsCancellationRequested) + { + var client = await listener.AcceptTcpClientAsync(); + var task = Task.Run(() => WorkWithClient(client)); + tasks.Add(task); + } + }); + tasks.Add(mainTask); + return new ServerHandler(StopServer); + } + + /// + /// Завершает работу сервера + /// + public void StopServer() + { + tokenSource.Cancel(); + Task.WhenAll(tasks); + listener.Stop(); + } + + private async void WorkWithClient(TcpClient client) + { + using (client) + { + using var stream = client.GetStream(); + using var reader = new StreamReader(stream); + using var writer = new StreamWriter(stream) {AutoFlush = true}; + var data = await reader.ReadLineAsync(); + (var request, var path) = ParseData(data); + switch (request) + { + case Request.Nonsense: + await writer.WriteAsync("Bro you broke protocol, don't do that anymore, please"); + break; + case Request.List: + await ResponseForList(path, writer); + break; + default: + await ResponseForGet(path, writer, stream); + break; + } + } + } + + private (Request, string) ParseData(string data) + { + if (data[0] != '1' && data[0] != '2') + { + return (Request.Nonsense, ""); + } + + var request = data[0] == '1' ? Request.List : Request.Get; + if (data[1] != ' ') + { + return (Request.Nonsense, ""); + } + + var path = data.Substring(2); + return (request, path); + } + + private async Task ResponseForList(string path, StreamWriter writer) + { + if (!Directory.Exists(path)) + { + await writer.WriteLineAsync("-1 Directory doesn't exist"); + return; + } + var files = Directory.GetFiles(path); + var directories = Directory.GetDirectories(path); + var response = new StringBuilder(); + response.Append((files.Length + directories.Length).ToString()); + files.ToList().ForEach(x => response.Append($" {x} False")); + directories.ToList().ForEach(x => response.Append($" {x} True")); + await writer.WriteLineAsync(response.ToString()); + } + + private async Task ResponseForGet(string path, StreamWriter writer, NetworkStream stream) + { + if (!File.Exists(path)) + { + await writer.WriteLineAsync("-1 File doesn't exist"); + return; + } + + using var fileStream = new FileStream(path, FileMode.Open); + await writer.WriteAsync(fileStream.Length.ToString() + " "); + await fileStream.CopyToAsync(stream); + await stream.FlushAsync(); + } + } +} \ No newline at end of file diff --git a/WorkWithFTP/WorkWithFTPServer/WorkWithFTPServer.csproj b/WorkWithFTP/WorkWithFTPServer/WorkWithFTPServer.csproj new file mode 100644 index 0000000..ed8c327 --- /dev/null +++ b/WorkWithFTP/WorkWithFTPServer/WorkWithFTPServer.csproj @@ -0,0 +1,9 @@ + + + + Exe + net5.0 + WorkWithFTP + + +