Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
352 views
in Technique[技术] by (71.8m points)

c# - How to process multiple connections simultaneously with HttpListener?

In the application that I build, there is a need for webserver that can serve, simultaneously, multiple clients.
For that I use the HttpListener object. with its Async methodsevents BeginGetContext and EndGetContext.
In the delegated method, there is a call for the listener to start listening again, and it works.. mostly.

The code provided is a mix of code that i found here and there, and a delay, to simulate a data processing bottleneck.

The problem is, it starts to manage the next connection only AFTER the last one was served.. no use for me.

public class HtServer {


    public void startServer(){
        HttpListener HL = new HttpListener();
        HL.Prefixes.Add("http://127.0.0.1:800/");
        HL.Start();
        IAsyncResult HLC = HL.BeginGetContext(new AsyncCallback(clientConnection),HL);   
    }

    public void clientConnection(IAsyncResult res){
        HttpListener listener = (HttpListener)res.AsyncState;
        HttpListenerContext context = listener.EndGetContext(res);
        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();
        listener.BeginGetContext(new AsyncCallback(clientConnection), listener);
    }
}

edit

    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";

        // prevent thread from exiting.
        Thread.Sleep(3000);
        // moved these lines here.. to simulate process delay
        ctx.Response.OutputStream.Write(buf, 0, buf.Length);
        ctx.Response.OutputStream.Close();
        Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss.fff") + " completed");
    }

the output is enter image description here

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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:

enter image description here

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...