diff --git a/MyFTP/MyFTP.Test/MyFTP.Test.csproj b/MyFTP/MyFTP.Test/MyFTP.Test.csproj
new file mode 100644
index 0000000..dcfd5bc
--- /dev/null
+++ b/MyFTP/MyFTP.Test/MyFTP.Test.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net6.0
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MyFTP/MyFTP.Test/MyFTPTest.cs b/MyFTP/MyFTP.Test/MyFTPTest.cs
new file mode 100644
index 0000000..e43be81
--- /dev/null
+++ b/MyFTP/MyFTP.Test/MyFTPTest.cs
@@ -0,0 +1,91 @@
+namespace MyFTP.Test;
+
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using MyFTPClient;
+using MyFTPServer;
+using NUnit.Framework;
+
+public class Tests
+{
+ private Server _server;
+ private Client _client;
+ private Stream _fileStream;
+ private CancellationToken _cancellationToken;
+
+ [SetUp]
+ public void Setup()
+ {
+ _server = new Server("127.0.0.1", 80);
+ _client = new Client("127.0.0.1", 80);
+ _fileStream = new MemoryStream();
+ _cancellationToken = new ();
+ _server.StartServer();
+ Thread.Sleep(5000);
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ _server.StopServer();
+ }
+
+ [Test]
+ public void GetInvalidFileNameTest()
+ {
+ Assert.ThrowsAsync(() => _client.Get("Text.txt", _fileStream, _cancellationToken));
+ }
+
+ [Test]
+ public void ListInvalidFileNameTest()
+ {
+ Assert.ThrowsAsync(() => _client.Get("Text.txt", _fileStream, _cancellationToken));
+ }
+
+ [Test]
+ public async Task ListTest()
+ {
+ var data = await _client.List("../../../TestData", _cancellationToken);
+ Assert.AreEqual("../../../TestData\\Data.txt", data[0].name);
+ Assert.AreEqual(false, data[0].isDir);
+ }
+
+ [Test]
+ public async Task GetTest()
+ {
+ var destination = "../../../TestData/Data1.txt";
+ var pathForFile = "../../../TestData/Data.txt";
+ var result = File.ReadAllBytes(pathForFile);
+ using (var fstream = new FileStream(destination, FileMode.OpenOrCreate))
+ {
+ var data = await _client.Get(pathForFile, fstream, _cancellationToken);
+ Assert.AreEqual(result.Length, data);
+ }
+
+ var result2 = File.ReadAllBytes(destination);
+ Assert.AreEqual(result, result2);
+ File.Delete(destination);
+ }
+
+ [Test]
+ public void ParallelClientConnection()
+ {
+ var clients = new Task[2];
+ for (var i = 0; i < 2; i++)
+ {
+ clients[i] = Task.Run(() =>
+ {
+ var localClient = new Client("127.0.0.1", 80);
+ var answer = localClient.List("../../../TestData", _cancellationToken).Result;
+ Assert.AreEqual("../../../TestData\\Data.txt", answer[0].name);
+ });
+ }
+
+ foreach (var request in clients)
+ {
+ request.Wait();
+ }
+ }
+}
\ No newline at end of file
diff --git a/MyFTP/MyFTP.Test/TestData/Data.txt b/MyFTP/MyFTP.Test/TestData/Data.txt
new file mode 100644
index 0000000..dc36f61
--- /dev/null
+++ b/MyFTP/MyFTP.Test/TestData/Data.txt
@@ -0,0 +1 @@
+qwerty
\ No newline at end of file
diff --git a/MyFTP/MyFTP.sln b/MyFTP/MyFTP.sln
new file mode 100644
index 0000000..794b62f
--- /dev/null
+++ b/MyFTP/MyFTP.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyFTPClient", "MyFTPClient\MyFTPClient.csproj", "{97AFB503-9655-404C-9CE3-DB6CF51BD205}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyFTP.Test", "MyFTP.Test\MyFTP.Test.csproj", "{D4A4CBB3-3517-431E-8B0D-A71D6389E3B8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyFTPServer", "MyFTPServer\MyFTPServer.csproj", "{D6B47A97-BA0C-4B6C-9A22-3A3BB8AF6F1E}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {97AFB503-9655-404C-9CE3-DB6CF51BD205}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {97AFB503-9655-404C-9CE3-DB6CF51BD205}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {97AFB503-9655-404C-9CE3-DB6CF51BD205}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {97AFB503-9655-404C-9CE3-DB6CF51BD205}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D4A4CBB3-3517-431E-8B0D-A71D6389E3B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D4A4CBB3-3517-431E-8B0D-A71D6389E3B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D4A4CBB3-3517-431E-8B0D-A71D6389E3B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D4A4CBB3-3517-431E-8B0D-A71D6389E3B8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D6B47A97-BA0C-4B6C-9A22-3A3BB8AF6F1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D6B47A97-BA0C-4B6C-9A22-3A3BB8AF6F1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D6B47A97-BA0C-4B6C-9A22-3A3BB8AF6F1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D6B47A97-BA0C-4B6C-9A22-3A3BB8AF6F1E}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/MyFTP/MyFTPClient/Client.cs b/MyFTP/MyFTPClient/Client.cs
new file mode 100644
index 0000000..3df7009
--- /dev/null
+++ b/MyFTP/MyFTPClient/Client.cs
@@ -0,0 +1,80 @@
+namespace MyFTPClient;
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net.Sockets;
+using System.Threading;
+using System.Threading.Tasks;
+
+///
+/// класс клиента для общения с сервером
+///
+public class Client
+{
+ private readonly int _port;
+ private readonly string _host;
+
+ public Client(string host, int port )
+ {
+ _host = host;
+ _port = port;
+ }
+
+ ///
+ /// запрос на листинг файлов в папке по пути
+ ///
+ public async Task<(string name, bool isDir)[]> List(string path, CancellationToken cancellationToken)
+ {
+ using var client = new TcpClient();
+ await client.ConnectAsync(_host, _port, cancellationToken);
+ await using var stream = client.GetStream();
+ await using var writer = new StreamWriter(stream);
+ using var reader = new StreamReader(stream);
+ await writer.WriteLineAsync($"1 {path}");
+ await writer.FlushAsync();
+ var info = await reader.ReadLineAsync();
+ var infoArray = info.Split(' ');
+ var size = Convert.ToInt32(infoArray[0]);
+ if (size == -1)
+ {
+ throw new FileNotFoundException();
+ }
+
+ var data = new List<(string, bool)>();
+
+
+
+ for (int i = 1; i < infoArray.Length; i += 2)
+ {
+
+ var isDir = Convert.ToBoolean(infoArray[i + 1]);
+ data.Add((infoArray[i], isDir));
+ }
+
+ return data.ToArray();
+ }
+
+ ///
+ /// запрос на скачивание нужного файла
+ ///
+ public async Task Get(string path, Stream fileStream, CancellationToken cancellationToken)
+ {
+ using var client = new TcpClient();
+ await client.ConnectAsync(_host, _port, cancellationToken);
+ await using var stream = client.GetStream();
+ await using var writer = new StreamWriter(stream);
+ using var reader = new StreamReader(stream);
+ await writer.WriteLineAsync($"2 {path}");
+ await writer.FlushAsync();
+ var size = Convert.ToInt32(await reader.ReadLineAsync());
+ if (size == -1)
+ {
+ throw new FileNotFoundException();
+ }
+
+ await stream.CopyToAsync(fileStream, cancellationToken);
+
+ return size;
+ }
+}
\ No newline at end of file
diff --git a/MyFTP/MyFTPClient/MyFTPClient.csproj b/MyFTP/MyFTPClient/MyFTPClient.csproj
new file mode 100644
index 0000000..7b96d01
--- /dev/null
+++ b/MyFTP/MyFTPClient/MyFTPClient.csproj
@@ -0,0 +1,8 @@
+
+
+
+ Exe
+ net6.0
+
+
+
diff --git a/MyFTP/MyFTPClient/Program.cs b/MyFTP/MyFTPClient/Program.cs
new file mode 100644
index 0000000..0c57810
--- /dev/null
+++ b/MyFTP/MyFTPClient/Program.cs
@@ -0,0 +1,58 @@
+namespace MyFTPClient;
+
+using System.Threading;
+using System;
+using System.IO;
+using System.Threading.Tasks;
+
+public class Program
+{
+ public static async Task Main(string[] args)
+ {
+ var client = new Client(args[0], Convert.ToInt32(args[1]));
+ var token = new CancellationToken();
+ Console.WriteLine("1 - List — листинг файлов в директории на сервере\nНапример, 1 ./Test/Files\n");
+ Console.WriteLine("2 - Get — скачивание файла с сервера\n2 ./Test/Files/file1.txt\n");
+ Console.WriteLine("Введите !exit, чтобы остановить сервер\n");
+ Console.WriteLine("Введите команду:");
+ var request = Console.ReadLine().Split(' ');
+ while (request[0] != "!exit" || !token.IsCancellationRequested)
+ {
+ if (request[0] == "1" && request.Length == 2)
+ {
+ try
+ {
+ var response = await client.List(request[1], token);
+ Console.WriteLine(response.Length);
+ foreach (var file in response)
+ {
+ Console.WriteLine($"{file.Item1} {file.Item2}");
+ }
+ }
+ catch (FileNotFoundException)
+ {
+ Console.WriteLine("-1");
+ }
+ }
+ else if (request[0] == "2" && request.Length == 2)
+ {
+ using var fstream = new FileStream(request[2], FileMode.OpenOrCreate);
+ try
+ {
+ var response = await client.Get(request[1], fstream, token);
+ Console.WriteLine(response);
+ }
+ catch (FileNotFoundException)
+ {
+ Console.WriteLine("-1");
+ }
+ }
+ else
+ {
+ Console.WriteLine("Некорректная команда!");
+ }
+ Console.WriteLine("\nВведите команду:");
+ request = Console.ReadLine().Split(' ');
+ }
+ }
+}
\ No newline at end of file
diff --git a/MyFTP/MyFTPServer/MyFTPServer.csproj b/MyFTP/MyFTPServer/MyFTPServer.csproj
new file mode 100644
index 0000000..7b96d01
--- /dev/null
+++ b/MyFTP/MyFTPServer/MyFTPServer.csproj
@@ -0,0 +1,8 @@
+
+
+
+ Exe
+ net6.0
+
+
+
diff --git a/MyFTP/MyFTPServer/Program.cs b/MyFTP/MyFTPServer/Program.cs
new file mode 100644
index 0000000..82349c7
--- /dev/null
+++ b/MyFTP/MyFTPServer/Program.cs
@@ -0,0 +1,33 @@
+namespace MyFTPServer;
+
+using System;
+using System.Threading.Tasks;
+
+public class Program
+{
+ public static async Task Main(string[] args)
+ {
+ if (args.Length != 2)
+ {
+ Console.WriteLine("Вы не передали параметры или передали неверные.");
+ return;
+ }
+ try
+ {
+ var server = new Server(args[0], Convert.ToInt32(args[1]));
+ var serverStop = server.StartServer();
+ Console.WriteLine("Введите !exit, чтобы остановить сервер");
+ var command = "";
+ while (command != "!exit")
+ {
+ command = Console.ReadLine();
+ }
+ server.StopServer();
+ await serverStop;
+ }
+ catch (ArgumentException)
+ {
+ Console.WriteLine("Ошибка!");
+ }
+ }
+}
diff --git a/MyFTP/MyFTPServer/Server.cs b/MyFTP/MyFTPServer/Server.cs
new file mode 100644
index 0000000..8a70355
--- /dev/null
+++ b/MyFTP/MyFTPServer/Server.cs
@@ -0,0 +1,117 @@
+namespace MyFTPServer;
+
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+///
+/// класс сервера для приема запросов от клиента
+///
+public class Server
+{
+ private readonly TcpListener _listener;
+ private readonly CancellationTokenSource _tokenSource = new ();
+
+ public Server(string host, int port)
+ {
+ _listener = new TcpListener(IPAddress.Parse(host), port);
+ }
+
+ ///
+ /// старт приема запросов
+ ///
+ public async Task StartServer()
+ {
+ var task = new List();
+ _listener.Start();
+ while (!_tokenSource.IsCancellationRequested)
+ {
+ var client = await _listener.AcceptTcpClientAsync();
+ task.Add(Working(client));
+ }
+ await Task.WhenAll(task);
+ _listener.Stop();
+ }
+
+ ///
+ /// остановка приема запросов
+ ///
+ public void StopServer()
+ => _tokenSource.Cancel();
+
+ ///
+ /// метод для распределения запросов
+ ///
+ private async Task Working(TcpClient client)
+ {
+ using (client)
+ {
+ using var stream = client.GetStream();
+ using var reader = new StreamReader(stream);
+ using var writer = new StreamWriter(stream);
+ var request = await reader.ReadLineAsync();
+ var (command, path) = (request?.Split()[0], request?.Split()[1]);
+ switch (command)
+ {
+ case "1":
+ await List(writer, path);
+ break;
+ case "2":
+ await Get(writer, path, stream);
+ break;
+ case "!exit":
+ StopServer();
+ break;
+ default:
+ await writer.WriteAsync("Ваш протокол сломан!");
+ break;
+ }
+ }
+ }
+
+ private async Task List(StreamWriter writer, string path)
+ {
+ if (!Directory.Exists(path))
+ {
+ await writer.WriteLineAsync("-1");
+ return;
+ }
+
+ var files = Directory.GetFiles(path);
+ var directories = Directory.GetDirectories(path);
+ var size = files.Length + directories.Length;
+ var result = new StringBuilder();
+ result.Append(size).ToString();
+ foreach (var file in files)
+ {
+ result.Append($" {file} false");
+ }
+
+ foreach (var dir in directories)
+ {
+ result.Append($" {dir} true");
+ }
+
+ await writer.WriteLineAsync(size.ToString() + result);
+ await writer.FlushAsync();
+ }
+
+ private async Task Get(StreamWriter writer, string path, NetworkStream stream)
+ {
+ if (!File.Exists(path))
+ {
+ await writer.WriteLineAsync("-1");
+ return;
+ }
+
+ var file = new FileStream(path, FileMode.Open);
+ await writer.WriteLineAsync($"{file.Length} ");
+ await writer.FlushAsync();
+ await file.CopyToAsync(stream);
+ await writer.FlushAsync();
+ }
+}
\ No newline at end of file
diff --git a/MyFTP/global.json b/MyFTP/global.json
new file mode 100644
index 0000000..c14434a
--- /dev/null
+++ b/MyFTP/global.json
@@ -0,0 +1,7 @@
+{
+ "sdk": {
+ "version": "6.0",
+ "rollForward": "minor",
+ "allowPrerelease": false
+ }
+}
\ No newline at end of file
diff --git a/appveyor.yml b/appveyor.yml
index 9809100..d79f319 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,3 +1,6 @@
+image: Visual Studio 2022
+
+
build_script:
- For /R %%I in (*.sln) do dotnet test %%I
test: of
\ No newline at end of file