0

So I've been searching around the web and wasn't able to find out how to configure an .NET HttpListener instance, so it can re-use the local port number (SocketOption: ReuseAddress); otherwise, if I restart HttpListener on the same port as its last incarnation, I often get the following exception:

System.Net.HttpListenerException (0x80004005): 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()

Any ideas about is it possible to customize the behavior for HttpLisener?

EDIT: the following is the test program running under the dotnet core 2.0 (C# 7.1) environment, and to answer a comment below - no, there is no dispose happening yet when the exception occurs - the exception occurs during HttpListener.Start()

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

namespace Test
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var server = new AsyncHttpServer(portNumber: 1234);
            await server.Run();
        }

        class AsyncHttpServer
        {
            readonly HttpListener listener;
            const string RESPONSE_TEMPLATE =
                "{{\"RawUrl\":\"{0}\", \"i\":{1}}}";

            public AsyncHttpServer(int portNumber)
            {
                listener = new HttpListener();
                listener.Prefixes.Add(string.Format("http://+:{0}/", portNumber));
            }

            int i = 0;

            async void HandleRequest(HttpListenerContext context)
            {
                Console.WriteLine($"HandleRequest: {context.Request.Url}, {context.Request.RawUrl}, {context.Request.HttpMethod}");

                await Task.Delay(TimeSpan.FromSeconds(1));

                var response = context.Request.RawUrl == "/" ? string.Format(RESPONSE_TEMPLATE, context.Request.Url.ToString(), ++i) : "";

                using (var sw = new StreamWriter(context.Response.OutputStream))
                {
                    await sw.WriteAsync(response);
                    await sw.FlushAsync();
                }
            }

            public async Task Run()
            {
                try
                {
                    listener.Start();

                    while (true)
                    {
                        var ctx = await listener.GetContextAsync();
                        HandleRequest(ctx);
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }
        }
    }
}
Dejavu
  • 1,299
  • 14
  • 24
  • Show us how you're trying to use it...there are good chances you're not disposing the object correctly – Adriano Repetti Oct 17 '17 at 09:35
  • This might be related and help? https://stackoverflow.com/questions/223063/how-can-i-create-an-httplistener-class-on-a-random-port-in-c – bartbje Oct 17 '17 at 09:35
  • Well, I admit I didn't explicitly dispose the old running instance - I simply killed the old instance, leaving the old TCP port lingering underlying for as long as TCP is configured - but, this is a typical scenario, if my simple http server crashed, I would like to restart as soon as possible without waiting for the old lingering TCP port to clean up for about 2 min or something. – Dejavu Oct 17 '17 at 09:49
  • Indeed, this seems to be a duplicate of the other SO post. Seems HttpListener is not a versatile solution to use then. Just for the record - the solution proposed by the other post suggested using try/catch/finally to make sure the HttpListener is properly disposed when the program exits, but it doesn't guard the condition of the process being killed by a third party and restarting ... – Dejavu Oct 17 '17 at 10:00
  • @Dejavu when a process is forcefully killed by a third party (task manager? `taskkill`?) then you may well wait when starting up. It's not a _normal_ condition, it's something that should hopefully (almost) never happen. – Adriano Repetti Oct 18 '17 at 08:37
  • @AdrianoRepetti the current solution is a combination of properly disposing and wait and retry at start up should there be the "address already in use" exception. – Dejavu Oct 19 '17 at 03:34

0 Answers0