From bb4b9c60d381c20fafaa2784bb0bff5bb8d1808d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Thu, 24 Sep 2026 12:59:17 +0100 Subject: [PATCH 1/4] Fix several IntelliSense comments in NetworkStream --- .../Sockets/NetworkStream.cs | 140 ++++-------------- 1 file changed, 25 insertions(+), 115 deletions(-) diff --git a/nanoFramework.System.Net/Sockets/NetworkStream.cs b/nanoFramework.System.Net/Sockets/NetworkStream.cs index 0eb67556..f6a5f9ad 100644 --- a/nanoFramework.System.Net/Sockets/NetworkStream.cs +++ b/nanoFramework.System.Net/Sockets/NetworkStream.cs @@ -34,57 +34,17 @@ public class NetworkStream : Stream /// protected bool _disposed; - // Summary: - // Creates a new instance of the System.Net.Sockets.NetworkStream class for - // the specified System.Net.Sockets.Socket. - // - // Parameters: - // socket: - // The System.Net.Sockets.Socket that the System.Net.Sockets.NetworkStream will - // use to send and receive data. - // - // Exceptions: - // System.ArgumentNullException: - // socket is null. - // - // System.IO.IOException: - // socket is not connected.-or- The System.Net.Sockets.Socket.SocketType property - // of socket is not System.Net.Sockets.SocketType.Stream.-or- socket is in a - // nonblocking state. - /// /// Creates a new instance of the class for the specified . /// /// The that the will use to send and receive data. + /// is . + /// is not connected. -or- The property of is not . -or- is in a nonblocking state. public NetworkStream(Socket socket) : this(socket, false) { } - // - // Summary: - // Initializes a new instance of the System.Net.Sockets.NetworkStream class - // for the specified System.Net.Sockets.Socket with the specified System.Net.Sockets.Socket - // ownership. - // - // Parameters: - // ownsSocket: - // true to indicate that the System.Net.Sockets.NetworkStream will take ownership - // of the System.Net.Sockets.Socket; otherwise, false. - // - // socket: - // The System.Net.Sockets.Socket that the System.Net.Sockets.NetworkStream will - // use to send and receive data. - // - // Exceptions: - // System.IO.IOException: - // socket is not connected.-or- The value of the System.Net.Sockets.Socket.SocketType - // property of socket is not System.Net.Sockets.SocketType.Stream.-or- socket - // is in a nonblocking state. - // - // System.ArgumentNullException: - // socket is null. - /// /// Initializes a new instance of the class for the specified /// with the specified ownership. @@ -93,6 +53,8 @@ public NetworkStream(Socket socket) /// use to send and receive data. /// to indicate that the will take ownership of the ; /// otherwise, . + /// is . + /// is not connected. -or- The property of is not . -or- is in a nonblocking state. public NetworkStream(Socket socket, bool ownsSocket) { if (socket == null) throw new ArgumentNullException(); @@ -224,24 +186,11 @@ public virtual bool DataAvailable } } - // - // Summary: - // Closes the System.Net.Sockets.NetworkStream after waiting the specified time - // to allow data to be sent. - // - // Parameters: - // timeout: - // A 32-bit signed integer that specifies how long to wait to send any remaining - // data before closing. - // - // Exceptions: - // System.ArgumentOutOfRangeException: - // timeout is less than -1. - /// /// Closes the after waiting the specified time to allow data to be sent. /// /// A 32-bit signed integer that specifies the number of milliseconds to wait to send any remaining data before closing. + /// is less than -1. /// /// The method frees both unmanaged and managed resources associated with the . If the owns the underlying Socket, it is closed as well. /// @@ -250,7 +199,7 @@ public void Close(int timeout) if (timeout < -1) throw new ArgumentOutOfRangeException(); - System.Threading.Thread.Sleep(timeout); + Threading.Thread.Sleep(timeout); Close(); } @@ -289,34 +238,21 @@ public override void Flush() { } - // Exceptions: - // System.IO.IOException: - // The underlying System.Net.Sockets.Socket is closed. - // - // System.ArgumentNullException: - // buffer is null. - // - // System.ObjectDisposedException: - // The System.Net.Sockets.NetworkStream is closed.-or- There is a failure reading - // from the network. - // - // System.ArgumentOutOfRangeException: - // offset is less than 0.-or- offset is greater than the length of buffer.-or- - // size is less than 0.-or- size is greater than the length of buffer minus - // the value of the offset parameter. -or-An error occurred when accessing the - // socket. See the Remarks section for more information. - /// /// Reads data from the NetworkStream. /// - /// An array of type Byte that is the location in memory to store data read from the NetworkStream. - /// The location in buffer to begin storing the data to. - /// The number of bytes to read from the NetworkStream. - /// The number of bytes read from the NetworkStream. + /// An array of type that is the location in memory to store data read from the . + /// The location in to begin storing the data to. + /// The number of bytes to read from the . + /// The total number of bytes read into the buffer between zero (0) and the requested count. The method returns zero (0) only if zero bytes were requested or if no more bytes are available because the peer socket performed a graceful shutdown. + /// The underlying is closed. + /// is . + /// The is closed or there is a failure reading from the network." + /// is less than 0 or greater than the length of . -or- is less than 0 or greater than the length of minus the value of the parameter. /// - /// This method reads data into the buffer parameter and returns the number of bytes successfully read. If no data is available for reading, the Read method returns 0. The Read operation reads as much data as is available, up to the number of bytes specified by the count parameter. If the remote host shuts down the connection, and all available data has been received, the Read method completes immediately and return zero bytes. + /// This method reads data into the parameter and returns the number of bytes successfully read. The operation reads as much data as is available, up to the number of bytes specified by the parameter. If the remote host shuts down the connection, and all available data has been received, the Read method completes immediately and return zero bytes. /// - /// Check to see if the is readable by calling the property. If you attempt to read from a that is not readable, you will get an IOException. + /// Check to see if the is readable by calling the property. If you attempt to read from a that is not readable, you will get an . /// /// public override int Read(byte[] buffer, int offset, int count) @@ -372,46 +308,20 @@ public override void SetLength(long value) throw new NotSupportedException(); } - // - // Summary: - // Writes data to the System.Net.Sockets.NetworkStream. - // - // Parameters: - // offset: - // The location in buffer from which to start writing data. - // - // size: - // The number of bytes to write to the System.Net.Sockets.NetworkStream. - // - // buffer: - // An array of type System.Byte that contains the data to write to the System.Net.Sockets.NetworkStream. - // - // Exceptions: - // System.ArgumentOutOfRangeException: - // offset is less than 0.-or- offset is greater than the length of buffer.-or- - // size is less than 0.-or- size is greater than the length of buffer minus - // the value of the offset parameter. - // - // System.ObjectDisposedException: - // The System.Net.Sockets.NetworkStream is closed.-or- There was a failure reading - // from the network. - // - // System.IO.IOException: - // There was a failure while writing to the network. -or-An error occurred when - // accessing the socket. See the Remarks section for more information. - // - // System.ArgumentNullException: - // buffer is null. - /// /// Writes data to the . /// - /// An array of type Byte that contains the data to write to the NetworkStream. - /// The location in buffer from which to start writing data. - /// The number of bytes to write to the NetworkStream. + /// An array of type that contains the data to write to the . + /// The location in from which to start writing data. + /// The number of bytes to write to the . + /// is less than 0 or greater than the length of . -or- is less than 0 or greater than the length of minus the value of the parameter. + /// The is closed or there is a failure reading from the network. + /// There was a failure while writing to the network. -or-An error occurred when accessing the socket. See the Remarks section for more information. + /// is . + /// The underlying is not of type or . /// /// The Write method starts at the specified offset and sends count bytes from the contents of buffer to the network. - /// The Write method blocks until the requested number of bytes is sent or a SocketException is thrown. + /// The Write method blocks until the requested number of bytes is sent or a is thrown. /// If you receive a , use the property to obtain /// the specific error code, and refer to the Windows Sockets version 2 API error code documentation in MSDN for a detailed description of the error. /// From 28475107240fcc7bba442d666c050c686eb2dc36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 29 Sep 2026 17:21:51 +0100 Subject: [PATCH 2/4] NetworkStream now rejects non-stream sockets - Constructor throws IOException when socket type is not Stream, matching .NET. - Remove unreachable datagram branches from Read and Write. - Remove redundant socket type check from SslStream constructor. - Fix exception documentation (drop nonblocking clause, stray characters, wrong conditions). - Add NetworkStream constructor tests. --- Tests/SocketTests/NetworkStreamTests.cs | 124 ++++++++++++++++++ Tests/SocketTests/SocketTests.nfproj | 1 + .../Security/SslStream.cs | 5 - .../Sockets/NetworkStream.cs | 53 +++----- 4 files changed, 141 insertions(+), 42 deletions(-) create mode 100644 Tests/SocketTests/NetworkStreamTests.cs diff --git a/Tests/SocketTests/NetworkStreamTests.cs b/Tests/SocketTests/NetworkStreamTests.cs new file mode 100644 index 00000000..708d58f0 --- /dev/null +++ b/Tests/SocketTests/NetworkStreamTests.cs @@ -0,0 +1,124 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.TestFramework; +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; + +namespace NFUnitTestSocketTests +{ + [TestClass] + public class NetworkStreamTests + { + [Setup] + public void SetupConnectToEthernetTests() + { + // Comment next line to run the tests on a real hardware + Assert.SkipTest("Skipping tests using nanoCLR Win32 in a pipeline"); + } + + [TestMethod] + public void Ctor_NullSocket_ThrowsArgumentNullException() + { + Assert.ThrowsException(typeof(ArgumentNullException), () => + { + _ = new NetworkStream(null); + }); + + Assert.ThrowsException(typeof(ArgumentNullException), () => + { + _ = new NetworkStream(null, true); + }); + } + + [TestMethod] + public void Ctor_UnconnectedStreamSocket_ThrowsIOException() + { + Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + try + { + Assert.ThrowsException(typeof(IOException), () => + { + _ = new NetworkStream(socket); + }); + + Assert.ThrowsException(typeof(IOException), () => + { + _ = new NetworkStream(socket, false); + }); + } + finally + { + socket.Close(); + } + } + + [TestMethod] + public void Ctor_ConnectedDgramSocket_ThrowsIOException() + { + SocketPair testSockets = new SocketPair(ProtocolType.Udp, SocketType.Dgram); + + try + { + testSockets.Startup(0, 0); + + // a connected datagram socket has a remote endpoint, so this exercises the socket type check + testSockets.socketClient.Connect(testSockets.epServer); + + Assert.ThrowsException(typeof(IOException), () => + { + _ = new NetworkStream(testSockets.socketClient); + }); + + Assert.ThrowsException(typeof(IOException), () => + { + _ = new NetworkStream(testSockets.socketClient, false); + }); + } + finally + { + testSockets.TearDown(); + } + } + + [TestMethod] + public void Ctor_ConnectedStreamSocket_Succeeds() + { + SocketPair testSockets = new SocketPair(ProtocolType.Tcp, SocketType.Stream); + Socket acceptedSocket = null; + + try + { + testSockets.Startup(0, 0); + testSockets.socketServer.Listen(1); + testSockets.socketClient.Connect(testSockets.epServer); + acceptedSocket = testSockets.socketServer.Accept(); + + NetworkStream clientStream = new NetworkStream(testSockets.socketClient); + NetworkStream serverStream = new NetworkStream(acceptedSocket, true); + + clientStream.Write(testSockets.bufSend, 0, testSockets.bufSend.Length); + + int bytesRead = serverStream.Read(testSockets.bufReceive, 0, testSockets.bufReceive.Length); + + testSockets.AssertDataReceived(bytesRead); + + clientStream.Dispose(); + + // server stream owns the accepted socket, so disposing it closes the socket + serverStream.Dispose(); + acceptedSocket = null; + } + finally + { + acceptedSocket?.Close(); + testSockets.TearDown(); + } + } + } +} diff --git a/Tests/SocketTests/SocketTests.nfproj b/Tests/SocketTests/SocketTests.nfproj index d080e784..11d6a921 100644 --- a/Tests/SocketTests/SocketTests.nfproj +++ b/Tests/SocketTests/SocketTests.nfproj @@ -27,6 +27,7 @@ + diff --git a/nanoFramework.System.Net/Security/SslStream.cs b/nanoFramework.System.Net/Security/SslStream.cs index 44e935d6..ad944d14 100644 --- a/nanoFramework.System.Net/Security/SslStream.cs +++ b/nanoFramework.System.Net/Security/SslStream.cs @@ -54,11 +54,6 @@ public class SslStream : NetworkStream public SslStream(Socket socket) : base(socket, false) { - if (SocketType.Stream != (SocketType)_socketType) - { - throw new NotSupportedException(); - } - _sslContext = -1; _isServer = false; diff --git a/nanoFramework.System.Net/Sockets/NetworkStream.cs b/nanoFramework.System.Net/Sockets/NetworkStream.cs index f6a5f9ad..ba4c8172 100644 --- a/nanoFramework.System.Net/Sockets/NetworkStream.cs +++ b/nanoFramework.System.Net/Sockets/NetworkStream.cs @@ -22,7 +22,7 @@ public class NetworkStream : Stream protected int _socketType; /// - /// Internal endpoint ref used for dgram sockets + /// Internal endpoint ref of the remote peer /// protected EndPoint _remoteEndPoint; @@ -39,7 +39,7 @@ public class NetworkStream : Stream /// /// The that the will use to send and receive data. /// is . - /// is not connected. -or- The property of is not . -or- is in a nonblocking state. + /// is not connected. -or- The property of is not . public NetworkStream(Socket socket) : this(socket, false) { @@ -54,7 +54,7 @@ public NetworkStream(Socket socket) /// to indicate that the will take ownership of the ; /// otherwise, . /// is . - /// is not connected. -or- The property of is not . -or- is in a nonblocking state. + /// is not connected. -or- The property of is not . public NetworkStream(Socket socket, bool ownsSocket) { if (socket == null) throw new ArgumentNullException(); @@ -70,7 +70,12 @@ public NetworkStream(Socket socket, bool ownsSocket) throw new IOException(errCode.ToString(), e); } - + + if (socket.SocketType != SocketType.Stream) + { + throw new IOException(); + } + // Set the internal socket _socket = socket; @@ -245,14 +250,14 @@ public override void Flush() /// The location in to begin storing the data to. /// The number of bytes to read from the . /// The total number of bytes read into the buffer between zero (0) and the requested count. The method returns zero (0) only if zero bytes were requested or if no more bytes are available because the peer socket performed a graceful shutdown. - /// The underlying is closed. + /// The underlying is closed. -or- There was a failure while reading from the network. /// is . - /// The is closed or there is a failure reading from the network." + /// The is closed. /// is less than 0 or greater than the length of . -or- is less than 0 or greater than the length of minus the value of the parameter. /// /// This method reads data into the parameter and returns the number of bytes successfully read. The operation reads as much data as is available, up to the number of bytes specified by the parameter. If the remote host shuts down the connection, and all available data has been received, the Read method completes immediately and return zero bytes. /// - /// Check to see if the is readable by calling the property. If you attempt to read from a that is not readable, you will get an . + /// Check to see if the is readable by calling the property. If you attempt to read from a that is not readable, you will get an . /// /// public override int Read(byte[] buffer, int offset, int count) @@ -273,18 +278,7 @@ public override int Read(byte[] buffer, int offset, int count) count = available; } - if (_socketType == (int)SocketType.Stream) - { - return _socket.Receive(buffer, offset, count, SocketFlags.None); - } - else if (_socketType == (int)SocketType.Dgram) - { - return _socket.ReceiveFrom(buffer, offset, count, SocketFlags.None, ref _remoteEndPoint); - } - else - { - throw new NotSupportedException(); - } + return _socket.Receive(buffer, offset, count, SocketFlags.None); } /// @@ -315,10 +309,9 @@ public override void SetLength(long value) /// The location in from which to start writing data. /// The number of bytes to write to the . /// is less than 0 or greater than the length of . -or- is less than 0 or greater than the length of minus the value of the parameter. - /// The is closed or there is a failure reading from the network. - /// There was a failure while writing to the network. -or-An error occurred when accessing the socket. See the Remarks section for more information. + /// The is closed. + /// There was a failure while writing to the network. -or- An error occurred when accessing the socket. See the Remarks section for more information. /// is . - /// The underlying is not of type or . /// /// The Write method starts at the specified offset and sends count bytes from the contents of buffer to the network. /// The Write method blocks until the requested number of bytes is sent or a is thrown. @@ -333,20 +326,7 @@ public override void Write(byte[] buffer, int offset, int count) if (offset < 0 || offset > buffer.Length) throw new ArgumentOutOfRangeException(); if (count < 0 || count > buffer.Length - offset) throw new ArgumentOutOfRangeException(); - int bytesSent = 0; - - if (_socketType == (int)SocketType.Stream) - { - bytesSent = _socket.Send(buffer, offset, count, SocketFlags.None); - } - else if (_socketType == (int)SocketType.Dgram) - { - bytesSent = _socket.SendTo(buffer, offset, count, SocketFlags.None, _socket.RemoteEndPoint); - } - else - { - throw new NotSupportedException(); - } + int bytesSent = _socket.Send(buffer, offset, count, SocketFlags.None); if (bytesSent != count) throw new IOException(); } @@ -358,4 +338,3 @@ public override int Read(SpanByte buffer) } } } - From 853050b62902d359c413340ef0df4347917ebf3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 29 Sep 2026 17:39:34 +0100 Subject: [PATCH 3/4] Add exception docs to SslStream constructor --- nanoFramework.System.Net/Security/SslStream.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nanoFramework.System.Net/Security/SslStream.cs b/nanoFramework.System.Net/Security/SslStream.cs index ad944d14..2f6705ae 100644 --- a/nanoFramework.System.Net/Security/SslStream.cs +++ b/nanoFramework.System.Net/Security/SslStream.cs @@ -47,6 +47,8 @@ public class SslStream : NetworkStream /// Initializes a new instance of the SslStream class using the specified Socket. /// /// A valid socket that currently has a TCP connection. + /// is . + /// is not connected. -or- The property of is not . /// /// The SslStream maintains the lifetime of the socket. When the SslStream object is disposed, /// the underlying TCP socket will be closed. From 9758d279c828a84a3ce917dd4a2811cb35597614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Tue, 29 Sep 2026 17:58:42 +0100 Subject: [PATCH 4/4] Fix code smells in NetworkStream - Disambiguate Read cref references (CS0419). - Make Read overloads adjacent (S4136). - Remove unnecessary boolean literals (S1125). --- nanoFramework.System.Net/Sockets/NetworkStream.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/nanoFramework.System.Net/Sockets/NetworkStream.cs b/nanoFramework.System.Net/Sockets/NetworkStream.cs index ba4c8172..86b72f2f 100644 --- a/nanoFramework.System.Net/Sockets/NetworkStream.cs +++ b/nanoFramework.System.Net/Sockets/NetworkStream.cs @@ -90,7 +90,7 @@ public NetworkStream(Socket socket, bool ownsSocket) /// /// true if data can be read from the stream; otherwise, false. The default value is true. /// - /// If CanRead is true, allows calls to the method. Provide the appropriate FileAccess enumerated value in the constructor to set + /// If CanRead is true, allows calls to the method. Provide the appropriate FileAccess enumerated value in the constructor to set /// the readability and write-ability of the . The CanRead property is set when the is initialized. /// public override bool CanRead { get { return true; } } @@ -152,7 +152,7 @@ public override long Length { get { - if (_disposed == true) throw new ObjectDisposedException(); + if (_disposed) throw new ObjectDisposedException(); if (_socket.m_Handle == -1) throw new IOException(); return _socket.Available; @@ -184,7 +184,7 @@ public virtual bool DataAvailable { get { - if (_disposed == true) throw new ObjectDisposedException(); + if (_disposed) throw new ObjectDisposedException(); if (_socket.m_Handle == -1) throw new IOException(); return (_socket.Available > 0); @@ -255,14 +255,14 @@ public override void Flush() /// The is closed. /// is less than 0 or greater than the length of . -or- is less than 0 or greater than the length of minus the value of the parameter. /// - /// This method reads data into the parameter and returns the number of bytes successfully read. The operation reads as much data as is available, up to the number of bytes specified by the parameter. If the remote host shuts down the connection, and all available data has been received, the Read method completes immediately and return zero bytes. + /// This method reads data into the parameter and returns the number of bytes successfully read. The operation reads as much data as is available, up to the number of bytes specified by the parameter. If the remote host shuts down the connection, and all available data has been received, the Read method completes immediately and return zero bytes. /// /// Check to see if the is readable by calling the property. If you attempt to read from a that is not readable, you will get an . /// /// public override int Read(byte[] buffer, int offset, int count) { - if (_disposed) throw new ObjectDisposedException(); + if (_disposed) throw new ObjectDisposedException(); if (_socket.m_Handle == -1) throw new IOException(); if (buffer == null) throw new ArgumentNullException(); if (offset < 0 || offset > buffer.Length) throw new ArgumentOutOfRangeException(); @@ -320,7 +320,7 @@ public override void SetLength(long value) /// public override void Write(byte[] buffer, int offset, int count) { - if (_disposed) throw new ObjectDisposedException(); + if (_disposed) throw new ObjectDisposedException(); if (_socket.m_Handle == -1) throw new IOException(); if (buffer == null) throw new ArgumentNullException(); if (offset < 0 || offset > buffer.Length) throw new ArgumentOutOfRangeException();