diff --git a/README.md b/README.md
index 861d945..156c3ee 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,57 @@
|:-|---|---|
| System.Net | [](https://dev.azure.com/nanoframework/System.Net/_build/latest?definitionId=20&repoName=nanoframework%2FSystem.Net&branchName=main) | [](https://www.nuget.org/packages/nanoFramework.System.Net/) |
+## NetworkHelper usage
+
+`NetworkHelper` provides two patterns for establishing a network connection: a blocking token-based approach for simple use-cases, and an event-based approach for background connection management.
+
+### Token-based (retryable)
+
+Call `SetupAndConnectNetwork` with a `CancellationToken` timeout. This method can be called repeatedly — if the first attempt times out, call it again:
+
+```csharp
+bool connected = false;
+while (!connected)
+{
+ CancellationTokenSource cs = new(30000);
+ connected = NetworkHelper.SetupAndConnectNetwork(requiresDateTime: true, token: cs.Token);
+ if (!connected)
+ {
+ Debug.WriteLine($"Network not ready, status: {NetworkHelper.Status}");
+ // wait before retrying
+ Thread.Sleep(5000);
+ }
+}
+```
+
+### Event-based
+
+Call `SetupNetworkHelper` once at startup. The helper connects in the background. Wait on `NetworkReady`:
+
+```csharp
+NetworkHelper.SetupNetworkHelper(requiresDateTime: true);
+
+if (!NetworkHelper.NetworkReady.WaitOne(30000, true))
+{
+ Debug.WriteLine($"Failed to connect: {NetworkHelper.Status}");
+}
+```
+
+> **Note:** `NetworkReady` is reset when the connection is lost and re-signaled when it is restored, accurately reflecting live network state. Code that previously assumed `NetworkReady` would remain set after first connect should be updated to handle transient disconnects.
+
+### Reset and reconfigure
+
+Call `Reset()` to fully reset the helper so it can be called again with different settings, or to restart after an error:
+
+```csharp
+NetworkHelper.Reset();
+
+// Now call SetupNetworkHelper or SetupAndConnectNetwork again
+NetworkHelper.SetupNetworkHelper(requiresDateTime: true);
+```
+
+`SetupNetworkHelper` throws `InvalidOperationException` if called a second time without a prior `Reset()`. Token-based methods (`SetupAndConnectNetwork`) do not have this restriction and are always retryable.
+
## Feedback and documentation
For documentation, providing feedback, issues and finding out how to contribute please refer to the [Home repo](https://github.com/nanoframework/Home).
diff --git a/Tests/IPAddressTests/IPAddressTests.cs b/Tests/IPAddressTests/IPAddressTests.cs
index 49f6b45..3d50069 100644
--- a/Tests/IPAddressTests/IPAddressTests.cs
+++ b/Tests/IPAddressTests/IPAddressTests.cs
@@ -27,11 +27,11 @@ public void NetTest1_DNS()
{
IPHostEntry ipHostEntry = Dns.GetHostEntry("192.168.1.1");
- Assert.Equal(ipHostEntry.AddressList.Length, 1, "GetHostEntry returned wrong number of addresses");
+ Assert.AreEqual(1, ipHostEntry.AddressList.Length, "GetHostEntry returned wrong number of addresses");
IPAddress address = ipHostEntry.AddressList[0];
- Assert.NotNull(address, "Address is null");
- Assert.Equal(address.ToString(), "192.168.1.1", "Address is incorrect");
+ Assert.IsNotNull(address, "Address is null");
+ Assert.AreEqual("192.168.1.1", address.ToString(), "Address is incorrect");
}
[TestMethod]
@@ -54,25 +54,25 @@ public void NetTest2_IPAddressBasic()
+ IPInts[1] * 256
+ IPInts[2] * 256 * 256
+ IPInts[3] * 256 * 256 * 256);
- Assert.NotNull(address, "Address is null");
+ Assert.IsNotNull(address, "Address is null");
Type typeOfAddress = address.GetType();
- Assert.IsType(typeOfAddress, Type.GetType("System.Net.IPAddress"), "Type is incorrect");
+ Assert.IsInstanceOfType(address, Type.GetType("System.Net.IPAddress"), "Type is incorrect");
byte[] targetBytes = { (byte)IPInts[0], (byte)IPInts[1], (byte)IPInts[2], (byte)IPInts[3] };
byte[] addressBytes = address.GetAddressBytes();
- Assert.Equal(addressBytes.Length, 4, "GetAddressBytes returns wrong size");
+ Assert.AreEqual(4, addressBytes.Length, "GetAddressBytes returns wrong size");
for (int j = 0; j < 4; j++)
{
- Assert.Equal(addressBytes[j], targetBytes[j], "GetAddressBytes returns wrong bytes");
+ Assert.AreEqual(targetBytes[j], addressBytes[j], "GetAddressBytes returns wrong bytes");
}
IPAddress addressFromByteArray = new(targetBytes);
addressBytes = addressFromByteArray.GetAddressBytes();
for (int j = 0; j < 4; j++)
{
- Assert.Equal(addressBytes[j], targetBytes[j], "Address from byte array returns wrong bytes");
+ Assert.AreEqual(targetBytes[j], addressBytes[j], "Address from byte array returns wrong bytes");
}
IPAddress address2 = new(
@@ -81,9 +81,9 @@ public void NetTest2_IPAddressBasic()
+ IPInts[2] * 256 * 256
+ IPInts[3] * 256 * 256 * 256);
- Assert.Equal(address.ToString(), address2.ToString(), "ToString returns differently for same data");
+ Assert.AreEqual(address.ToString(), address2.ToString(), "ToString returns differently for same data");
- Assert.Equal(address.GetHashCode(), address2.GetHashCode(), "GetHasCode returns differently for same data");
+ Assert.AreEqual(address.GetHashCode(), address2.GetHashCode(), "GetHasCode returns differently for same data");
address2 = new IPAddress(
(IPInts[0] % 2 + 1)
@@ -91,7 +91,7 @@ public void NetTest2_IPAddressBasic()
+ (IPInts[2] % 2 + 1) * 256 * 256
+ (IPInts[3] % 2 + 1) * 256 * 256 * 256);
- Assert.NotEqual(address.GetHashCode(), address2.GetHashCode(), "GetHasCode returns same for " + address.ToString()
+ Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode(), "GetHasCode returns same for " + address.ToString()
+ " as " + address2.ToString());
}
}
@@ -100,20 +100,20 @@ public void NetTest2_IPAddressBasic()
public void NetTest3_IPAddressLoopback()
{
IPAddress address = IPAddress.Loopback;
- Assert.NotNull(address, "Address is null");
+ Assert.IsNotNull(address, "Address is null");
- Assert.Equal(address.ToString(), "127.0.0.1", "Address is incorrect");
+ Assert.AreEqual("127.0.0.1", address.ToString(), "Address is incorrect");
Type typeOfAddress = address.GetType();
- Assert.IsType(typeOfAddress, Type.GetType("System.Net.IPAddress"), "Type is incorrect");
+ Assert.IsInstanceOfType(address, Type.GetType("System.Net.IPAddress"), "Type is incorrect");
byte[] localhostBytes = { 127, 0, 0, 1 };
byte[] addressBytes = address.GetAddressBytes();
- Assert.Equal(addressBytes.Length, 4, "GetAddressBytes returns wrong size");
+ Assert.AreEqual(4, addressBytes.Length, "GetAddressBytes returns wrong size");
for (int i = 0; i < 4; i++)
{
- Assert.Equal(addressBytes[i], localhostBytes[i], "GetAddressBytes returns wrong bytes");
+ Assert.AreEqual(localhostBytes[i], addressBytes[i], "GetAddressBytes returns wrong bytes");
}
}
@@ -123,20 +123,20 @@ public void NetTest4_IPAddressAny()
{
IPAddress address = IPAddress.Any;
- Assert.NotNull(address, "Address is null");
+ Assert.IsNotNull(address, "Address is null");
- Assert.Equal(address.ToString(), "0.0.0.0", "Address is incorrect");
+ Assert.AreEqual("0.0.0.0", address.ToString(), "Address is incorrect");
Type typeOfAddress = address.GetType();
- Assert.IsType(typeOfAddress, Type.GetType("System.Net.IPAddress"), "Type is incorrect");
+ Assert.IsInstanceOfType(address, Type.GetType("System.Net.IPAddress"), "Type is incorrect");
byte[] localhostBytes = { 0, 0, 0, 0 };
byte[] addressBytes = address.GetAddressBytes();
- Assert.Equal(addressBytes.Length, 4, "GetAddressBytes returns wrong size");
+ Assert.AreEqual(4, addressBytes.Length, "GetAddressBytes returns wrong size");
for (int i = 0; i < 4; i++)
{
- Assert.Equal(addressBytes[i], localhostBytes[i], "GetAddressBytes returns wrong bytes");
+ Assert.AreEqual(localhostBytes[i], addressBytes[i], "GetAddressBytes returns wrong bytes");
}
}
@@ -171,29 +171,29 @@ public void NetTest5_IPEndPointBasic()
Debug.WriteLine("EndPoint2 created with long and int");
IPEndPoint endPoint2 = new(addressLong, portInt);
- Assert.NotNull(endPoint1, "EndPoint1 is null");
- Assert.NotNull(endPoint2, "EndPoint2 is null");
+ Assert.IsNotNull(endPoint1, "EndPoint1 is null");
+ Assert.IsNotNull(endPoint2, "EndPoint2 is null");
Type typeOfEndPoint = endPoint1.GetType();
- Assert.IsType(typeOfEndPoint, Type.GetType("System.Net.IPEndPoint"), "EndPoint1 Type is incorrect");
+ Assert.IsInstanceOfType(endPoint1, Type.GetType("System.Net.IPEndPoint"), "EndPoint1 Type is incorrect");
typeOfEndPoint = endPoint2.GetType();
- Assert.IsType(typeOfEndPoint, Type.GetType("System.Net.IPEndPoint"), "EndPoint2 Type is incorrect");
+ Assert.IsInstanceOfType(endPoint2, Type.GetType("System.Net.IPEndPoint"), "EndPoint2 Type is incorrect");
- Assert.Equal(endPoint1.ToString(), endPoint2.ToString(), "ToString returns differently for same data");
+ Assert.AreEqual(endPoint1.ToString(), endPoint2.ToString(), "ToString returns differently for same data");
- Assert.True(endPoint1.Equals(endPoint2), "Equals returns false for same data");
+ Assert.IsTrue(endPoint1.Equals(endPoint2), "Equals returns false for same data");
int hashCode1 = endPoint1.GetHashCode();
int hashCode2 = endPoint2.GetHashCode();
- Assert.Equal(hashCode1, hashCode2, "GetHasCode returns differently for same data");
+ Assert.AreEqual(hashCode1, hashCode2, "GetHasCode returns differently for same data");
- Assert.False(endPoint1.Address.ToString() != endPoint2.Address.ToString()
+ Assert.IsFalse(endPoint1.Address.ToString() != endPoint2.Address.ToString()
|| endPoint1.Address.ToString() != address.ToString()
|| endPoint2.Address.ToString() != address.ToString(), "Address returns wrong data");
- Assert.False(endPoint1.Port != endPoint2.Port
+ Assert.IsFalse(endPoint1.Port != endPoint2.Port
|| endPoint1.Port != portInt
|| endPoint2.Port != portInt, "Port returns wrong data");
@@ -201,17 +201,17 @@ public void NetTest5_IPEndPointBasic()
endPoint2 = (IPEndPoint)endPoint2.Create(endPoint1.Serialize());
typeOfEndPoint = endPoint2.GetType();
- Assert.IsType(typeOfEndPoint, Type.GetType("System.Net.IPEndPoint"), "EndPoint2 Type is incorrect after clone");
+ Assert.IsInstanceOfType(endPoint2, Type.GetType("System.Net.IPEndPoint"), "EndPoint2 Type is incorrect after clone");
- Assert.Equal(endPoint1.ToString(), endPoint2.ToString(), "ToString returns differently for cloned data");
+ Assert.AreEqual(endPoint1.ToString(), endPoint2.ToString(), "ToString returns differently for cloned data");
- Assert.Equal(endPoint1.GetHashCode(), endPoint2.GetHashCode(), "GetHashCode returns differently for cloned data");
+ Assert.AreEqual(endPoint1.GetHashCode(), endPoint2.GetHashCode(), "GetHashCode returns differently for cloned data");
- Assert.False(endPoint1.Address.ToString() != endPoint2.Address.ToString()
+ Assert.IsFalse(endPoint1.Address.ToString() != endPoint2.Address.ToString()
|| endPoint1.Address.ToString() != address.ToString()
|| endPoint2.Address.ToString() != address.ToString(), "Address returns wrong data after clone");
- Assert.False(endPoint1.Port != endPoint2.Port
+ Assert.IsFalse(endPoint1.Port != endPoint2.Port
|| endPoint1.Port != portInt
|| endPoint2.Port != portInt, "Port returns wrong data after clone");
@@ -225,14 +225,14 @@ public void NetTest5_IPEndPointBasic()
+ (IPInts[3] % 2 + 1) * 256 * 256 * 256;
endPoint2 = new IPEndPoint(addressLong2, portInt2);
- Assert.NotEqual(endPoint1.GetHashCode(), endPoint2.GetHashCode(), "GetHashCode returns same for "
+ Assert.AreNotEqual(endPoint1.GetHashCode(), endPoint2.GetHashCode(), "GetHashCode returns same for "
+ endPoint1.ToString()
+ " as " + endPoint2.ToString());
- Assert.False(endPoint1.Address == endPoint2.Address
+ Assert.IsFalse(endPoint1.Address == endPoint2.Address
|| endPoint2.Address == address, "Address returns wrong data after change");
- Assert.False(endPoint1.Port == endPoint2.Port
+ Assert.IsFalse(endPoint1.Port == endPoint2.Port
|| endPoint2.Port == portInt, "Port returns wrong data after change");
}
}
@@ -241,13 +241,13 @@ public void NetTest5_IPEndPointBasic()
public void NetTest5_IPHostEntryBasic()
{
IPHostEntry ipHostEntry = Dns.GetHostEntry("192.168.1.1");
- Assert.NotNull(ipHostEntry, "IPHostEntry is null");
+ Assert.IsNotNull(ipHostEntry, "IPHostEntry is null");
Type typeOfIPHostEntry = ipHostEntry.GetType();
- Assert.IsType(typeOfIPHostEntry, Type.GetType("System.Net.IPHostEntry"), "IPHostEntry Type is incorrect");
+ Assert.IsInstanceOfType(ipHostEntry, Type.GetType("System.Net.IPHostEntry"), "IPHostEntry Type is incorrect");
- Assert.Equal(ipHostEntry.AddressList[0].ToString(), "192.168.1.1", "AddressList[0] is incorrect");
- Assert.Throws(typeof(IndexOutOfRangeException), () => { ipHostEntry.AddressList[1].ToString(); });
+ Assert.AreEqual("192.168.1.1", ipHostEntry.AddressList[0].ToString(), "AddressList[0] is incorrect");
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () => { ipHostEntry.AddressList[1].ToString(); });
}
[TestMethod]
@@ -278,25 +278,25 @@ public void NetTest6_SocketAddressBasic()
SocketAddress socketAddress1 = ipEndpoint1.Serialize();
SocketAddress socketAddress2 = ipEndpoint1.Serialize();
- Assert.NotNull(socketAddress1, "socketAddress1 is null");
- Assert.NotNull(socketAddress2, "socketAddress2 is null");
+ Assert.IsNotNull(socketAddress1, "socketAddress1 is null");
+ Assert.IsNotNull(socketAddress2, "socketAddress2 is null");
Type typeOfSocketAddress = socketAddress1.GetType();
- Assert.IsType(typeOfSocketAddress, Type.GetType("System.Net.SocketAddress"), "socketAddress1 Type is incorrect");
+ Assert.IsInstanceOfType(socketAddress1, Type.GetType("System.Net.SocketAddress"), "socketAddress1 Type is incorrect");
typeOfSocketAddress = socketAddress2.GetType();
- Assert.IsType(typeOfSocketAddress, Type.GetType("System.Net.SocketAddress"), "socketAddress2 Type is incorrect");
+ Assert.IsInstanceOfType(socketAddress2, Type.GetType("System.Net.SocketAddress"), "socketAddress2 Type is incorrect");
- Assert.Equal(socketAddress1.ToString(), socketAddress2.ToString(), "ToString returns differently for same data");
+ Assert.AreEqual(socketAddress1.ToString(), socketAddress2.ToString(), "ToString returns differently for same data");
- Assert.Equal(socketAddress1.GetHashCode(), socketAddress2.GetHashCode(), $"GetHashCode returns differently for same data");
- Assert.True(socketAddress1.Family == AddressFamily.InterNetwork, "socketAddress1 Family is incorrect");
- Assert.True(socketAddress2.Family == AddressFamily.InterNetwork, "socketAddress2 Family is incorrect");
+ Assert.AreEqual(socketAddress1.GetHashCode(), socketAddress2.GetHashCode(), $"GetHashCode returns differently for same data");
+ Assert.IsTrue(socketAddress1.Family == AddressFamily.InterNetwork, "socketAddress1 Family is incorrect");
+ Assert.IsTrue(socketAddress2.Family == AddressFamily.InterNetwork, "socketAddress2 Family is incorrect");
// Recreate a different Socket
socketAddress2 = new SocketAddress(AddressFamily.Chaos, 8);
- Assert.NotEqual(socketAddress1.GetHashCode(), socketAddress2.GetHashCode(), "GetHashCode returns same for "
+ Assert.AreNotEqual(socketAddress1.GetHashCode(), socketAddress2.GetHashCode(), "GetHashCode returns same for "
+ socketAddress1.ToString() + " " + socketAddress1.GetHashCode()
+ " as " + socketAddress2.ToString() + " " + socketAddress2.GetHashCode());
}
diff --git a/Tests/NetworkHelperTests/ConnectToEthernetTests.cs b/Tests/NetworkHelperTests/ConnectToEthernetTests.cs
index e33fa78..7a55754 100644
--- a/Tests/NetworkHelperTests/ConnectToEthernetTests.cs
+++ b/Tests/NetworkHelperTests/ConnectToEthernetTests.cs
@@ -29,10 +29,9 @@ public void TestFixedIPAddress_01()
DisplayLastError(success);
- Assert.True(success);
+ Assert.IsTrue(success);
- // need to reset this internal flag to allow calling the NetworkHelper again
- NetworkHelper.ResetInstance();
+ NetworkHelper.Reset();
}
[TestMethod]
@@ -45,10 +44,9 @@ public void TestFixedIPAddress_02()
new[] { "192.168.1.1" }), true);
// wait 10 seconds to connect to the network
- Assert.True(NetworkHelper.NetworkReady.WaitOne(10000, true));
+ Assert.IsTrue(NetworkHelper.NetworkReady.WaitOne(10000, true));
- // need to reset this internal flag to allow calling the NetworkHelper again
- NetworkHelper.ResetInstance();
+ NetworkHelper.Reset();
}
[TestMethod]
@@ -62,10 +60,9 @@ public void TestDhcp_01()
DisplayLastError(success);
- Assert.True(success);
+ Assert.IsTrue(success);
- // need to reset this internal flag to allow calling the NetworkHelper again
- NetworkHelper.ResetInstance();
+ NetworkHelper.Reset();
}
[TestMethod]
@@ -74,23 +71,64 @@ public void TestDhcp_02()
NetworkHelper.SetupNetworkHelper(true);
// wait 10 seconds to connect to the network and get an IP address
- Assert.True(NetworkHelper.NetworkReady.WaitOne(10000, true));
+ Assert.IsTrue(NetworkHelper.NetworkReady.WaitOne(10000, true));
- // need to reset this internal flag to allow calling the NetworkHelper again
- NetworkHelper.ResetInstance();
+ NetworkHelper.Reset();
}
[TestMethod]
public void TestSingleUsage()
{
- Assert.Throws(typeof(InvalidOperationException), () =>
+ Assert.ThrowsException(typeof(InvalidOperationException), () =>
{
// call once, it's OK
NetworkHelper.SetupNetworkHelper();
- // call twice, it's a NO NO and should throw an exception
+ // call twice without Reset — must throw
NetworkHelper.SetupNetworkHelper();
});
+
+ NetworkHelper.Reset();
+ }
+
+ [TestMethod]
+ public void TestRetryAfterTimeout()
+ {
+ // First attempt: very short timeout so it expires
+ CancellationTokenSource cs1 = new(1000);
+ var firstResult = NetworkHelper.SetupAndConnectNetwork(token: cs1.Token);
+
+ Assert.IsFalse(firstResult, "First call should have timed out");
+ Assert.IsTrue(NetworkHelper.Status == NetworkHelperStatus.TokenExpiredWaitingIPAddress);
+
+ // Second attempt: longer timeout — must not throw InvalidOperationException
+ CancellationTokenSource cs2 = new(10000);
+ var secondResult = NetworkHelper.SetupAndConnectNetwork(token: cs2.Token);
+
+ // If there is a network, second attempt should succeed;
+ // if not, it will time out again — either way, it must NOT throw
+ Assert.IsTrue(
+ NetworkHelper.Status == NetworkHelperStatus.NetworkIsReady ||
+ NetworkHelper.Status == NetworkHelperStatus.TokenExpiredWaitingIPAddress ||
+ NetworkHelper.Status == NetworkHelperStatus.TokenExpiredWaitingDateTime,
+ "Expected a terminal status after the second attempt");
+
+ NetworkHelper.Reset();
+ }
+
+ [TestMethod]
+ public void TestResetAllowsSetupNetworkHelperRestart()
+ {
+ NetworkHelper.SetupNetworkHelper();
+
+ // Reset and call again — must not throw
+ NetworkHelper.Reset();
+ NetworkHelper.SetupNetworkHelper();
+
+ // wait briefly
+ NetworkHelper.NetworkReady.WaitOne(5000, true);
+
+ NetworkHelper.Reset();
}
public void DisplayLastError(bool success)
diff --git a/Tests/SocketTests/SocketExceptionsTests.cs b/Tests/SocketTests/SocketExceptionsTests.cs
index d218802..76de8c1 100644
--- a/Tests/SocketTests/SocketExceptionsTests.cs
+++ b/Tests/SocketTests/SocketExceptionsTests.cs
@@ -25,7 +25,7 @@ public void SocketExceptionTest2_AddressAlreadyInUse()
{
Socket socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Socket socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- Assert.Throws(typeof(SocketException), () =>
+ Assert.ThrowsException(typeof(SocketException), () =>
{
socketClient.Bind(new IPEndPoint(IPAddress.Loopback, 10));
@@ -46,7 +46,7 @@ public void SocketExceptionTest3_Protocol_Address_FamilyNotSupported()
}
catch (SocketException e)
{
- Assert.False(e.ErrorCode != (int)SocketError.ProtocolFamilyNotSupported && e.ErrorCode != (int)SocketError.AddressFamilyNotSupported, "Incorrect ErrorCode in SocketException "
+ Assert.IsFalse(e.ErrorCode != (int)SocketError.ProtocolFamilyNotSupported && e.ErrorCode != (int)SocketError.AddressFamilyNotSupported, "Incorrect ErrorCode in SocketException "
+ e.ErrorCode);
return;
}
@@ -65,7 +65,7 @@ public void SocketExceptionTest4_ProtocolNotSupported()
}
catch (SocketException e)
{
- Assert.Equal(e.ErrorCode, (int)SocketError.ProtocolNotSupported, "Incorrect ErrorCode in SocketException "
+ Assert.AreEqual((int)SocketError.ProtocolNotSupported, e.ErrorCode, "Incorrect ErrorCode in SocketException "
+ e.ErrorCode);
return;
}
@@ -77,7 +77,7 @@ public void SocketExceptionTest6_IsConnected()
{
SocketPair testSockets = new SocketPair(ProtocolType.Tcp, SocketType.Stream);
- Assert.Throws(typeof(SocketException), () =>
+ Assert.ThrowsException(typeof(SocketException), () =>
{
testSockets.Startup(0, 0);
testSockets.socketServer.Listen(1);
@@ -93,7 +93,7 @@ public void SocketExceptionTest6_IsConnected()
public void SocketExceptionTest11_AccessDenied()
{
SocketPair testSockets = new SocketPair(ProtocolType.Udp, SocketType.Dgram);
- Assert.Throws(typeof(SocketException), () =>
+ Assert.ThrowsException(typeof(SocketException), () =>
{
int clientPort = SocketTools.nextPort;
int serverPort = SocketTools.nextPort;
@@ -113,7 +113,7 @@ public void SocketExceptionTest12_NotConnected()
{
SocketPair testSockets = new SocketPair(ProtocolType.Tcp, SocketType.Stream);
- Assert.Throws(typeof(SocketException), () =>
+ Assert.ThrowsException(typeof(SocketException), () =>
{
testSockets.Startup(0, 0);
Socket socketTemp = new Socket(AddressFamily.InterNetwork,
@@ -130,7 +130,7 @@ public void SocketExceptionTest13_InvalidArgument()
{
SocketPair testSockets = new SocketPair(ProtocolType.Tcp, SocketType.Stream);
- Assert.Throws(typeof(SocketException), () =>
+ Assert.ThrowsException(typeof(SocketException), () =>
{
int clientPort = SocketTools.nextPort;
int serverPort = SocketTools.nextPort;
@@ -147,7 +147,7 @@ public void SocketExceptionTest13_InvalidArgument()
public void SocketExceptionTest14_AddressNotAvailable()
{
SocketPair testSockets = new SocketPair(ProtocolType.Tcp, SocketType.Stream);
- Assert.Throws(typeof(SocketException), () =>
+ Assert.ThrowsException(typeof(SocketException), () =>
{
int clientPort = SocketTools.nextPort;
int serverPort = SocketTools.nextPort;
@@ -163,7 +163,7 @@ public void SocketExceptionTest14_AddressNotAvailable()
[TestMethod]
public void SocketExceptionTest16_HostNotFound()
{
- Assert.Throws(typeof(SocketException), () =>
+ Assert.ThrowsException(typeof(SocketException), () =>
{
IPHostEntry ipHostEntry = Dns.GetHostEntry("fakeHostName");
});
@@ -172,7 +172,7 @@ public void SocketExceptionTest16_HostNotFound()
[TestMethod]
public void SocketExceptionTest17_SocketError()
{
- Assert.Throws(typeof(SocketException), () =>
+ Assert.ThrowsException(typeof(SocketException), () =>
{
SocketPair testSockets = new SocketPair(ProtocolType.Udp, SocketType.Stream);
});
@@ -183,7 +183,7 @@ public void SocketExceptionTest18_Fault()
{
SocketPair testSockets = new SocketPair(ProtocolType.Tcp, SocketType.Stream);
- Assert.Throws(typeof(SocketException), () =>
+ Assert.ThrowsException(typeof(SocketException), () =>
{
testSockets.socketClient.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.Linger, new byte[] { (byte)0 });
@@ -197,7 +197,7 @@ public void SocketExceptionTest18_Fault()
public void SocketExceptionTest19_ProtocolOption()
{
SocketPair testSockets = new SocketPair(ProtocolType.Tcp, SocketType.Stream);
- Assert.Throws(typeof(SocketException), () =>
+ Assert.ThrowsException(typeof(SocketException), () =>
{
testSockets.Startup(0, 0);
testSockets.socketClient.GetSocketOption(SocketOptionLevel.IP,
@@ -212,7 +212,7 @@ public void SocketExceptionTest19_ProtocolOption()
public void SocketExceptionTest20_OperationNotSupported()
{
SocketPair testSockets = new SocketPair(ProtocolType.Tcp, SocketType.Stream);
- Assert.Throws(typeof(SocketException), () =>
+ Assert.ThrowsException(typeof(SocketException), () =>
{
testSockets.Startup(0, 0);
diff --git a/Tests/SocketTests/SocketOptionsTests.cs b/Tests/SocketTests/SocketOptionsTests.cs
index 80b455d..953d399 100644
--- a/Tests/SocketTests/SocketOptionsTests.cs
+++ b/Tests/SocketTests/SocketOptionsTests.cs
@@ -29,17 +29,17 @@ public void SocketGetSocketOptions_00()
socketType,
ProtocolType.Tcp);
- Assert.Throws(typeof(NotSupportedException), () =>
+ Assert.ThrowsException(typeof(NotSupportedException), () =>
{
testSocket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.AddMembership);
}, "Getting SocketOptionName.AddMembership should have thrown an exception");
- Assert.Throws(typeof(NotSupportedException), () =>
+ Assert.ThrowsException(typeof(NotSupportedException), () =>
{
testSocket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DropMembership);
}, "Getting SocketOptionName.DropMembership should have thrown an exception");
- Assert.True((SocketType)testSocket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Type) == socketType, "Getting SocketOptionName.Type returned a different type.");
+ Assert.IsTrue((SocketType)testSocket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Type) == socketType, "Getting SocketOptionName.Type returned a different type.");
testSocket?.Close();
}
diff --git a/nanoFramework.System.Net/NetworkHelper/NetworkHelper.cs b/nanoFramework.System.Net/NetworkHelper/NetworkHelper.cs
index 13a5f29..04696d3 100644
--- a/nanoFramework.System.Net/NetworkHelper/NetworkHelper.cs
+++ b/nanoFramework.System.Net/NetworkHelper/NetworkHelper.cs
@@ -27,8 +27,11 @@ public static class NetworkHelper
private static IPConfiguration _ipConfiguration;
+ private static Thread _workerThread;
+ private static bool _stopRequested;
+
///
- /// This flag will make sure there is only one and only call to any of the helper methods.
+ /// This flag will make sure there is only one and only one call to the event-based helper methods.
///
private static bool _helperInstanciated = false;
@@ -37,7 +40,12 @@ public static class NetworkHelper
///
///
/// The conditions for this are setup in the call to .
- /// It will be a composition of network connected, IpAddress available and valid system .
+ /// It will be a composition of network connected, IpAddress available and valid system .
+ ///
+ /// When using , this event is reset when the connection is lost
+ /// and re-signaled when it is restored, accurately reflecting live network state.
+ ///
+ ///
public static ManualResetEvent NetworkReady => _networkReady;
///
@@ -55,7 +63,7 @@ public static class NetworkHelper
/// That will be the network connection to be up, having a valid IpAddress and optionally for a valid date and time to become available.
///
/// Set to if valid date and time are required.
- /// If any of the methods is called more than once.
+ /// If called more than once without an intervening call to .
/// There is no network interface configured. Open the 'Edit Network Configuration' in Device Explorer and configure one.
public static void SetupNetworkHelper(bool requiresDateTime = false)
{
@@ -64,7 +72,8 @@ public static void SetupNetworkHelper(bool requiresDateTime = false)
SetupHelper(true);
// fire working thread
- new Thread(WorkingThread).Start();
+ _workerThread = new Thread(WorkingThread);
+ _workerThread.Start();
}
///
@@ -73,6 +82,7 @@ public static void SetupNetworkHelper(bool requiresDateTime = false)
///
/// The static IP configuration you want to apply.
/// Set to if valid date and time are required.
+ /// If called more than once without an intervening call to .
/// There is no network interface configured. Open the 'Edit Network Configuration' in Device Explorer and configure one.
public static void SetupNetworkHelper(
IPConfiguration ipConfiguration,
@@ -84,11 +94,13 @@ public static void SetupNetworkHelper(
SetupHelper(true);
// fire working thread
- new Thread(WorkingThread).Start();
+ _workerThread = new Thread(WorkingThread);
+ _workerThread.Start();
}
///
/// This will wait for the network connection to be up and optionally for a valid date and time to become available.
+ /// This method is retryable and can be called multiple times after a previous call times out or fails.
///
/// A used for timing out the operation.
/// Set to if valid date and time are required.
@@ -102,6 +114,7 @@ public static bool SetupAndConnectNetwork(
///
/// This will wait for the network connection to be up and optionally for a valid date and time to become available.
+ /// This method is retryable and can be called multiple times after a previous call times out or fails.
///
/// The static IPv4 configuration to apply to the Ethernet network interface.
/// A used for timing out the operation.
@@ -122,6 +135,44 @@ public static bool SetupAndConnectNetwork(
requiresDateTime);
}
+ ///
+ /// Resets the to its initial state, allowing to be called again
+ /// or the network configuration to be changed.
+ ///
+ ///
+ /// Call this before switching network configuration or restarting the event-based helper.
+ /// This method does not disconnect the network interface or alter IP settings.
+ ///
+ public static void Reset()
+ {
+ // deregister event handler to prevent a handler leak
+ NetworkChange.NetworkAddressChanged -= AddressChangedCallback;
+
+ // signal the worker thread to stop and unblock it if it is waiting for an IP address
+ _stopRequested = true;
+
+ if (_ipAddressAvailable != null)
+ {
+ _ipAddressAvailable.Set();
+ }
+
+ if (_workerThread != null)
+ {
+ // give the thread a moment to exit cleanly before clearing shared state
+ _workerThread.Join(1000);
+ _workerThread = null;
+ }
+
+ _stopRequested = false;
+ _helperInstanciated = false;
+ _ipAddressAvailable = null;
+ _networkReady = new(false);
+ _requiresDateTime = false;
+ _networkHelperStatus = NetworkHelperStatus.None;
+ _helperException = null;
+ _ipConfiguration = null;
+ }
+
internal static bool InternalWaitNetworkAvailable(
NetworkInterfaceType networkInterface,
ref NetworkHelperStatus helperStatus,
@@ -194,20 +245,32 @@ internal static bool InternalWaitNetworkAvailable(
private static void WorkingThread()
{
// check if we have an IP
- if(!NetworkHelperInternal.CheckIP(
+ if (!NetworkHelperInternal.CheckIP(
_workingNetworkInterface,
_ipConfiguration))
{
- // wait here until we have an IP address
+ // wait here until we have an IP address or until Reset() unblocks us
_ipAddressAvailable.WaitOne();
}
+ // bail out if Reset() was called while we were waiting
+ if (_stopRequested)
+ {
+ return;
+ }
+
if (_requiresDateTime)
{
// wait until there is a valid DateTime
NetworkHelperInternal.WaitForValidDateTime();
}
+ // bail out if Reset() was called during the DateTime wait
+ if (_stopRequested)
+ {
+ return;
+ }
+
// all conditions met
_networkReady.Set();
@@ -217,68 +280,73 @@ private static void WorkingThread()
private static void AddressChangedCallback(object sender, EventArgs e)
{
- if(NetworkHelperInternal.CheckIP(
+ if (_stopRequested)
+ {
+ return;
+ }
+
+ if (NetworkHelperInternal.CheckIP(
_workingNetworkInterface,
_ipConfiguration))
{
_ipAddressAvailable.Set();
+
+ // re-signal ready; check DateTime condition in case it was required
+ if (!_requiresDateTime || DateTime.UtcNow.Year >= 2021)
+ {
+ _networkReady.Set();
+ _networkHelperStatus = NetworkHelperStatus.NetworkIsReady;
+ }
+ }
+ else
+ {
+ // IP was lost - reset signals so callers block until the connection is restored
+ _networkReady.Reset();
+ _ipAddressAvailable.Reset();
+ _networkHelperStatus = NetworkHelperStatus.Reconnecting;
}
}
///
/// Perform setup of the various fields and events, along with any of the required event handlers.
///
- /// Set true to setup the events. Required for the thread approach. Not required for the CancelationToken implementation.
+ /// Set to setup the events and background thread. Required for the event-based approach. Not required for the CancellationToken approach.
private static void SetupHelper(bool setupEvents)
{
- if (_helperInstanciated)
- {
- throw new InvalidOperationException();
- }
- else
+ if (setupEvents)
{
+ if (_helperInstanciated)
+ {
+ throw new InvalidOperationException();
+ }
+
// set flag
_helperInstanciated = true;
// setup event
_ipAddressAvailable = new(false);
+ }
- NetworkInterface[] nis = NetworkInterface.GetAllNetworkInterfaces();
+ NetworkInterface[] nis = NetworkInterface.GetAllNetworkInterfaces();
- if (setupEvents)
+ if (setupEvents)
+ {
+ // check if there are any network interfaces setup
+ if (nis.Length == 0)
{
- // check if there are any network interface setup
- if (nis.Length == 0)
- {
- _networkHelperStatus = NetworkHelperStatus.FailedNoNetworkInterface;
-
- throw new NotSupportedException();
- }
+ _networkHelperStatus = NetworkHelperStatus.FailedNoNetworkInterface;
- // setup handler
- NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(AddressChangedCallback);
+ throw new NotSupportedException();
}
- NetworkHelperInternal.InternalSetupHelper(nis, _workingNetworkInterface, _ipConfiguration);
-
- // update status
- _networkHelperStatus = NetworkHelperStatus.Started;
+ // setup handler
+ NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(AddressChangedCallback);
}
- }
-
- ///
- /// Method to reset internal fields to it's defaults
- /// ONLY TO BE USED BY UNIT TESTS
- ///
- internal static void ResetInstance()
- {
- _ipAddressAvailable = null;
- _networkReady = new(false);
- _requiresDateTime = false;
- _networkHelperStatus = NetworkHelperStatus.None;
- _helperException = null;
- _ipConfiguration = null;
- _helperInstanciated = false;
+
+ NetworkHelperInternal.InternalSetupHelper(nis, _workingNetworkInterface, _ipConfiguration);
+
+ // update status
+ _networkHelperStatus = NetworkHelperStatus.Started;
}
}
-}
+}
\ No newline at end of file
diff --git a/nanoFramework.System.Net/NetworkHelper/NetworkHelperStatus.cs b/nanoFramework.System.Net/NetworkHelper/NetworkHelperStatus.cs
index 6a4f077..57f7895 100644
--- a/nanoFramework.System.Net/NetworkHelper/NetworkHelperStatus.cs
+++ b/nanoFramework.System.Net/NetworkHelper/NetworkHelperStatus.cs
@@ -45,6 +45,11 @@ public enum NetworkHelperStatus
///
/// An exception occurred with waiting for the network to become ready. Check HelperException property to find the that was thrown.
///
- ExceptionOccurred
+ ExceptionOccurred,
+
+ ///
+ /// The network was previously ready but the IP address was lost. Waiting for the connection to be restored.
+ ///
+ Reconnecting
}
}