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
+
+
+