TCPListener doesn't create a new socket on Stop (dotnet/corefx#41547)
authorAlexander Nikolaev <55398552+alnikola@users.noreply.github.com>
Thu, 10 Oct 2019 11:59:12 +0000 (13:59 +0200)
committerGitHub <noreply@github.com>
Thu, 10 Oct 2019 11:59:12 +0000 (13:59 +0200)
TCPListener.Stop disposes the socket and resets the reference. New socket is created only on the next Start call or Server property access.
Fixes dotnet/corefx#26170

Commit migrated from https://github.com/dotnet/corefx/commit/c5edb254b85819d4cd4fd6ce35ebb6acc53f450d

src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPListener.cs
src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpListenerTest.cs

index 5a6a5db..95b5622 100644 (file)
@@ -66,6 +66,7 @@ namespace System.Net.Sockets
         {
             get
             {
+                CreateNewSocketIfNeeded();
                 return _serverSocket;
             }
         }
@@ -93,7 +94,7 @@ namespace System.Net.Sockets
         {
             get
             {
-                return _serverSocket.ExclusiveAddressUse;
+                return _serverSocket != null ? _serverSocket.ExclusiveAddressUse : _exclusiveAddressUse;
             }
             set
             {
@@ -102,7 +103,10 @@ namespace System.Net.Sockets
                     throw new InvalidOperationException(SR.net_tcplistener_mustbestopped);
                 }
 
-                _serverSocket.ExclusiveAddressUse = value;
+                if (_serverSocket != null)
+                {
+                    _serverSocket.ExclusiveAddressUse = value;
+                }
                 _exclusiveAddressUse = value;
             }
         }
@@ -139,6 +143,8 @@ namespace System.Net.Sockets
                 return;
             }
 
+            CreateNewSocketIfNeeded();
+
             _serverSocket.Bind(_serverSocketEP);
             try
             {
@@ -160,14 +166,9 @@ namespace System.Net.Sockets
         {
             if (NetEventSource.IsEnabled) NetEventSource.Enter(this);
 
-            _serverSocket.Dispose();
+            _serverSocket?.Dispose();
             _active = false;
-            _serverSocket = new Socket(_serverSocketEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
-
-            if (_exclusiveAddressUse)
-            {
-                _serverSocket.ExclusiveAddressUse = true;
-            }
+            _serverSocket = null;
 
             if (NetEventSource.IsEnabled) NetEventSource.Exit(this);
         }
@@ -333,5 +334,15 @@ namespace System.Net.Sockets
 
             return listener;
         }
+
+        private void CreateNewSocketIfNeeded()
+        {
+            _serverSocket ??= new Socket(_serverSocketEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+
+            if (_exclusiveAddressUse)
+            {
+                _serverSocket.ExclusiveAddressUse = true;
+            }
+        }
     }
 }
index a1036b2..ea15336 100644 (file)
@@ -142,6 +142,74 @@ namespace System.Net.Sockets.Tests
             s.Close();
         }
 
+        [Fact]
+        public async Task Accept_StartAfterStop_AcceptsSuccessfully()
+        {
+            var listener = new TcpListener(IPAddress.Loopback, 0);
+            listener.Start();
+            await VerifyAccept(listener);
+            listener.Stop();
+
+            Assert.NotNull(listener.Server);
+
+            listener.Start();
+            Assert.NotNull(listener.Server);
+            await VerifyAccept(listener);
+            listener.Stop();
+
+            async Task VerifyAccept(TcpListener listener)
+            {
+                using var client = new TcpClient();
+                Task connectTask = client.ConnectAsync(IPAddress.Loopback, ((IPEndPoint)listener.LocalEndpoint).Port);
+                using Socket s = await listener.AcceptSocketAsync();
+                Assert.False(listener.Pending());
+                await connectTask;
+            }
+        }
+
+        [Fact]
+        public void ExclusiveAddressUse_ListenerNotStarted_SetAndReadSuccessfully()
+        {
+            var listener = new TcpListener(IPAddress.Loopback, 0);
+
+            listener.ExclusiveAddressUse = true;
+            Assert.True(listener.ExclusiveAddressUse);
+            listener.ExclusiveAddressUse = false;
+            Assert.False(listener.ExclusiveAddressUse);
+        }
+
+        [Fact]
+        public void ExclusiveAddressUse_SetStartListenerThenRead_ReadSuccessfully()
+        {
+            var listener = new TcpListener(IPAddress.Loopback, 0);
+
+            listener.ExclusiveAddressUse = true;
+
+            listener.Start();
+            Assert.True(listener.ExclusiveAddressUse);
+            listener.Stop();
+
+            Assert.True(listener.ExclusiveAddressUse);
+        }
+
+        [Fact]
+        public void ExclusiveAddressUse_SetStartAndStopListenerThenRead_ReadSuccessfully()
+        {
+            var listener = new TcpListener(IPAddress.Loopback, 0);
+
+            listener.Start();
+            listener.Stop();
+
+            listener.ExclusiveAddressUse = true;
+            Assert.True(listener.ExclusiveAddressUse);
+
+            listener.Start();
+            Assert.True(listener.ExclusiveAddressUse);
+            listener.Stop();
+
+            Assert.True(listener.ExclusiveAddressUse);
+        }
+
         private sealed class DerivedTcpListener : TcpListener
         {
 #pragma warning disable 0618