Conversation
yurii-litvinov
left a comment
There was a problem hiding this comment.
Что-то проектные файлы вообще не выложены :) И ConsoleApp1 как имя проекта не годится. И тестов нет.
MyFTP/MyFTP/Program.cs
Outdated
| static void Main(string[] args) | ||
| { | ||
|
|
||
| } |
There was a problem hiding this comment.
Не, я хочу две консольные программы --- для сервера и для клиента. Чтобы я мог запустить сервер, и затем запустить клиент и подконнектиться к серверу.
MyFTP/ConsoleApp1/Client.cs
Outdated
|
|
||
| namespace ConsoleApp1 | ||
| { | ||
| public class Client |
MyFTP/ConsoleApp1/Client.cs
Outdated
|
|
||
| public async Task<(string name, bool isDir)[]> List(string path) | ||
| { | ||
| using var client = new TcpClient(_host, _port); |
There was a problem hiding this comment.
Не-а, это заставит TcpClient синхронно подключаться к хосту, так что если у него не выйдет, процесс затянется до наступления таймаута, а всё это время поток будет висеть. Надо ConnectAsync
MyFTP/ConsoleApp1/Client.cs
Outdated
|
|
||
| for (int i = 0; i < size; i++) | ||
| { | ||
| var name = await reader.ReadLineAsync(); |
There was a problem hiding this comment.
По протоколу разделители --- пробелы, поэтому сервер вообще не может папки с пробелами правильно обрабатывать. Но протокол есть протокол, от него нельзя отступать, потому что Вы решили, что так правильнее.
MyFTP/ConsoleApp1/Client.cs
Outdated
| throw new FileNotFoundException(); | ||
| } | ||
|
|
||
| var content = new byte[size]; |
There was a problem hiding this comment.
Не надо грузить файл в память целиком, файлы могут быть очень большие. stream.CopyToAsync вполне ок.
MyFTP/MyFTP/Client.cs
Outdated
| /// <summary> | ||
| /// запрос на листинг файлов в папке по пути | ||
| /// </summary> | ||
| public async Task<(string name, bool isDir)[]> List(string path) |
There was a problem hiding this comment.
Кстати, все длительные операции должны быть прерываемы, так что и List, и Get должны принимать CancellationToken
MyFTP/MyFTP/Server.cs
Outdated
| while (!_cancellationToken.IsCancellationRequested) | ||
| { | ||
| var client = await _listener.AcceptTcpClientAsync(); | ||
| Working(client); |
There was a problem hiding this comment.
Многопоточность не поддержана, пока обслуживают одного клиента, второй не может подключиться. А надо
MyFTP/MyFTP/Server.cs
Outdated
| var stream = client.GetStream(); | ||
| var reader = new StreamReader(stream); | ||
| var writer = new StreamWriter(stream); |
There was a problem hiding this comment.
Они все IDisposable, так что должны быть using. client тоже, кстати
MyFTP/MyFTP/Server.cs
Outdated
| await Get(writer, path, stream); | ||
| break; | ||
| default: | ||
| throw new ArgumentException(); |
There was a problem hiding this comment.
Так сервер упадёт, а клиент об этом ничего не узнает. Надо исключение бросать не сюда, а сообщать клиенту по сети, что ошибка протокола.
MyFTP/MyFTP/Server.cs
Outdated
| { | ||
| await writer.WriteLineAsync("-1"); | ||
| await writer.FlushAsync(); | ||
| } |
There was a problem hiding this comment.
И дальше продолжили работать как ни в чём не бывало :)
MyFTP/MyFTPClient/MyFTPClient.csproj
Outdated
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net5.0</TargetFramework> |
There was a problem hiding this comment.
Можно уже на .net 6 и C# 10 переходить, там всякие крутые штуки типа file-scoped namespaces и глобальных юзингов.
MyFTP/MyFTPServer/Dockerfile
Outdated
| @@ -0,0 +1,18 @@ | |||
| FROM mcr.microsoft.com/dotnet/runtime:5.0 AS base | |||
MyFTP/MyFTP.Test/MyFTPTest.cs
Outdated
| _server = new Server("127.0.0.1", 80); | ||
| _client = new Client(80, "127.0.0.1"); |
There was a problem hiding this comment.
Как-то оно хаотично, у одного сначала IP, потом порт, у второго наоборот
MyFTP/MyFTP.Test/MyFTPTest.cs
Outdated
| public void GetInvalidFileNameTest() | ||
| { | ||
| Assert.Throws<AggregateException>(() => _client.Get("Text.txt", _fileStream, _cancellationToken).Wait()); | ||
| _server.StopServer(); |
There was a problem hiding this comment.
Это стоит в TearDown написать, а то если вдруг тест не пройдёт и минует StopServer, остальные тесты, скорее всего, даже не запустятся, потому что порт занят.
MyFTP/MyFTP.Test/MyFTPTest.cs
Outdated
| [Test] | ||
| public async Task ListInvalidFileNameTest() | ||
| { | ||
| Assert.Throws<AggregateException>(() => _client.Get("Text.txt", _fileStream, _cancellationToken).Wait()); |
There was a problem hiding this comment.
Бывает Assert.ThrowsAsync
MyFTP/MyFTPServer/Program.cs
Outdated
| Console.WriteLine("Введите IP сервер:"); | ||
| var ipAddress = Console.ReadLine(); | ||
| Console.WriteLine("Введите порт сервера:"); | ||
| var port = int.Parse(Console.ReadLine()); |
There was a problem hiding this comment.
Тут тем более. Сервер в принципе не предполагает какой-то интерактивности, поэтому только аргументы командной строки, никаких ReadLine-ов :)
MyFTP/MyFTPServer/Program.cs
Outdated
| command = Console.ReadLine(); | ||
| } | ||
| } | ||
| catch (Exception e) |
There was a problem hiding this comment.
| catch (Exception e) | |
| catch (Exception) |
MyFTP/MyFTPServer/Server.cs
Outdated
| public class Server | ||
| { | ||
| private readonly TcpListener _listener; | ||
| private readonly CancellationTokenSource _cancellationToken = new (); |
There was a problem hiding this comment.
Так это Source или токен? Мне кажется, Вы путаете фабрику и продукт. См. https://stackoverflow.com/questions/14215784/why-cancellationtoken-is-separate-from-cancellationtokensource
MyFTP/MyFTPServer/Server.cs
Outdated
| while (!_cancellationToken.IsCancellationRequested) | ||
| { | ||
| var client = await _listener.AcceptTcpClientAsync(); | ||
| Working(client); |
There was a problem hiding this comment.
Working нигде не await-ится, что огорчает компилятор и даёт возможность остановить сервер до того, как он успел ответить клиенту, что может привести к порче данных. Тут можно было сложить таск, который вернёт Working, в список, и потом в конце заawait-ить их все.
MyFTP/MyFTPServer/Server.cs
Outdated
| foreach (var dir in directories) | ||
| { | ||
| result += $" {dir} true"; | ||
| } |
There was a problem hiding this comment.
Конкатенировать строки в цикле правильнее StringBuilder-ом. Без него это одна большая утечка памяти, квадратичная от размера строки.
| using System.Threading.Tasks; | ||
| using NUnit.Framework; | ||
|
|
||
| public class Tests |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
MyFTP/MyFTP.Test/MyFTPTest.cs
Outdated
| [Test] | ||
| public void GetInvalidFileNameTest() | ||
| { | ||
| Assert.ThrowsAsync<AggregateException>(() => Task.Run(() => _client.Get("Text.txt", _fileStream, _cancellationToken).Wait())); |
There was a problem hiding this comment.
Task.Run(...).Wait() --- это практически то же самое, что просто ..., эта конструкция довольно бессмысленна. На что намекает в частности то, что тут лямбда не асинхронная.
| Assert.AreEqual(result, result2); | ||
| File.Delete(destination); | ||
| } | ||
| } No newline at end of file |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
| /// <summary> | ||
| /// класс клиента для общения с сервером | ||
| /// </summary> | ||
| public class Client |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
MyFTP/MyFTPClient/Client.cs
Outdated
| var size = Convert.ToInt32(infoArray[0]); | ||
| if (size == -1) | ||
| { | ||
| throw new Exception(); |
There was a problem hiding this comment.
Ой, нет, кидать Exception запрещено :) Это слишком общее исключение
MyFTP/MyFTPClient/Client.cs
Outdated
| } | ||
| if (size == -1) | ||
| { | ||
| throw new ArgumentException(); |
There was a problem hiding this comment.
Это тоже. ArgumentException --- это когда нам неверные параметры передали
MyFTP/MyFTPClient/Program.cs
Outdated
| global using System.IO; | ||
|
|
||
| var client = new Client(args[0], Convert.ToInt32(args[1])); | ||
| var request = Console.ReadLine().Split(' '); |
There was a problem hiding this comment.
Передавать аргументы в консольное приложение не через args, а с помощью ReadLine --- нельзя, замучаетесь скрипты писать
MyFTP/MyFTPClient/Program.cs
Outdated
|
|
||
| if (request[0] == "2") | ||
| { | ||
| using (var fstream = new FileStream(request[2], FileMode.OpenOrCreate)) |
There was a problem hiding this comment.
Тут лучше просто using var, отдельный scope тут не нужен
MyFTP/MyFTPClient/Program.cs
Outdated
| Console.WriteLine($"{file.Item1} {file.Item2}"); | ||
| } | ||
| } | ||
| catch (Exception) |
There was a problem hiding this comment.
Ловить просто Exception тоже нельзя, кстати
MyFTP/MyFTPServer/Program.cs
Outdated
| try | ||
| { | ||
| var server = new Server(args[0], Convert.ToInt32(args[1])); | ||
| await server.StartServer(); |
There was a problem hiding this comment.
Есть ли какой-нибудь способ его остановить? :)
| using System.Threading.Tasks; | ||
| using NUnit.Framework; | ||
|
|
||
| public class Tests |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
MyFTP/MyFTP.Test/MyFTPTest.cs
Outdated
| _client = new Client("127.0.0.1", 80); | ||
| _fileStream = new MemoryStream(); | ||
| _cancellationToken = new (); | ||
| _= _server.StartServer(); |
There was a problem hiding this comment.
Зачем присваивание в никуда? :) И пробела не хватает
MyFTP/MyFTPClient/Program.cs
Outdated
| { | ||
| var client = new Client(args[0], Convert.ToInt32(args[1])); | ||
| var token = new CancellationToken(); | ||
| while (args[0] != "exit" || !token.IsCancellationRequested) |
There was a problem hiding this comment.
args[0] != "exit" выглядит довольно странно. Будто args[0] как-то может поменяться :)
MyFTP/MyFTPClient/Program.cs
Outdated
|
|
||
| if (args[0] == "2") | ||
| { | ||
| using (var fstream = new FileStream(args[2], FileMode.OpenOrCreate)) |
There was a problem hiding this comment.
Можно using var без скобок
| { | ||
| var server = new Server(args[0], Convert.ToInt32(args[1])); | ||
| await server.StartServer(); | ||
| Console.WriteLine("Введите !exit, чтобы остановить сервер"); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
| { | ||
| try | ||
| { | ||
| var server = new Server(args[0], Convert.ToInt32(args[1])); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
| while (command != "!exit") | ||
| { | ||
| command = Console.ReadLine(); | ||
| } |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
MyFTP/MyFTPServer/Server.cs
Outdated
| /// </summary> | ||
| private async Task Working(TcpClient client) | ||
| { | ||
| using var stream = client.GetStream(); |
There was a problem hiding this comment.
client реализует IDisposable, так что надо сначала using на него сделать
yurii-litvinov
left a comment
There was a problem hiding this comment.
Да, вот теперь всё правильно, хоть и неаккуратно. Зачтена.
| [Test] | ||
| public void ParallelClientConnection() | ||
| { | ||
| var clients = new Task[2]; |
There was a problem hiding this comment.
Ну, два клиента — это не очень убедительно, было бы их хотя бы 200... Но ладно.
| var data = new List<(string, bool)>(); | ||
|
|
||
|
|
||
|
|
||
| for (int i = 1; i < infoArray.Length; i += 2) |
There was a problem hiding this comment.
| var data = new List<(string, bool)>(); | |
| for (int i = 1; i < infoArray.Length; i += 2) | |
| var data = new List<(string, bool)>(); | |
| for (int i = 1; i < infoArray.Length; i += 2) |
| { | ||
|
|
||
| var isDir = Convert.ToBoolean(infoArray[i + 1]); |
There was a problem hiding this comment.
| { | |
| var isDir = Convert.ToBoolean(infoArray[i + 1]); | |
| { | |
| var isDir = Convert.ToBoolean(infoArray[i + 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"); |
There was a problem hiding this comment.
Мы клиент, зачем останавливать сервер
No description provided.