Handling Multiple Requests with C# Httplistener

Handling multiple requests with C# HttpListener

If you need a more simple alternative to BeginGetContext, you can merely queue jobs in ThreadPool, instead of executing them on the main thread. Like such:

private void CreateLListener() {
//....
while(true) {
ThreadPool.QueueUserWorkItem(Process, listener.GetContext());
}
}
void Process(object o) {
var context = o as HttpListenerContext;
// process request and make response
}

How to process multiple connections simultaneously with HttpListener?

Well. That's because you start to fetch the next context after you have processed the first. Don't do that. Get the next context directly:

public void clientConnection(IAsyncResult res){
HttpListener listener = (HttpListener)res.AsyncState;
HttpListenerContext context = listener.EndGetContext(res);

//tell listener to get the next context directly.
listener.BeginGetContext(clientConnection, listener);

HttpListenerRequest request = context.Request;
// Obtain a response object.
HttpListenerResponse response = context.Response;
// Construct a response.
// add a delay to simulate data process
String before_wait = String.Format("{0}", DateTime.Now);
Thread.Sleep(4000);
String after_wait = String.Format("{0}", DateTime.Now);
string responseString = "<HTML><BODY> BW: " + before_wait + "<br />AW:" + after_wait + "</BODY></HTML>";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
// Get a response stream and write the response to it.
response.ContentLength64 = buffer.Length;
System.IO.Stream output = response.OutputStream;
// You must close the output stream.
output.Write(buffer, 0, buffer.Length);
output.Close();
}

Here is my sample code that proves that it work (updated per request of the OP):

class Program
{
private static HttpListener _listener;

static void Main(string[] args)
{
_listener = new HttpListener();
_listener.Prefixes.Add("http://localhost/asynctest/");
_listener.Start();
_listener.BeginGetContext(OnContext, null);

Console.ReadLine();
}

private static void OnContext(IAsyncResult ar)
{
var ctx = _listener.EndGetContext(ar);
_listener.BeginGetContext(OnContext, null);

Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss.fff") + " Handling request");

var buf = Encoding.ASCII.GetBytes("Hello world");
ctx.Response.ContentType = "text/plain";

// simulate work
Thread.Sleep(10000);

ctx.Response.OutputStream.Write(buf, 0, buf.Length);
ctx.Response.OutputStream.Close();

Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss.fff") + " completed");
}
}

Generates:

Sample Image

Both requests starts to get processed directly.

Why the above code works

HTTP have something called pipelining. It means that all requests that are received over the same connection must get their responses in the same order. However, the built in HttpListener doesn't seem to support pipelining, instead it's completes the response for the first request before taking care of the second. It's therefore important that you make sure that every request is sent over a new connection.

The easiest way to do that is to use different browsers when trying out the code. I did that, and as you see both my requests are handled at the same time.

HttpListener handling multiple requests

It stops listening after processing one request because you just stop listening. You need to implement something like a waiting loop.

Example which should help you can be found on codeproject - example.

Pay attention to this part of the code and how it is used in the example:

private void startlistener(object s)
{
while (true)
{
////blocks until a client has connected to the server
ProcessRequest();
}
}

Handling post requests in HttpListener

You should add ctx.Response.ContentLength64=....
(you may also need ctx.Response.Close())

Multi-threading with .Net HttpListener

I have consulted my code in EDIT part of my question and I've decided to accept it with some modifications:

public void Stop() 
{
lock (locker)
{
isStopping = true;
}
resetEvent.WaitOne(); //initially set to true
listener.Stop();
}

private void ListenerCallback(IAsyncResult ar)
{
lock (locker) //locking on this is a bad idea, but I forget about it before
{
if (isStopping)
return;

resetEvent.Reset();
numberOfRequests++;
}

try
{
var listener = ar.AsyncState as HttpListener;

var context = listener.EndGetContext(ar);

//do some stuff
}
finally //to make sure that bellow code will be executed
{
lock (locker)
{
if (--numberOfRequests == 0)
resetEvent.Set();
}
}
}


Related Topics



Leave a reply



Submit