diff --git a/S7.Net.UnitTest/ConnectionCloseTest.cs b/S7.Net.UnitTest/ConnectionCloseTest.cs
new file mode 100644
index 0000000..87c9ea9
--- /dev/null
+++ b/S7.Net.UnitTest/ConnectionCloseTest.cs
@@ -0,0 +1,181 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.IO;
+using System.Linq;
+using System.Net.Sockets;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace S7.Net.UnitTest
+{
+ ///
+ /// Test stream which only gives 1 byte per read.
+ ///
+ class TestStreamConnectionClose : Stream
+ {
+ private readonly CancellationTokenSource _cancellationTokenSource;
+
+ public TestStreamConnectionClose(CancellationTokenSource cancellationTokenSource)
+ {
+ _cancellationTokenSource = cancellationTokenSource;
+ }
+ public override bool CanRead => false;
+
+ public override bool CanSeek => throw new NotImplementedException();
+
+ public override bool CanWrite => true;
+
+ public override long Length => throw new NotImplementedException();
+
+ public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
+
+ public override void Flush()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ _cancellationTokenSource.Cancel();
+ }
+ }
+
+ ///
+ /// These tests are intended to test functions and other stream-related special cases.
+ ///
+ [TestClass]
+ public class ConnectionCloseTest
+ {
+ const short TestServerPort = 31122;
+ const string TestServerIp = "127.0.0.1";
+
+ [TestMethod]
+ public async Task Test_CancellationDuringTransmission()
+ {
+ var plc = new Plc(CpuType.S7300, TestServerIp, TestServerPort, 0, 2);
+
+ // Set up a shared cancellation source so we can let the stream
+ // initiate cancel after some data has been written to it.
+ var cancellationSource = new CancellationTokenSource();
+ var cancellationToken = cancellationSource.Token;
+
+ var stream = new TestStreamConnectionClose(cancellationSource);
+ var requestData = new byte[100]; // empty data, it does not matter what is in there
+
+ // Set up access to private method and field
+ var dynMethod = plc.GetType().GetMethod("NoLockRequestTpduAsync",
+ BindingFlags.NonPublic | BindingFlags.Instance);
+ if (dynMethod == null)
+ {
+ throw new NullReferenceException("Could not find method 'NoLockRequestTpduAsync' on Plc object.");
+ }
+ var tcpClientField = plc.GetType().GetField("tcpClient", BindingFlags.NonPublic | BindingFlags.Instance);
+ if (tcpClientField == null)
+ {
+ throw new NullReferenceException("Could not find field 'tcpClient' on Plc object.");
+ }
+
+ // Set a value to tcpClient field so we can later ensure that it has been closed.
+ tcpClientField.SetValue(plc, new TcpClient());
+ var tcpClientValue = tcpClientField.GetValue(plc);
+ Assert.IsNotNull(tcpClientValue);
+
+ try
+ {
+ var result = (Task) dynMethod.Invoke(plc, new object[] { stream, requestData, cancellationToken });
+ await result;
+ }
+ catch (OperationCanceledException)
+ {
+ Console.WriteLine("Task was cancelled as expected.");
+
+ // Ensure that the plc connection was closed since the task was cancelled
+ // after data has been sent through the network. We expect that the tcpClient
+ // object was set to NULL
+ var tcpClientValueAfter = tcpClientField.GetValue(plc);
+ Assert.IsNull(tcpClientValueAfter);
+ return;
+ }
+ catch (Exception e)
+ {
+ Assert.Fail($"Wrong exception type received. Expected {typeof(OperationCanceledException)}, received {e.GetType()}.");
+ }
+
+ // Ensure test fails if cancellation did not occur.
+ Assert.Fail("Task was not cancelled as expected.");
+ }
+
+ [TestMethod]
+ public async Task Test_CancellationBeforeTransmission()
+ {
+ var plc = new Plc(CpuType.S7300, TestServerIp, TestServerPort, 0, 2);
+
+ // Set up a cancellation source
+ var cancellationSource = new CancellationTokenSource();
+ var cancellationToken = cancellationSource.Token;
+
+ var stream = new TestStreamConnectionClose(cancellationSource);
+ var requestData = new byte[100]; // empty data, it does not matter what is in there
+
+ // Set up access to private method and field
+ var dynMethod = plc.GetType().GetMethod("NoLockRequestTpduAsync",
+ BindingFlags.NonPublic | BindingFlags.Instance);
+ if (dynMethod == null)
+ {
+ throw new NullReferenceException("Could not find method 'NoLockRequestTpduAsync' on Plc object.");
+ }
+ var tcpClientField = plc.GetType().GetField("tcpClient", BindingFlags.NonPublic | BindingFlags.Instance);
+ if (tcpClientField == null)
+ {
+ throw new NullReferenceException("Could not find field 'tcpClient' on Plc object.");
+ }
+
+ // Set a value to tcpClient field so we can later ensure that it has been closed.
+ tcpClientField.SetValue(plc, new TcpClient());
+ var tcpClientValue = tcpClientField.GetValue(plc);
+ Assert.IsNotNull(tcpClientValue);
+
+ try
+ {
+ // cancel the task before we start transmitting data
+ cancellationSource.Cancel();
+ var result = (Task)dynMethod.Invoke(plc, new object[] { stream, requestData, cancellationToken });
+ await result;
+ }
+ catch (OperationCanceledException)
+ {
+ Console.WriteLine("Task was cancelled as expected.");
+
+ // Ensure that the plc connection was not closed, since we cancelled the task before
+ // sending data through the network. We expect that the tcpClient
+ // object was NOT set to NULL
+ var tcpClientValueAfter = tcpClientField.GetValue(plc);
+ Assert.IsNotNull(tcpClientValueAfter);
+ return;
+ }
+ catch (Exception e)
+ {
+ Assert.Fail($"Wrong exception type received. Expected {typeof(OperationCanceledException)}, received {e.GetType()}.");
+ }
+
+ // Ensure test fails if cancellation did not occur.
+ Assert.Fail("Task was not cancelled as expected.");
+ }
+ }
+}