HttpListener
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 async
code:
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 8000
of 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 url
to 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.
HttpListenerRequest
tells us about the user (the client) and what they want from us (the server)HttpListenerResponse
lets 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
text/html
- 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.