Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stopping HttpListener doesn't free up address in use for some time on macOS (System.Net.HttpListenerException: Address already in use) #24025

Closed
TylerLeonhardt opened this issue Nov 2, 2017 · 14 comments
Labels
Milestone

Comments

@TylerLeonhardt
Copy link

I'm receiving the following error:

Unhandled Exception: System.Net.HttpListenerException: Address already in use
   at System.Net.HttpEndPointManager.GetEPListener(String host, Int32 port, HttpListener listener, Boolean secure)
   at System.Net.HttpEndPointManager.AddPrefixInternal(String p, HttpListener listener)
   at System.Net.HttpEndPointManager.AddListener(HttpListener listener)
   at System.Net.HttpListener.Start()
   at TestHttpListener.TestHttpListener.<SimpleListenerExample>d__3.MoveNext() in /Users/tylerleonhardt/Desktop/CompSci/DotNET/Core/TestHttpListener/TestHttpListener.cs:line 29
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at TestHttpListener.TestHttpListener.<>c__DisplayClass1_0.<<Start>b__0>d.MoveNext() in /Users/tylerleonhardt/Desktop/CompSci/DotNET/Core/TestHttpListener/TestHttpListener.cs:line 13
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadPoolWorkQueue.Dispatch()

I am writing an http server using HttpListener (I need to use HttpListener) and I run into this exception after starting and stoping and starting the server.

Here is my (watered down) library:

using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

namespace TestHttpListener
{
    public class TestHttpListener
    {
        public bool KeepGoing = true;
        public void Start(int Port)
        {
            Thread listenerThread = new Thread(async () => { await SimpleListenerExample(Port); });
            listenerThread.Start();
        }

        public void Stop()
        {
            KeepGoing = false;
        }

        public async Task SimpleListenerExample(int Port)
        {
            // Create a listener.
            HttpListener listener = new HttpListener();
            // Add the prefixes.
            listener.Prefixes.Add("http://localhost:" + Port + "/");

            listener.Start();
            Console.WriteLine("Listening...");

            while (KeepGoing)
            {
                // Note: The GetContext method blocks while waiting for a request.
                HttpListenerContext context = await listener.GetContextAsync();
                HttpListenerRequest request = context.Request;
                // Obtain a response object.
                HttpListenerResponse response = context.Response;

                // Construct a response.
                string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
                byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
                // Get a response stream and write the response to it.
                response.ContentLength64 = buffer.Length;
                System.IO.Stream output = response.OutputStream;
                output.Write(buffer,0,buffer.Length);
                // You must close the output stream.
                output.Close();
            }

            listener.Stop();
        }
    }
}

I've also tried this with listener.Close()

And the file I'm using to verify this behavior is:

using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace TestHttpListener
{
    class Program
    {
        private static readonly HttpClient client = new HttpClient();
        static void Main(string[] args)
        {
            Run().Wait();
        }

        static async Task Run()
        {
            var app = new TestHttpListener();
            app.Start(8081);
            await Task.Delay(TimeSpan.FromSeconds(2));
            var response = await client.GetAsync("http://localhost:8081");
            app.Stop();
            // need second GetAsync becuase HttpListener GetContextAsync does not take a cancellation token
            response = await client.GetAsync("http://localhost:8081");
            await Task.Delay(TimeSpan.FromSeconds(2));
            app.Start(8081);
            Console.ReadLine();
        }
    }
}

I noticed there was this issue with "Address already in use" issues but it seemed unrelated to HttpListener class so I opened this.

This is using the .NET Command Line Tools - 2.0.0
macOS - v10.12.6 (16G29) aka Sierra

Please let me know if there's any additional info I can provide.

@TylerLeonhardt
Copy link
Author

This issue does not repro on Windows with .NET Framework.

@PeterSmithRedmond
Copy link

Without being a macOs network expert, my first thought is that you are running into TIME_WAIT and MSL issues. This is a good StackOverflow discussion of what they mean.

When you close a connection, the OS waits "a while" for any stray packets that might have been delayed on the network to either arrive (and be thrown out) or to time out on the network (which also involves them being thrown out). In all case, we don't want those stray packets to arrive on the destination machine and possibly be considered to be part of an new connection!

The amount of time the OS will wait is 2*msl. According to this ServerFault post, MSL on macOs Lion is 15 seconds. That implies that you have to wait for at least 30 seconds to reuse the server port.

Can you try either bumping up the time, or switching the port to different values?

@TylerLeonhardt
Copy link
Author

I appreciate this. However, I'm wondering how frameworks like Node.js Express.js and ASP.NET Core are handing this. I'm able to stop and start their webservers without any issue. Why does it work for those but not HttpListener on .NET Core?

@stephentoub
Copy link
Member

@TylerLeonhardt
Copy link
Author

I thought it might be related to those but wasn't sure since those didn't mention HttpListener. Is this then a dupe of one of those, you think?

@tmds
Copy link
Member

tmds commented Nov 10, 2017

@tylerl0706 yes, I believe that is the root cause of the issue you are seeing.

@stephentoub
Copy link
Member

Dup of #23803 / dotnet/corefx#24809. Thanks for reporting.

@dhowe
Copy link

dhowe commented May 10, 2018

Apologies if I missed it above, but is there a fix for this issue with HttpListener on OS X?

@bgottfried91
Copy link

bgottfried91 commented May 24, 2018

@dhowe , can't speak for OS X, but the issue appears to be fixed in the latest dotnet core 2.1 Debian release (tested against docker image microsoft/dotnet:2.1-runtime). Try switching to the 2.1 framework and see if it resolves the issue for you on OS X.

@TylerLeonhardt
Copy link
Author

TylerLeonhardt commented May 24, 2018

Haven't messed with this in some time so I'm good. 👍

@dhowe
Copy link

dhowe commented May 24, 2018

Unless something has changed very recently, this is issue is NOT resolved. I have since switched to debian where the issue (as @bgottfried91 mentions above) does not appear to exist. On OSX the only workaround I had found was to use random ports.

@tmds
Copy link
Member

tmds commented May 25, 2018

@dhowe are you using 2.1?

@dhowe
Copy link

dhowe commented May 25, 2018

dotnet 2.0 due to compatibility requirements

@tmds
Copy link
Member

tmds commented May 25, 2018

The fix is not in 2.0. Using 2.1 you shouldn't see this. 2.1 will be released soon-ish.

@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 2.1.0 milestone Jan 31, 2020
@dotnet dotnet locked as resolved and limited conversation to collaborators Dec 19, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

7 participants