1

I am struggling with HttpListener and awaiting its context. Sometimes I am really confused. I have the application to run this server. However, I don't want to await the requests. I want to run server in a background and don't care about it. But I am not sure if it is a good idea, because Microsoft says that if you have something like an I/O operation it should be awaited. But I don't want to await it. What if I want to write something in my console? I cannot, because I await HttpListener's context. The solution looks pretty simple:

    // _server is an instance of HttpListener
    public void StartServer()
    {
        _server.Prefixes.Add("http://127.0.0.1:8080/test/");
        _server.Start();

        Task.Run(async() => 
        {
            while (_server.IsListening)
            {
                HttpListenerContext context = await _server.GetContextAsync();
                await HandleContextAsync(context);
            }
        }).ConfigureAwait(false);
    }

However, in this case I am not sure that there won't be any deadlocks or something, because handling the context requires a lot of operations (for example, it opens a DB connection somewhere).

The last thing I have decided to do is to separate my main application and the server side, but I still want to run server in the background to be able to stop it using some input from the console. What should I do?

I've also tried something like this:

    Task serverListener = server.StartServerAsync();

    Console.ReadLine();
    await server.StopServerAsync();

    //
    // The server methods:
    //
    public async Task StartServerAsync()
    {
        _server.Prefixes.Add("http://127.0.0.1:8080/test/");
        _server.Start();

        while (_server.IsListening)
        {
            HttpListenerContext context = await _server.GetContextAsync();
            await HandleContextAsync(context);
        }
    }

    public Task StopServerAsync()
    {
        _server.Stop();
        _server.Close();

        return Task.CompletedTask;
    }

But in this case I am not sure what happens with Task, because it is not cancelled or something. The server has been stopped, but the Task is still awaitable. Is it better than the first case with Task.Run() or not?

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
Dasic
  • 29
  • 5

2 Answers2

1

However, in this case I am not sure that there are won't be any deadlocks or something

No, this will not introduce any deadlocks (except the ones you already have if any). Task.Run schedules execution of the code on thread pool which does not "flow" the SynchronizationContext which is the infamous reason for several types of async-related deadlocks.

I've also tried something like this: But in this case I am not sure what happens with Task, because it is not cancelled or something.

The OS will handle cleaning all the requested resources by the app on the closure. The StartServerAsync Task itself will be set with an exception result due to the listener being stopped, but since it is not awaited the exception will not be observed (though I would argue that is ok in this case) and that's it.

Read more:

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • Thank you! So, I guess, it is not a bad idea to run the server using Task.Run(), isn't it? Will everything be fine? – Dasic Apr 08 '23 at 13:02
  • 1
    @Dasic yes, it is quite good. Also you can run the `Console.Read...` from another thread/Task. Also personally I would consider running an ASP.NET Core app instead of handling the listener by myself. – Guru Stron Apr 08 '23 at 13:30
1

First, I recommend using an already-built solution like Kestrel. If you build your own solution, you'll need to make several decisions and write a lot of code yourself.

Microsoft says that if you have something like an I/O operation it should be awaited. But I don't want to await it.

That's the general rule, yes. There are two reasons:

  1. By awaiting, your app knows when the operation is completed. If your app doesn't know when it's completed, then it can't know when it's safe to exit.
  2. By awaiting, your app can detect and respond to all exceptions.

I am not sure that there won't be any deadlocks or something

Deadlocks aren't an issue here. Assuming a Console application, then all your continuations will run on the thread pool by default. Note: ConfigureAwait(false) doesn't do anything in the code you posted; ConfigureAwait configures the await, but there's no await. And in the case of a Console app, ConfigureAwait(false) is the same as not having it at all anyway.

I still want to run server in the background to be able to stop it using some input from the console.

You can kick it off with a Task.Run and save the result in a Task variable, and then await that variable at the end of your application. Or if you want a "top-level loop" task, then have the code inside the Task.Run use a top-level try/catch and report errors that way.

You also need to consider how clean you want your shutdowns. Most web servers when requested to exit do not exit immediately; they allow any outstanding requests to complete (and close their listening socket so no new ones come in). You'd want to await the task if you want to ensure a clean shutdown.

Also, your current code has a "top-level loop" task for the listener loop, but then it processes each connection before listening for the next one. This is highly unusual behavior for a web or TCP/IP server; it's normal for each connection to kick off its own "top-level loop" to handle each request so that other incoming requests are not blocked. This naturally complicates the shutdown logic you'll need to write if you want a clean shutdown (you now have to manage a listener and a collection of connections).

Or you can use Kestrel, which does all this for you. :)

If you do want to do it yourself, you might find a new library I'm hacking on to be interesting. It's specifically designed to help handle these "top-level loops" in a more structured way. It's in a very early pre-release alpha state at the moment, though.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Thank you so much, this answer will help. I've also figured out you to have written the book about asynchronous and so on. I have worked with Unity, DI, WPF and know a lot of patterns, but I have misconceptions about async programming. I would like to have a more complete understanding of it. Is your book the right way to begin with if 'async-await' feels like magic? – Dasic Apr 09 '23 at 06:12
  • 1
    @Dasic: I usually recommend people read my [async intro](https://blog.stephencleary.com/2012/02/async-and-await.html) followed by [async best practices](https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming?WT.mc_id=DT-MVP-5000058). Then if you are ready for the next step, my book is a good resource. IMO :) – Stephen Cleary Apr 10 '23 at 12:39