in a nutshell,
HttpListener is a class that will create a HTTP server that will listen and response to requests. I originally thought about writing a TCP server where we would manually handle the HTTP messages ourselves, but after some thinking, this class is a lot easier to work with.
If you are interested in writing a web app with a C# backend,
HttpListener is not the thing to use. I'm sure you've heard of the very well tried and true ASP.NET platform; please... use that instead. There are other alternatives too, like Nancy or FubuMVC if you don't like Microsoft's solution. I'm sure there is some CMS out there written in C# you should be able to modify or write plugins for.
So for our simple little web site, we want it to do just a few things:
- Deliver a web page to a web browser
- Show a page view count (dynamic content)
- Let the user shutdown the Http Server in the browser by clicking a button on the page
Like the last example, we're going to use
Once the code is running and the terminal reports the server is good, go ahead and visit http://localhost:8000. Refresh the page a few times, view the console output, and then press the Shutdown button on the web page and try refreshing again. Let's clarify what's going on exactly.
When we call
Start() on our
HttpListener instance, it will open up a TCP socket and start listening for any HTTP messages that are being sent to any of our
Prefixes we have added. In this case, we are listening in on port
localhost, at the root path. Say if you only wanted the server to respond to messages at the path
asdf, you would have to change the value of
http://localhost:8000/asdf/. Please note that all of you prefixes need to end with a trailing slash, or an
ArgumentException will be thrown.
When we call
listener.GetContextAsync() the function
HandleIncomingConnections() will wait until someone has sent a HTTP request to our server (be it a web browser, or with something like a web page downloader like we made in the last section). Once we have a connection, from the
HttpListenerContext we can peel out the necessary request and response objects.
HttpListenerRequesttells us about the user (the client) and what they want from us (the server)
HttpListenerResponselets us talk to the user
Lines 48 through 54 just print some meta-data about the request. What we care most about is the
HttpMethod they sent us and what path they are requesting. If they sent us a
POST message to the path
/shutdown, this means they have clicked the Shutdown button and want us to terminate the server. But if not, just give them the standard page with a view count. We also want to make sure if they request the file
favicon.ico (which almost all browser out there do), we don't want to add to the page view count.
For the response, we need to write a few things to the header first:
- Specify a MIME type. We're just sending plain HTML here, so we use
- Tell the response what the encoding of the message is. Since we want to be good little developers that support internationalization, we use UTF-8
- Say how many bytes long our message is
Once that is done, we grab
OutputStream from the response object and write to it (asynchronously) like any other stream. Lastly, we close the
HttpListenerResponse to tell it we're done with this message. This will also close the stream for us too.
This may go on for a bit (i.e. refreshes), but once the Shutdown button has been pressed,
HandleIncomingConnections() will deliver a final message to that client and exit. We also need to clean up all of our we were using resources, like calling
listener.Close() to free up any of the ports.
Now, near the top of the page I told you not to use this for writing web apps, then why would should you use this class at all? Everything has its good use case. For instance, if I wanted to give a user a graphical way of editing a configuration file, this would be great! HTML programming is much easier than desktop GUI programming, it's better than telling your user "go find this text file and edit it," (instead you tell them to "point their browser to this URL,"), it comes built into .NET (no extra dependencies), and it's very simple to use (but keep in mind it doesn't have many complex features).
There's one thing that's not so great about the design of this HTTP server. Look inside the body of
while(runServer). If you're having some trouble figuring it out, put a call to
Thread.Sleep(TimeSpan.FromSeconds(15)) at the end of the loop after the response is closed. Then try viewing the page a few times.
With how the code is currently structured inside of the loop, only one client can be handled at a time. So if a second client tries to connect while the server is still talking to another user, it will have to wait until the server is done with the first client. A common pattern in server design is to create a new thread or fork the server process immediately after a user has connected to it. This way it can handle new incoming connections while serving that user.