diff --git a/Test2.Tests/ClientAndServerTests.cs b/Test2.Tests/ClientAndServerTests.cs
new file mode 100644
index 0000000..c9955e0
--- /dev/null
+++ b/Test2.Tests/ClientAndServerTests.cs
@@ -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));
+ }
+}
diff --git a/Test2.Tests/Test2.Tests.csproj b/Test2.Tests/Test2.Tests.csproj
new file mode 100644
index 0000000..b3b7b55
--- /dev/null
+++ b/Test2.Tests/Test2.Tests.csproj
@@ -0,0 +1,23 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Test2.Tests/Usings.cs b/Test2.Tests/Usings.cs
new file mode 100644
index 0000000..9a28bd8
--- /dev/null
+++ b/Test2.Tests/Usings.cs
@@ -0,0 +1 @@
+global using NUnit.Framework;
diff --git a/Test2/Client.cs b/Test2/Client.cs
new file mode 100644
index 0000000..ffa1182
--- /dev/null
+++ b/Test2/Client.cs
@@ -0,0 +1,86 @@
+namespace Test2;
+
+using System.Net;
+using System.Net.Sockets;
+
+public class Client
+{
+ public IPAddress Ip { get; private set; }
+ public int Port { get; private set; }
+ public bool IsInterrupted { get; private set; } = false;
+
+ ///
+ /// Creates new instance of server class.
+ ///
+ public Client(IPAddress ip, int port)
+ {
+ Ip = ip;
+ Port = port;
+ }
+
+ ///
+ /// Starts client, that writes messages from concole, communicates witn server and stops when get "exit" from server or from comsole.
+ ///
+ ///
+ public async Task Start()
+ {
+ 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);
+
+ async void actionWriteEntered() => await WriteEntered(writer, lockObject);
+ async void actionReadPrintResponse() => await ReadPrintResponse(reader, lockObject);
+
+ 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);
+ }
+ }
+ }
+ 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;
+ }
+ }
+ }
+}
+
+
+
+
diff --git a/Test2/Program.cs b/Test2/Program.cs
new file mode 100644
index 0000000..db69684
--- /dev/null
+++ b/Test2/Program.cs
@@ -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)");
+}
\ No newline at end of file
diff --git a/Test2/Server.cs b/Test2/Server.cs
new file mode 100644
index 0000000..788c4a7
--- /dev/null
+++ b/Test2/Server.cs
@@ -0,0 +1,93 @@
+namespace Test2;
+
+using System.Net;
+using System.Net.Sockets;
+
+///
+/// Server class
+///
+public class Server
+{
+ private TcpListener listener;
+ public int Port { get; private set; }
+ public bool IsInterrupted { get; private set; }
+
+ ///
+ /// Creates new instance of Server class
+ ///
+ public Server(int port)
+ {
+ listener = new TcpListener(IPAddress.Any, port);
+ Port = port;
+ IsInterrupted = false;
+ }
+
+ ///
+ /// Starts server, that writes messages from concole, communicates with client and stops when get "exit" from client or from comsole.
+ ///
+ ///
+ public async Task Start()
+ {
+ object lockObject = new();
+ listener.Start();
+ var tcpClient = await listener.AcceptTcpClientAsync();
+ 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;
+ }
+
+ 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;
+ }
+ }
+ }
+}
+
+
+
diff --git a/Test2/Test2.csproj b/Test2/Test2.csproj
new file mode 100644
index 0000000..d439800
--- /dev/null
+++ b/Test2/Test2.csproj
@@ -0,0 +1,10 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+
+
+
diff --git a/Test2/Test2.sln b/Test2/Test2.sln
new file mode 100644
index 0000000..a22c19f
--- /dev/null
+++ b/Test2/Test2.sln
@@ -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