From c4b0796b22c814b5e03ee5ae2a4b8fb33a33b179 Mon Sep 17 00:00:00 2001 From: josesimoes Date: Wed, 12 Jan 2022 15:16:36 +0000 Subject: [PATCH] Improvements in get/set socket optins - Rework GetSocketOption to correctly return correct type with several SocketOptionName. - GetSocketOption can now return value for SocketOptionName.DontLinger. - Rewrote some parts of the documentation to make it clearer. - Add some unit tests for socket options. --- Tests/SocketTests/SocketOptionsTests.cs | 72 ++++++++++++++++++++++ Tests/SocketTests/SocketTests.nfproj | 1 + nanoFramework.System.Net/Sockets/Socket.cs | 53 +++++++++++----- 3 files changed, 110 insertions(+), 16 deletions(-) create mode 100644 Tests/SocketTests/SocketOptionsTests.cs diff --git a/Tests/SocketTests/SocketOptionsTests.cs b/Tests/SocketTests/SocketOptionsTests.cs new file mode 100644 index 0000000..80b455d --- /dev/null +++ b/Tests/SocketTests/SocketOptionsTests.cs @@ -0,0 +1,72 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.TestFramework; +using System; +using System.Net.Sockets; + +namespace NFUnitTestSocketTests +{ + [TestClass] + public class SocketOptionsTests + { + [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 SocketGetSocketOptions_00() + { + SocketType socketType = SocketType.Stream; + + Socket testSocket = new( + AddressFamily.InterNetwork, + socketType, + ProtocolType.Tcp); + + Assert.Throws(typeof(NotSupportedException), () => + { + testSocket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.AddMembership); + }, "Getting SocketOptionName.AddMembership should have thrown an exception"); + + Assert.Throws(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."); + + testSocket?.Close(); + } + + [TestMethod] + public void SocketLinger() + { + SocketType socketType = SocketType.Stream; + + Socket testSocket = new( + AddressFamily.InterNetwork, + socketType, + ProtocolType.Tcp); + + // TODO + // connect to endpoint + + // get linger option + //Assert.IsType(typeof(bool), testSocket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger), "SocketOptionName.DontLinger should return a bool."); + + // set DontLinger option + // read back linger option + // set LINGER value + + // read back linger option + + testSocket?.Close(); + } + } +} diff --git a/Tests/SocketTests/SocketTests.nfproj b/Tests/SocketTests/SocketTests.nfproj index 37f43b8..2773308 100644 --- a/Tests/SocketTests/SocketTests.nfproj +++ b/Tests/SocketTests/SocketTests.nfproj @@ -27,6 +27,7 @@ $(MSBuildProjectDirectory)\nano.runsettings + diff --git a/nanoFramework.System.Net/Sockets/Socket.cs b/nanoFramework.System.Net/Sockets/Socket.cs index 0a01a91..7f478a8 100644 --- a/nanoFramework.System.Net/Sockets/Socket.cs +++ b/nanoFramework.System.Net/Sockets/Socket.cs @@ -869,6 +869,8 @@ public int ReceiveFrom(byte[] buffer, ref EndPoint remoteEP) /// options determine the behavior of the current . For an option with a Boolean data type, specify a nonzero value to enable the option, and a /// zero value to disable the option. For an option with an integer data type, specify the appropriate value. options are grouped by level of protocol support. /// + /// For option the it's the number of seconds that the socket will linger before closing the connection. + /// To disable socket linger call with and setting it to . /// public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue) { @@ -931,32 +933,33 @@ public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName opti /// One of the values. /// One of the values. /// - /// An object that represents the value of the option. When the optionName parameter is set to the return value is an instance of the LingerOption - /// class. When optionName is set to or , the return value is an instance of the MulticastOption class. When optionName is - /// any other value, the return value is an integer. + /// /// An object that represents the value of the option. + /// + /// When the parameter is set to the return value is an with the value in seconds that the socket will linger after close. + /// To check if linger is enabled for the socket the option should be queried. + /// + /// + /// When optionName is set to , , , or , the return value is . + /// + /// + /// When optionName is any other value, the return value is an integer. + /// /// - /// - /// options determine the behavior of the current . Use this overload to get the , , and options. - /// For the option, use for the optionLevel parameter. For and , use . If you want to set the value of any of - /// the options listed above, use the method. - /// + /// When using an that can't be retrieved. public object GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName) { - if (optionName == SocketOptionName.DontLinger || - optionName == SocketOptionName.AddMembership || - optionName == SocketOptionName.DropMembership) + if (optionName is SocketOptionName.AddMembership + or SocketOptionName.DropMembership) { //special case linger? throw new NotSupportedException(); } // socket options that don't require any request to the native end - if(optionLevel == SocketOptionLevel.Socket) + if (optionLevel == SocketOptionLevel.Socket + && optionName == SocketOptionName.Type) { - if(optionName == SocketOptionName.Type) - { - return _socketType; - } + return _socketType; } // reached here: have to make a request to the lower level to get it @@ -965,6 +968,24 @@ public object GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName op GetSocketOption(optionLevel, optionName, val); + // process specific options + if (optionName is + SocketOptionName.ExclusiveAddressUse or + SocketOptionName.DontLinger) + { + // these are boolean AND negated + return val[0] == 0; + } + else if (optionName is + SocketOptionName.AcceptConnection or + SocketOptionName.Broadcast or + SocketOptionName.KeepAlive) + { + // these are boolean + return val[0] == 1; + } + + // all the others are integers int iVal = (val[0] << 0) | (val[1] << 8) | (val[2] << 16) | (val[3] << 24); return iVal;