-
Notifications
You must be signed in to change notification settings - Fork 0
MyFTP #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
MyFTP #4
Conversation
yurii-litvinov
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Что-то проектные файлы вообще не выложены :) И ConsoleApp1 как имя проекта не годится. И тестов нет.
MyFTP/MyFTP/Program.cs
Outdated
| static void Main(string[] args) | ||
| { | ||
|
|
||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Не, я хочу две консольные программы --- для сервера и для клиента. Чтобы я мог запустить сервер, и затем запустить клиент и подконнектиться к серверу.
MyFTP/ConsoleApp1/Client.cs
Outdated
|
|
||
| namespace ConsoleApp1 | ||
| { | ||
| public class Client |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Нужны комментарии
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Не-а, это заставит 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
По протоколу разделители --- пробелы, поэтому сервер вообще не может папки с пробелами правильно обрабатывать. Но протокол есть протокол, от него нельзя отступать, потому что Вы решили, что так правильнее.
MyFTP/ConsoleApp1/Client.cs
Outdated
| throw new FileNotFoundException(); | ||
| } | ||
|
|
||
| var content = new byte[size]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Не надо грузить файл в память целиком, файлы могут быть очень большие. 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Кстати, все длительные операции должны быть прерываемы, так что и 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Многопоточность не поддержана, пока обслуживают одного клиента, второй не может подключиться. А надо
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Они все 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Так сервер упадёт, а клиент об этом ничего не узнает. Надо исключение бросать не сюда, а сообщать клиенту по сети, что ошибка протокола.
MyFTP/MyFTP/Server.cs
Outdated
| { | ||
| await writer.WriteLineAsync("-1"); | ||
| await writer.FlushAsync(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
И дальше продолжили работать как ни в чём не бывало :)
MyFTP/MyFTPClient/MyFTPClient.csproj
Outdated
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net5.0</TargetFramework> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Можно уже на .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 | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Как-то оно хаотично, у одного сначала 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Это стоит в 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Бывает 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Тут тем более. Сервер в принципе не предполагает какой-то интерактивности, поэтому только аргументы командной строки, никаких ReadLine-ов :)
MyFTP/MyFTPServer/Program.cs
Outdated
| command = Console.ReadLine(); | ||
| } | ||
| } | ||
| catch (Exception e) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Так это 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Working нигде не await-ится, что огорчает компилятор и даёт возможность остановить сервер до того, как он успел ответить клиенту, что может привести к порче данных. Тут можно было сложить таск, который вернёт Working, в список, и потом в конце заawait-ить их все.
MyFTP/MyFTPServer/Server.cs
Outdated
| foreach (var dir in directories) | ||
| { | ||
| result += $" {dir} true"; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Конкатенировать строки в цикле правильнее 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ой, нет, кидать Exception запрещено :) Это слишком общее исключение
MyFTP/MyFTPClient/Client.cs
Outdated
| } | ||
| if (size == -1) | ||
| { | ||
| throw new ArgumentException(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Это тоже. 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Передавать аргументы в консольное приложение не через 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Тут лучше просто using var, отдельный scope тут не нужен
MyFTP/MyFTPClient/Program.cs
Outdated
| Console.WriteLine($"{file.Item1} {file.Item2}"); | ||
| } | ||
| } | ||
| catch (Exception) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ловить просто 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Есть ли какой-нибудь способ его остановить? :)
| 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Зачем присваивание в никуда? :) И пробела не хватает
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Можно 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
client реализует IDisposable, так что надо сначала using на него сделать
yurii-litvinov
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Да, вот теперь всё правильно, хоть и неаккуратно. Зачтена.
| [Test] | ||
| public void ParallelClientConnection() | ||
| { | ||
| var clients = new Task[2]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ну, два клиента — это не очень убедительно, было бы их хотя бы 200... Но ладно.
| var data = new List<(string, bool)>(); | ||
|
|
||
|
|
||
|
|
||
| for (int i = 1; i < infoArray.Length; i += 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| { | |
| 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Мы клиент, зачем останавливать сервер
No description provided.