Skip to content

Conversation

@MikePuzanov
Copy link
Owner

No description provided.

Copy link
Collaborator

@yurii-litvinov yurii-litvinov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Что-то проектные файлы вообще не выложены :) И ConsoleApp1 как имя проекта не годится. И тестов нет.

static void Main(string[] args)
{

}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не, я хочу две консольные программы --- для сервера и для клиента. Чтобы я мог запустить сервер, и затем запустить клиент и подконнектиться к серверу.


namespace ConsoleApp1
{
public class Client
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нужны комментарии


public async Task<(string name, bool isDir)[]> List(string path)
{
using var client = new TcpClient(_host, _port);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не-а, это заставит TcpClient синхронно подключаться к хосту, так что если у него не выйдет, процесс затянется до наступления таймаута, а всё это время поток будет висеть. Надо ConnectAsync


for (int i = 0; i < size; i++)
{
var name = await reader.ReadLineAsync();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

По протоколу разделители --- пробелы, поэтому сервер вообще не может папки с пробелами правильно обрабатывать. Но протокол есть протокол, от него нельзя отступать, потому что Вы решили, что так правильнее.

throw new FileNotFoundException();
}

var content = new byte[size];
Copy link
Collaborator

@yurii-litvinov yurii-litvinov Nov 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не надо грузить файл в память целиком, файлы могут быть очень большие. stream.CopyToAsync вполне ок.

/// <summary>
/// запрос на листинг файлов в папке по пути
/// </summary>
public async Task<(string name, bool isDir)[]> List(string path)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Кстати, все длительные операции должны быть прерываемы, так что и List, и Get должны принимать CancellationToken

while (!_cancellationToken.IsCancellationRequested)
{
var client = await _listener.AcceptTcpClientAsync();
Working(client);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Многопоточность не поддержана, пока обслуживают одного клиента, второй не может подключиться. А надо

Comment on lines 52 to 54
var stream = client.GetStream();
var reader = new StreamReader(stream);
var writer = new StreamWriter(stream);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Они все IDisposable, так что должны быть using. client тоже, кстати

await Get(writer, path, stream);
break;
default:
throw new ArgumentException();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Так сервер упадёт, а клиент об этом ничего не узнает. Надо исключение бросать не сюда, а сообщать клиенту по сети, что ошибка протокола.

{
await writer.WriteLineAsync("-1");
await writer.FlushAsync();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

И дальше продолжили работать как ни в чём не бывало :)


<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
Copy link
Collaborator

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 и глобальных юзингов.

@@ -0,0 +1,18 @@
FROM mcr.microsoft.com/dotnet/runtime:5.0 AS base
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines 21 to 22
_server = new Server("127.0.0.1", 80);
_client = new Client(80, "127.0.0.1");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Как-то оно хаотично, у одного сначала IP, потом порт, у второго наоборот

public void GetInvalidFileNameTest()
{
Assert.Throws<AggregateException>(() => _client.Get("Text.txt", _fileStream, _cancellationToken).Wait());
_server.StopServer();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это стоит в TearDown написать, а то если вдруг тест не пройдёт и минует StopServer, остальные тесты, скорее всего, даже не запустятся, потому что порт занят.

[Test]
public async Task ListInvalidFileNameTest()
{
Assert.Throws<AggregateException>(() => _client.Get("Text.txt", _fileStream, _cancellationToken).Wait());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Бывает Assert.ThrowsAsync

Console.WriteLine("Введите IP сервер:");
var ipAddress = Console.ReadLine();
Console.WriteLine("Введите порт сервера:");
var port = int.Parse(Console.ReadLine());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут тем более. Сервер в принципе не предполагает какой-то интерактивности, поэтому только аргументы командной строки, никаких ReadLine-ов :)

command = Console.ReadLine();
}
}
catch (Exception e)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
catch (Exception e)
catch (Exception)

public class Server
{
private readonly TcpListener _listener;
private readonly CancellationTokenSource _cancellationToken = new ();
Copy link
Collaborator

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

while (!_cancellationToken.IsCancellationRequested)
{
var client = await _listener.AcceptTcpClientAsync();
Working(client);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working нигде не await-ится, что огорчает компилятор и даёт возможность остановить сервер до того, как он успел ответить клиенту, что может привести к порче данных. Тут можно было сложить таск, который вернёт Working, в список, и потом в конце заawait-ить их все.

foreach (var dir in directories)
{
result += $" {dir} true";
}
Copy link
Collaborator

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.

[Test]
public void GetInvalidFileNameTest()
{
Assert.ThrowsAsync<AggregateException>(() => Task.Run(() => _client.Get("Text.txt", _fileStream, _cancellationToken).Wait()));
Copy link
Collaborator

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.

/// <summary>
/// класс клиента для общения с сервером
/// </summary>
public class Client

This comment was marked as resolved.

var size = Convert.ToInt32(infoArray[0]);
if (size == -1)
{
throw new Exception();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ой, нет, кидать Exception запрещено :) Это слишком общее исключение

}
if (size == -1)
{
throw new ArgumentException();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это тоже. ArgumentException --- это когда нам неверные параметры передали

global using System.IO;

var client = new Client(args[0], Convert.ToInt32(args[1]));
var request = Console.ReadLine().Split(' ');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Передавать аргументы в консольное приложение не через args, а с помощью ReadLine --- нельзя, замучаетесь скрипты писать


if (request[0] == "2")
{
using (var fstream = new FileStream(request[2], FileMode.OpenOrCreate))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут лучше просто using var, отдельный scope тут не нужен

Console.WriteLine($"{file.Item1} {file.Item2}");
}
}
catch (Exception)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ловить просто Exception тоже нельзя, кстати

try
{
var server = new Server(args[0], Convert.ToInt32(args[1]));
await server.StartServer();
Copy link
Collaborator

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.

_client = new Client("127.0.0.1", 80);
_fileStream = new MemoryStream();
_cancellationToken = new ();
_= _server.StartServer();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Зачем присваивание в никуда? :) И пробела не хватает

{
var client = new Client(args[0], Convert.ToInt32(args[1]));
var token = new CancellationToken();
while (args[0] != "exit" || !token.IsCancellationRequested)
Copy link
Collaborator

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] как-то может поменяться :)


if (args[0] == "2")
{
using (var fstream = new FileStream(args[2], FileMode.OpenOrCreate))
Copy link
Collaborator

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.

{
try
{
var server = new Server(args[0], Convert.ToInt32(args[1]));

This comment was marked as resolved.

while (command != "!exit")
{
command = Console.ReadLine();
}

This comment was marked as resolved.

/// </summary>
private async Task Working(TcpClient client)
{
using var stream = client.GetStream();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

client реализует IDisposable, так что надо сначала using на него сделать

Copy link
Collaborator

@yurii-litvinov yurii-litvinov left a 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];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ну, два клиента — это не очень убедительно, было бы их хотя бы 200... Но ладно.

Comment on lines +44 to +48
var data = new List<(string, bool)>();



for (int i = 1; i < infoArray.Length; i += 2)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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)

Comment on lines +49 to +51
{

var isDir = Convert.ToBoolean(infoArray[i + 1]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{
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");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мы клиент, зачем останавливать сервер

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants