-
Notifications
You must be signed in to change notification settings - Fork 0
Решение 2 контрольной #8
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: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| namespace Test2.Tests; | ||
|
|
||
| using System.Net; | ||
| using System.Net.Sockets; | ||
|
|
||
| public class Tests | ||
| { | ||
| private int port = 12346; | ||
| private Server? server = null; | ||
| private Client? client = null; | ||
|
|
||
| [Test] | ||
| public async Task ServerInterruptionTest() | ||
| { | ||
| server = new(port); | ||
| async void actionStartServer() => await server.Start(); | ||
| var actionStartFakeClient = (Action)(async () => | ||
| { | ||
| Thread.Sleep(1000); | ||
| var client = new TcpClient(); | ||
| await client.ConnectAsync("localhost", port); | ||
| var stream = client.GetStream(); | ||
| var writer = new StreamWriter(stream) { AutoFlush = true }; | ||
| var reader = new StreamReader(stream); | ||
|
|
||
| await writer.WriteLineAsync("exit"); | ||
| }); | ||
|
|
||
| await Task.WhenAll(Task.Run(actionStartServer), Task.Run(actionStartFakeClient)); | ||
|
|
||
| Assert.That(server.IsInterrupted, Is.EqualTo(true)); | ||
| } | ||
|
|
||
| [Test] | ||
| public async Task ClientInterruptionTest() | ||
| { | ||
| client = new(Dns.GetHostAddresses("localhost")[1], port); | ||
| async void actionStartClient() => await client.Start(); | ||
| var actionStartFakeServer = (Action)(async () => | ||
| { | ||
| var listener = new TcpListener(IPAddress.Any, port); | ||
| listener.Start(); | ||
| var tcpClient = await listener.AcceptTcpClientAsync(); | ||
| var stream = tcpClient.GetStream(); | ||
| var writer = new StreamWriter(stream) { AutoFlush = true }; | ||
|
|
||
| await writer.WriteLineAsync("exit"); | ||
| listener.Stop(); | ||
| }); | ||
|
|
||
| await Task.WhenAll(Task.Run(actionStartFakeServer), Task.Run(actionStartClient)); | ||
| Assert.That(client.IsInterrupted, Is.EqualTo(true)); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net7.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
|
|
||
| <IsPackable>false</IsPackable> | ||
| <IsTestProject>true</IsTestProject> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" /> | ||
| <PackageReference Include="NUnit" Version="3.13.3" /> | ||
| <PackageReference Include="NUnit3TestAdapter" Version="4.4.2" /> | ||
| <PackageReference Include="NUnit.Analyzers" Version="3.6.1" /> | ||
| <PackageReference Include="coverlet.collector" Version="3.2.0" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\Test2\Test2.csproj" /> | ||
| </ItemGroup> | ||
| </Project> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| global using NUnit.Framework; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| namespace Test2; | ||
|
|
||
| using System.Net; | ||
| using System.Net.Sockets; | ||
|
|
||
| public class Client | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Надо комментарии |
||
| { | ||
| public IPAddress Ip { get; private set; } | ||
| public int Port { get; private set; } | ||
| public bool IsInterrupted { get; private set; } = false; | ||
|
|
||
| /// <summary> | ||
| /// Creates new instance of server class. | ||
| /// </summary> | ||
| public Client(IPAddress ip, int port) | ||
| { | ||
| Ip = ip; | ||
| Port = port; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Starts client, that writes messages from concole, communicates witn server and stops when get "exit" from server or from comsole. | ||
| /// </summary> | ||
| /// <returns></returns> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Пустые тэги не нужны |
||
| public async Task Start() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. По соглашению, принятому в .NET, все async-методы должны иметь суффикс "Async" |
||
| { | ||
| var lockObject = new object(); | ||
| var client = new TcpClient(); | ||
| await client.ConnectAsync(Ip, Port); | ||
| Console.WriteLine("Connected"); | ||
| var stream = client.GetStream(); | ||
| var writer = new StreamWriter(stream) { AutoFlush = true }; | ||
| var reader = new StreamReader(stream); | ||
|
Comment on lines
+31
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Кто-то из них должен быть объявлен с using, чтобы корректно закрыть поток |
||
|
|
||
| async void actionWriteEntered() => await WriteEntered(writer, lockObject); | ||
| async void actionReadPrintResponse() => await ReadPrintResponse(reader, lockObject); | ||
|
Comment on lines
+35
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Нет нужды именованные функции объявлять. Так можно, но можно просто лямбды. |
||
|
|
||
| await Task.WhenAny(Task.Run(actionWriteEntered), Task.Run(actionReadPrintResponse)); | ||
| } | ||
|
|
||
| private async Task WriteEntered(StreamWriter writer, object lockObject) | ||
| { | ||
| while (!IsInterrupted) | ||
| { | ||
| string? message = Console.ReadLine(); | ||
| if (message != null) | ||
| { | ||
| if (message == "exit") | ||
| { | ||
| IsInterrupted = true; | ||
| } | ||
| await writer.WriteLineAsync(message); | ||
| lock (lockObject) | ||
| { | ||
| Console.WriteLine(message); | ||
| } | ||
|
Comment on lines
+53
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Зачем, мы ведь его только что ввели. Вообще, Console.WriteLine внутри синхронизируется, lockObject не нужен |
||
| } | ||
| } | ||
| Console.WriteLine("Connection interrupted"); | ||
| } | ||
|
|
||
| private async Task ReadPrintResponse(StreamReader reader, object lockObject) | ||
| { | ||
| while (!IsInterrupted) | ||
| { | ||
| var response = await reader.ReadLineAsync(); | ||
| lock (lockObject) | ||
| { | ||
| Console.WriteLine(response); | ||
| } | ||
| if (response == "exit") | ||
| { | ||
| IsInterrupted = true; | ||
| lock (lockObject) | ||
| { | ||
| Console.WriteLine("Connection interrupted"); | ||
| } | ||
| return; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
Comment on lines
+83
to
+86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Много лишних пустых строчек |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| using Test2; | ||
|
|
||
| using System.Net; | ||
|
|
||
| if (args.Length == 0 || args.Length > 2) | ||
| { | ||
| Console.WriteLine("Expected port (number from 1025 to 65535) if you want to run the application as a server."); | ||
| Console.WriteLine("Expected IP and port (number from 1025 to 65535) if you want to run the application as a client."); | ||
| return; | ||
| } | ||
|
|
||
| if (args.Length == 1) | ||
| { | ||
| bool isNumber = int.TryParse(args[0], out int port); | ||
| if (isNumber && port > 1024 && port < 65536) | ||
| { | ||
| var server = new Server(port); | ||
| await server.Start(); | ||
| } | ||
| throw new InvalidDataException("Expected port (number from 1025 to 65535)"); | ||
| } | ||
| else | ||
| { | ||
| bool isNumber = int.TryParse(args[1], out int port); | ||
| bool isIP = IPAddress.TryParse(args[0], out IPAddress? ip); | ||
| if (isNumber && port > 1024 && port < 65536 && isIP && ip != null) | ||
| { | ||
| var client = new Client(ip, port); | ||
| await client.Start(); | ||
| } | ||
| throw new InvalidDataException("Expected IP and port (number from 1025 to 65535)"); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| namespace Test2; | ||
|
|
||
| using System.Net; | ||
| using System.Net.Sockets; | ||
|
|
||
| /// <summary> | ||
| /// Server class | ||
| /// </summary> | ||
| public class Server | ||
| { | ||
| private TcpListener listener; | ||
| public int Port { get; private set; } | ||
| public bool IsInterrupted { get; private set; } | ||
|
|
||
| /// <summary> | ||
| /// Creates new instance of Server class | ||
| /// </summary> | ||
| public Server(int port) | ||
| { | ||
| listener = new TcpListener(IPAddress.Any, port); | ||
| Port = port; | ||
| IsInterrupted = false; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Starts server, that writes messages from concole, communicates with client and stops when get "exit" from client or from comsole. | ||
| /// </summary> | ||
| /// <returns></returns> | ||
| public async Task Start() | ||
| { | ||
| object lockObject = new(); | ||
| listener.Start(); | ||
| var tcpClient = await listener.AcceptTcpClientAsync(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Вызывать длительную операцию без способа её отменить может быть плохой идеей, потому что можно задедлочиться. Тут, например, если к нам так и не подключится никакой клиент, AcceptTcpClientAsync никогда не вернёт управление и остановить сервер будет нельзя. Чтобы так не было, у AcceptTcpClientAsync есть перегрузка, принимающая CancellationToken. |
||
| Console.WriteLine("Connected"); | ||
| var stream = tcpClient.GetStream(); | ||
| var reader = new StreamReader(stream); | ||
| var writer = new StreamWriter(stream) { AutoFlush = true }; | ||
|
|
||
| async void actionWriteEntered() => await WriteEntered(writer, lockObject); | ||
| async void actionReadPrintResponse() => await ReadPrintResponse(reader, lockObject); | ||
|
|
||
| await Task.WhenAny(Task.Run(actionWriteEntered), Task.Run(actionReadPrintResponse)); | ||
| listener.Stop(); | ||
| } | ||
|
|
||
| private async Task WriteEntered(StreamWriter writer, object lockObject) | ||
| { | ||
| while (!IsInterrupted) | ||
| { | ||
| string? message = Console.ReadLine(); | ||
|
|
||
| if (message != null) | ||
| { | ||
| if (message == "exit") | ||
| { | ||
| IsInterrupted = true; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Тут тоже, если IsInterrupted true, то ReadPrintResponse не закончит свою работу, пока клиент нам что-нибудь не пришлёт. Потенциальный дедлок. |
||
| } | ||
|
|
||
| await writer.WriteLineAsync(message); | ||
|
|
||
| lock (lockObject) | ||
| { | ||
| Console.WriteLine(message); | ||
| } | ||
| } | ||
| } | ||
| Console.WriteLine("Connection interrupted"); | ||
| } | ||
|
|
||
| private async Task ReadPrintResponse(StreamReader reader, object lockObject) | ||
| { | ||
| while (!IsInterrupted) | ||
| { | ||
| var response = await reader.ReadLineAsync(); | ||
| lock(lockObject) | ||
| { | ||
| Console.WriteLine(response); | ||
| } | ||
| if (response == "exit") | ||
| { | ||
| IsInterrupted = true; | ||
| lock (lockObject) | ||
| { | ||
| Console.WriteLine("Connection interrupted"); | ||
| } | ||
| return; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net7.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| </PropertyGroup> | ||
|
|
||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| | ||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||
| # Visual Studio Version 16 | ||
| VisualStudioVersion = 25.0.1706.10 | ||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test2", "Test2.csproj", "{A9821BAB-9ABD-4EB7-AD0D-EFDEAA6F5766}" | ||
| EndProject | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test2.Tests", "..\Test2.Tests\Test2.Tests.csproj", "{6FCF5F0F-F709-43C2-808A-127718A579AF}" | ||
| EndProject | ||
| Global | ||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
| Debug|Any CPU = Debug|Any CPU | ||
| Release|Any CPU = Release|Any CPU | ||
| EndGlobalSection | ||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
| {A9821BAB-9ABD-4EB7-AD0D-EFDEAA6F5766}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {A9821BAB-9ABD-4EB7-AD0D-EFDEAA6F5766}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {A9821BAB-9ABD-4EB7-AD0D-EFDEAA6F5766}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {A9821BAB-9ABD-4EB7-AD0D-EFDEAA6F5766}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| {6FCF5F0F-F709-43C2-808A-127718A579AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {6FCF5F0F-F709-43C2-808A-127718A579AF}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {6FCF5F0F-F709-43C2-808A-127718A579AF}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {6FCF5F0F-F709-43C2-808A-127718A579AF}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| EndGlobalSection | ||
| GlobalSection(SolutionProperties) = preSolution | ||
| HideSolutionNode = FALSE | ||
| EndGlobalSection | ||
| GlobalSection(ExtensibilityGlobals) = postSolution | ||
| SolutionGuid = {48782106-A8DE-406E-B748-8A2FC02BBF9A} | ||
| EndGlobalSection | ||
| EndGlobal |
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 7 умер. .NET 9 Preview 2 даже давно доступен. Переходите на .NET 8