I have a program that controls a custom machine. I wanted to add some simple HTTP comms so I could control it from the network or other programs.
My server class is:
public class Server
{
private volatile bool stop = true;
private Action<string> methodOne;
public Server(Action<string> methodOne)
{
this.methodOne= methodOne;
}
public async Task StartAsync()
{
var prefix = "http://localhost:5005/";
HttpListener listener = new HttpListener();
listener.Prefixes.Add(prefix);
try
{
listener.Start();
stop = false;
}
catch (HttpListenerException hlex)
{
return;
}
while (listener.IsListening)
{
var context = await listener.GetContextAsync();
try
{
await ProcessRequestAsync(context);
}
catch (Exception ex)
{
Console.WriteLine("# EXCEPTION # " + ex.StackTrace);
}
if (stop == true) listener.Stop();
}
listener.Close();
}
public void Stop()
{
stop = true;
}
private async Task ProcessRequestAsync(HttpListenerContext context)
{
// Get the data from the HTTP stream
var body = await new StreamReader(context.Request.InputStream).ReadToEndAsync();
HttpListenerRequest request = context.Request;
if (request.RawUrl.StartsWith("/methodOne"))
{
//Get parameters
var options = context.Request.QueryString;
var keys = options.AllKeys;
//Run function
methodOne("some method parameter");
//Respond
byte[] b = Encoding.UTF8.GetBytes("ack");
context.Response.StatusCode = 200;
context.Response.KeepAlive = false;
context.Response.ContentLength64 = b.Length;
var output = context.Response.OutputStream;
await output.WriteAsync(b, 0, b.Length);
context.Response.Close();
}
}
}
I initialise the Server
by passing it the delegate for the function in the main view model that will be called, then start itwith server.StartAsync();
VS2017 gives me some advice that I didn't await server.StartAsync();
so thread will continue, but that is the functionality I want.
Any problems with making a server like this, using async and having it run on the main thread? All the server does is call methods in the view model, and there will usually be just one connection. The advantage for me is that being all on the same thread I don't have to worry about dispatching etc.
1 Answer 1
Visual Studio will warn if you don't use the Task object returned from the function, either immediately with an await
statement or by storing it in a variable. The reason is that this is the only connection you have with the Task that you have started, and without it there is no way to find out if it has completed or to get the return value.
Stylistically, methodOne
is not a good name for that variable, something like handler
or callback
would make more sense. It also might make sense for the signature to be a little more complex, like accepting a dictionary of parameters and returning an object to serialize to the response.
The other thing to realize is that the called function doesn't know what you do or don't do with the Task, so its behavior can't depend on that. Therefore "using async and having it run on the main thread" does not accurately describe what happens in your situation. The task is run asynchronously on a background task either way, but in your case the main thread does not block on its completion. If you step in with a debugger, you'll see that the code up to the first await (in the while loop) is executed on the calling thread, and at that point control returns to the caller, who could then maintain a reference to the Task if it wants.
As for whether async and Task are right for your job: it sounds like your needs are pretty modest and that there isn't a real argument to be made for or against. If there aren't many requests coming in, it doesn't really matter how you handle them. If there could be a lot coming in and you wanted to limit the impact on the rest of the system, maybe it would make sense to do the work synchronously on a single thread (that you create for that specific purpose).
Explore related questions
See similar questions with these tags.
Task
for this. \$\endgroup\$